diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7eb69a9380..a432aa6fbb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,4 +12,4 @@ /Resources/Maps/_WL/** @0leshe # Sprites -/Resources/Textures/** @Morb0 @DIMMoon1 @SonicHDC +/Resources/Textures/** @Morb0 @DIMMoon1 @MureixloI diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index e40e102e44..0fbbf68dd6 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -33,13 +33,21 @@ jobs: - 'Content.Shared/**' - 'Content.Server/**' - 'Content.Client/**' - - 'Resources/**' + - 'Resources/Prototypes/**' - 'RobustToolbox/**' prototypes: - '.github/workflows/update-wiki.yml' - 'Resources/Prototypes/**' + textures: + - '.github/workflows/update-wiki.yml' + - 'Resources/Textures/**' + + locale: + - '.github/workflows/update-wiki.yml' + - 'Resources/Locale/**' + - name: Setup Submodule run: | git submodule update --init --recursive @@ -67,6 +75,30 @@ jobs: run: dotnet ./bin/Content.Server/Content.Server.dll --cvar autogen.destination_file=prototypes.json continue-on-error: true + - name: Upload loc to wiki + if: ${{ github.event_name == 'workflow_dispatch' || steps.changes.outputs.locale == 'true' }} + continue-on-error: true + uses: jtmullen/mediawiki-edit-action@v0.1.1 + with: + wiki_text_file: ./bin/Content.Server/data/loc.json + edit_summary: Update loc.json via GitHub Actions + page_name: "${{ secrets.WIKI_PAGE_ROOT }}/loc.json" + api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php + username: ${{ secrets.WIKI_BOT_USER }} + password: ${{ secrets.WIKI_BOT_PASS }} + + - name: Update meta license + if: ${{ github.event_name == 'workflow_dispatch' || steps.changes.outputs.textures == 'true' }} + continue-on-error: true + uses: jtmullen/mediawiki-edit-action@v0.1.1 + with: + wiki_text_file: ./bin/Content.Server/data/meta_license.json + edit_summary: Update meta_license.json via GitHub Actions + page_name: "${{ secrets.WIKI_PAGE_ROOT }}/meta_license.json" + api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php + username: ${{ secrets.WIKI_BOT_USER }} + password: ${{ secrets.WIKI_BOT_PASS }} + # Проходит по всем JSON-файлам в директории BASE и загружает каждый файл как страницу в MediaWiki. # Имя страницы формируется из относительного пути к файлу. - name: Upload JSON files to wiki diff --git a/Content.Benchmarks/ComponentQueryBenchmark.cs b/Content.Benchmarks/ComponentQueryBenchmark.cs index bfe367790a..c9aebf7ba3 100644 --- a/Content.Benchmarks/ComponentQueryBenchmark.cs +++ b/Content.Benchmarks/ComponentQueryBenchmark.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -44,7 +45,7 @@ public class ComponentQueryBenchmark ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(typeof(QueryBenchSystem).Assembly); - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); _entMan = _pair.Server.ResolveDependency(); _itemQuery = _entMan.GetEntityQuery(); diff --git a/Content.Benchmarks/DeltaPressureBenchmark.cs b/Content.Benchmarks/DeltaPressureBenchmark.cs index 8d4929c47f..dac79e0376 100644 --- a/Content.Benchmarks/DeltaPressureBenchmark.cs +++ b/Content.Benchmarks/DeltaPressureBenchmark.cs @@ -1,3 +1,4 @@ +using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; @@ -68,7 +69,7 @@ public class DeltaPressureBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; var mapdata = await _pair.CreateTestMap(); diff --git a/Content.Benchmarks/DestructibleBenchmark.cs b/Content.Benchmarks/DestructibleBenchmark.cs index aa759c35fc..58c834c0ae 100644 --- a/Content.Benchmarks/DestructibleBenchmark.cs +++ b/Content.Benchmarks/DestructibleBenchmark.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; @@ -69,7 +70,7 @@ public class DestructibleBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; _entMan = server.ResolveDependency(); diff --git a/Content.Benchmarks/DeviceNetworkingBenchmark.cs b/Content.Benchmarks/DeviceNetworkingBenchmark.cs index bb2a22312e..fcde4feb64 100644 --- a/Content.Benchmarks/DeviceNetworkingBenchmark.cs +++ b/Content.Benchmarks/DeviceNetworkingBenchmark.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; @@ -60,7 +61,7 @@ public class DeviceNetworkingBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(typeof(DeviceNetworkingBenchmark).Assembly); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; await server.WaitPost(() => diff --git a/Content.Benchmarks/GasReactionBenchmark.cs b/Content.Benchmarks/GasReactionBenchmark.cs index 9ed30373d1..f298869366 100644 --- a/Content.Benchmarks/GasReactionBenchmark.cs +++ b/Content.Benchmarks/GasReactionBenchmark.cs @@ -1,3 +1,4 @@ +using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; @@ -51,7 +52,7 @@ public class GasReactionBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; // Create test map and grid diff --git a/Content.Benchmarks/HeatCapacityBenchmark.cs b/Content.Benchmarks/HeatCapacityBenchmark.cs index cef5bc10c7..18366306d7 100644 --- a/Content.Benchmarks/HeatCapacityBenchmark.cs +++ b/Content.Benchmarks/HeatCapacityBenchmark.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.IO; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; using Content.IntegrationTests.Pair; @@ -27,7 +28,7 @@ public class HeatCapacityBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); await _pair.Connect(); _cEntMan = _pair.Client.ResolveDependency(); _sEntMan = _pair.Server.ResolveDependency(); diff --git a/Content.Benchmarks/MapLoadBenchmark.cs b/Content.Benchmarks/MapLoadBenchmark.cs index 261d408aac..9ea95c84b6 100644 --- a/Content.Benchmarks/MapLoadBenchmark.cs +++ b/Content.Benchmarks/MapLoadBenchmark.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -29,7 +30,7 @@ public class MapLoadBenchmark ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); var server = _pair.Server; Paths = server.ResolveDependency() diff --git a/Content.Benchmarks/PvsBenchmark.cs b/Content.Benchmarks/PvsBenchmark.cs index 51a013539e..af1ec8d9ef 100644 --- a/Content.Benchmarks/PvsBenchmark.cs +++ b/Content.Benchmarks/PvsBenchmark.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.IO; using System.Linq; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -50,7 +51,7 @@ public class PvsBenchmark #endif PoolManager.Startup(); - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); _entMan = _pair.Server.ResolveDependency(); _pair.Server.CfgMan.SetCVar(CVars.NetPVS, true); _pair.Server.CfgMan.SetCVar(CVars.ThreadParallelCount, 0); diff --git a/Content.Benchmarks/RaiseEventBenchmark.cs b/Content.Benchmarks/RaiseEventBenchmark.cs index e3d377ccb3..99e032d0b5 100644 --- a/Content.Benchmarks/RaiseEventBenchmark.cs +++ b/Content.Benchmarks/RaiseEventBenchmark.cs @@ -1,4 +1,5 @@ #nullable enable +using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -21,7 +22,7 @@ public class RaiseEventBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(typeof(BenchSystem).Assembly); - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); var entMan = _pair.Server.EntMan; var fact = _pair.Server.ResolveDependency(); var bus = (EntityEventBus)entMan.EventBus; diff --git a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs index 9b878eac40..b6d5427480 100644 --- a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs +++ b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.IO; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; using Content.IntegrationTests.Pair; @@ -36,7 +37,7 @@ public class SpawnEquipDeleteBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; var mapData = await _pair.CreateTestMap(); diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs index 81b1c087d8..31f65dc78d 100644 --- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs +++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs @@ -65,7 +65,7 @@ namespace Content.Client.Access.UI _window?.UpdateState(castState); } - public void SubmitData(string newFullName, string newJobTitle, List> newAccessList, ProtoId newJobPrototype) + public void SubmitData(string newFullName, string newJobTitle, List> newAccessList, ProtoId? newJobPrototype) { SendMessage(new WriteToTargetIdMessage( newFullName, diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs index bb44ae2615..2169277f0d 100644 --- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs +++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs @@ -216,7 +216,7 @@ namespace Content.Client.Access.UI JobTitleLineEdit.Text, // Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair _accessButtons.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(), - jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : string.Empty); + jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : null); } } } diff --git a/Content.Client/Atmos/Components/MaxPressureVisualsComponent.cs b/Content.Client/Atmos/Components/MaxPressureVisualsComponent.cs new file mode 100644 index 0000000000..e71f212786 --- /dev/null +++ b/Content.Client/Atmos/Components/MaxPressureVisualsComponent.cs @@ -0,0 +1,36 @@ +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; + +namespace Content.Client.Atmos.Components; + +/// +/// This listens to appearance changes from +/// and applies sprite changes to a gas holder currently experiencing loss. +/// +[RegisterComponent] +public sealed partial class MaxPressureVisualsComponent : Component +{ + /// + /// What RsiState we use for our integrity visuals. + /// + [DataField] + public string? IntegrityState = "integrity"; + + /// + /// What RsiState we use for the mask that goes over integrity visuals. + /// + [DataField] + public string? IntegrityMask = "mask"; + + /// + /// How many steps there are + /// + [DataField("steps")] + public int IntegritySteps = 5; +} + +public enum MaxPressureVisualLayers : byte +{ + Base, + BaseUnshaded, +} diff --git a/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs b/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs index f0e4b13356..e192a66fcc 100644 --- a/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs +++ b/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs @@ -1,6 +1,7 @@ using Content.Client.Stylesheets; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.Monitor; using Content.Shared.FixedPoint; using Content.Shared.Temperature; @@ -22,6 +23,7 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer private readonly IEntityManager _entManager; private readonly IResourceCache _cache; + private readonly SharedAtmosphereSystem _atmosphere; private Dictionary _alarmStrings = new Dictionary() { @@ -37,6 +39,7 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer _entManager = IoCManager.Resolve(); _cache = IoCManager.Resolve(); + _atmosphere = _entManager.System(); NetEntity = uid; Coordinates = coordinates; @@ -149,7 +152,7 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer foreach ((var gas, (var mol, var percent, var alert)) in keyValuePairs) { FixedPoint2 gasPercent = percent * 100f; - var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation")); + var gasAbbreviation = Loc.GetString(_atmosphere.GetGas(gas).Abbreviation); var gasLabel = new Label() { diff --git a/Content.Client/Atmos/Consoles/AtmosMonitoringEntryContainer.xaml.cs b/Content.Client/Atmos/Consoles/AtmosMonitoringEntryContainer.xaml.cs index 0ce0c9c880..cf9a89d1a5 100644 --- a/Content.Client/Atmos/Consoles/AtmosMonitoringEntryContainer.xaml.cs +++ b/Content.Client/Atmos/Consoles/AtmosMonitoringEntryContainer.xaml.cs @@ -1,6 +1,7 @@ using Content.Client.Stylesheets; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.FixedPoint; using Content.Shared.Temperature; using Robust.Client.AutoGenerated; @@ -19,12 +20,14 @@ public sealed partial class AtmosMonitoringEntryContainer : BoxContainer private readonly IEntityManager _entManager; private readonly IResourceCache _cache; + private readonly SharedAtmosphereSystem _atmosphere; public AtmosMonitoringEntryContainer(AtmosMonitoringConsoleEntry data) { RobustXamlLoader.Load(this); _entManager = IoCManager.Resolve(); _cache = IoCManager.Resolve(); + _atmosphere = _entManager.System(); Data = data; @@ -132,7 +135,7 @@ public sealed partial class AtmosMonitoringEntryContainer : BoxContainer var gasPercent = (FixedPoint2)0f; gasPercent = percent * 100f; - var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation")); + var gasAbbreviation = Loc.GetString(_atmosphere.GetGas(gas).Abbreviation); var gasLabel = new Label() { diff --git a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index d950108922..24f3ffaa26 100644 --- a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -37,6 +37,20 @@ public sealed partial class AtmosphereSystem return NumericsHelpers.HorizontalAdd(tmp) > epsilon; } + public override float GetMass(GasMixture mix) + { + return GetMass(mix.Moles); + } + + public override float GetMass(float[] moles) + { + var tmp = new float[moles.Length]; + NumericsHelpers.Multiply(moles, GasMolarMasses, tmp); + + // Conversion of grams to kilograms. + return NumericsHelpers.HorizontalAdd(tmp) * Atmospherics.gToKg; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override float GetHeatCapacityCalculation(float[] moles, bool space) { @@ -51,7 +65,7 @@ public sealed partial class AtmosphereSystem // though this isnt the hottest code path so it should be fine // the gc can eat a little as a treat var tmp = new float[moles.Length]; - NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp); + NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); // Adjust heat capacity by speedup, because this is primarily what // determines how quickly gases heat up/cool. return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); diff --git a/Content.Client/Atmos/EntitySystems/GasTankSystem.cs b/Content.Client/Atmos/EntitySystems/GasTankSystem.cs index 696e7939f6..bae70ea2bd 100644 --- a/Content.Client/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Client/Atmos/EntitySystems/GasTankSystem.cs @@ -11,6 +11,12 @@ public sealed class GasTankSystem : SharedGasTankSystem SubscribeLocalEvent(OnGasTankState); } + protected override void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + // Atmos not predicted :( + throw new NotImplementedException(); + } + private void OnGasTankState(Entity ent, ref AfterAutoHandleStateEvent args) { if (UI.TryGetOpenUi(ent.Owner, SharedGasTankUiKey.Key, out var bui)) diff --git a/Content.Client/Atmos/EntitySystems/GasTileHeatBlurOverlaySystem.cs b/Content.Client/Atmos/EntitySystems/GasTileHeatBlurOverlaySystem.cs new file mode 100644 index 0000000000..a80208620b --- /dev/null +++ b/Content.Client/Atmos/EntitySystems/GasTileHeatBlurOverlaySystem.cs @@ -0,0 +1,30 @@ +using Content.Client.Atmos.Overlays; +using JetBrains.Annotations; +using Robust.Client.Graphics; + +namespace Content.Client.Atmos.EntitySystems; + +/// +/// System responsible for rendering heat distortion using . +/// +[UsedImplicitly] +public sealed class GasTileHeatBlurOverlaySystem : EntitySystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + private GasTileHeatBlurOverlay _gasTileHeatBlurOverlay = default!; + + public override void Initialize() + { + base.Initialize(); + + _gasTileHeatBlurOverlay = new GasTileHeatBlurOverlay(); + _overlayMan.AddOverlay(_gasTileHeatBlurOverlay); + } + + public override void Shutdown() + { + base.Shutdown(); + _overlayMan.RemoveOverlay(); + } +} diff --git a/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs b/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs new file mode 100644 index 0000000000..72eff2b409 --- /dev/null +++ b/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs @@ -0,0 +1,77 @@ +using Content.Client.Atmos.Components; +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; +using Content.Shared.Rounding; +using Robust.Client.GameObjects; + +namespace Content.Client.Atmos.EntitySystems; + +/// +/// This system handles sprite changes for a +/// with a when its changes. +/// +public sealed class MaxPressureVisualsSystem : EntitySystem +{ + [Dependency] private readonly SpriteSystem _sprite = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnMaxPressureInit); + SubscribeLocalEvent(OnAppearanceChange); + } + + private void OnMaxPressureInit(Entity entity, ref ComponentInit args) + { + if (!TryComp(entity, out var sprite)) + return; + + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(entity.Comp.IntegritySteps); + + if (_sprite.LayerMapTryGet((entity, sprite), MaxPressureVisualLayers.Base, out _, false)) + { + _sprite.LayerSetRsiState((entity, sprite), MaxPressureVisualLayers.Base, $"{entity.Comp.IntegrityMask}"); + _sprite.LayerSetVisible((entity, sprite), MaxPressureVisualLayers.Base, false); + } + + if (_sprite.LayerMapTryGet((entity, sprite), MaxPressureVisualLayers.BaseUnshaded, out _, false)) + { + _sprite.LayerSetRsiState((entity, sprite), MaxPressureVisualLayers.BaseUnshaded, $"{entity.Comp.IntegrityState}-unshaded-0"); + _sprite.LayerSetVisible((entity, sprite), MaxPressureVisualLayers.BaseUnshaded, false); + } + } + + private void OnAppearanceChange(Entity entity, ref AppearanceChangeEvent args) + { + if (args.Sprite is not { } sprite) + return; + + if (!args.AppearanceData.TryGetValue(GasIntegrity.Integrity, out var obj) || obj is not float integrity) + return; + + if (!args.AppearanceData.TryGetValue(GasIntegrity.MaxIntegrity, out obj) || obj is not float maxIntegrity) + return; + + // We don't want visuals at max integrity, so we return if we're at max. + if (integrity >= maxIntegrity) + { + _sprite.LayerSetVisible((entity, sprite), MaxPressureVisualLayers.Base, false); + _sprite.LayerSetVisible((entity, sprite), MaxPressureVisualLayers.BaseUnshaded, false); + return; + } + + _sprite.LayerSetVisible((entity, sprite), MaxPressureVisualLayers.Base, true); + _sprite.LayerSetVisible((entity, sprite), MaxPressureVisualLayers.BaseUnshaded, true); + + // Subtract our integrity + 1 to get an accurate step count. + if (entity.Comp.IntegritySteps > 1) + { + var step = ContentHelpers.RoundToEqualLevels(maxIntegrity - integrity - 1, maxIntegrity, entity.Comp.IntegritySteps); + _sprite.LayerSetRsiState((entity, sprite), MaxPressureVisualLayers.BaseUnshaded, $"{entity.Comp.IntegrityState}-unshaded-{step}"); + } + else + { + _sprite.LayerSetRsiState((entity, sprite), MaxPressureVisualLayers.BaseUnshaded, $"{entity.Comp.IntegrityState}-unshaded-0"); + } + } +} diff --git a/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs b/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs new file mode 100644 index 0000000000..d849319875 --- /dev/null +++ b/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs @@ -0,0 +1,263 @@ +using Content.Client.Atmos.EntitySystems; +using Content.Client.Graphics; +using Content.Client.Resources; +using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; +using Content.Shared.CCVar; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; +using Robust.Shared.Configuration; +using Robust.Shared.Enums; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; +using System.Numerics; +using Color = Robust.Shared.Maths.Color; +using Texture = Robust.Client.Graphics.Texture; + +namespace Content.Client.Atmos.Overlays; + +/// +/// Overlay responsible for rendering heat distortion shader. +/// +public sealed class GasTileHeatBlurOverlay : Overlay +{ + public override bool RequestScreenTexture { get; set; } = true; + private static readonly ProtoId UnshadedShader = "unshaded"; + private static readonly ProtoId HeatOverlayShader = "HeatBlur"; + + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IClyde _clyde = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; + + private readonly SharedTransformSystem _xformSys; + private readonly ShaderInstance _shader; + + private readonly Texture _noiseTexture; + private readonly Texture _heatGradientTexture; + private List> _intersectingGrids = new(); + private readonly OverlayResourceCache _resources = new(); + + // Overlay settings + private const float + ShaderSpilling = 2.5f; // for example 4f - spills shader one tile from hotspot, 2.5f - spills it half tile + + private const float ShaderStrength = 0.04f; // Makes waves stronger + private const float ShaderScale = 1f; // Makes more waves + private const float ShaderSpeed = 0.4f; // Makes waves run faster + + // Overlay settings for reduced motion setting + private const float ShaderStrengthForReducedMotion = 0.01f; + private const float ShaderScaleReducedMotion = 0.5f; + private const float ShaderSpeedReducedMotion = 0.25f; + + private const int MinDistortionTemp = 300; // Distortion starts to show up at this temperature in Kelvins + private const int MaxDistortionTemp = 2000; // Maximum distortion strength at this temperature in Kelvins + + public override OverlaySpace Space => OverlaySpace.WorldSpace; + + public GasTileHeatBlurOverlay() + { + IoCManager.InjectDependencies(this); + _xformSys = _entManager.System(); + + _noiseTexture = _resourceCache.GetTexture("/Textures/Effects/HeatBlur/perlin_noise.png"); + _heatGradientTexture = _resourceCache.GetTexture("/Textures/Effects/HeatBlur/soft_circle.png"); + + _shader = _proto.Index(HeatOverlayShader).InstanceUnique(); + _configManager.OnValueChanged(CCVars.ReducedMotion, SetReducedMotion, invokeImmediately: true); + } + + private void SetReducedMotion(bool reducedMotion) + { + _shader.SetParameter("strength_scale", reducedMotion ? ShaderStrengthForReducedMotion : ShaderStrength); + _shader.SetParameter("spatial_scale", reducedMotion ? ShaderScaleReducedMotion : ShaderScale); + _shader.SetParameter("speed_scale", reducedMotion ? ShaderSpeedReducedMotion : ShaderSpeed); + } + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + if (args.MapId == MapId.Nullspace) + return false; + + var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources()); + + var target = args.Viewport.RenderTarget; + + // Probably the resolution of the game window changed, remake the textures. + if (res.HeatTarget?.Texture.Size != target.Size) + { + res.HeatTarget?.Dispose(); + res.HeatTarget = _clyde.CreateRenderTarget( + target.Size, + new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), + name: nameof(GasTileHeatBlurOverlaySystem)); + } + + if (res.HeatBlurTarget?.Texture.Size != target.Size) + { + res.HeatBlurTarget?.Dispose(); + res.HeatBlurTarget = _clyde.CreateRenderTarget( + target.Size, + new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), + name: $"{nameof(GasTileHeatBlurOverlaySystem)}-blur"); + } + + var overlayQuery = _entManager.GetEntityQuery(); + + args.WorldHandle.UseShader(_proto.Index(UnshadedShader).Instance()); + + var mapId = args.MapId; + var worldAABB = args.WorldAABB; + var worldBounds = args.WorldBounds; + var worldHandle = args.WorldHandle; + var worldToViewportLocal = args.Viewport.GetWorldToLocalMatrix(); + + // If there is no distortion after checking all visible tiles, we can bail early + var anyDistortion = false; + + // We're rendering in the context of the heat target texture, which will encode data as to where and how strong + // the heat distortion will be + args.WorldHandle.RenderInRenderTarget(res.HeatTarget, + () => + { + _intersectingGrids.Clear(); + _mapManager.FindGridsIntersecting(mapId, worldAABB, ref _intersectingGrids); + foreach (var grid in _intersectingGrids) + { + if (!overlayQuery.TryGetComponent(grid.Owner, out var comp)) + continue; + + var gridEntToWorld = _xformSys.GetWorldMatrix(grid.Owner); + var gridEntToViewportLocal = gridEntToWorld * worldToViewportLocal; + + if (!Matrix3x2.Invert(gridEntToViewportLocal, out var viewportLocalToGridEnt)) + continue; + + var uvToUi = Matrix3Helpers.CreateScale(res.HeatTarget.Size.X, -res.HeatTarget.Size.Y); + var uvToGridEnt = uvToUi * viewportLocalToGridEnt; + + // Because we want the actual distortion to be calculated based on the grid coordinates*, we need + // to pass a matrix transformation to go from the viewport coordinates to grid coordinates. + // * (why? because otherwise the effect would shimmer like crazy as you moved around, think + // moving a piece of warped glass above a picture instead of placing the warped glass on the + // paper and moving them together) + _shader.SetParameter("grid_ent_from_viewport_local", uvToGridEnt); + + // Draw commands (like DrawRect) will be using grid coordinates from here + worldHandle.SetTransform(gridEntToViewportLocal); + + // We only care about tiles that fit in these bounds + var worldToGridLocal = _xformSys.GetInvWorldMatrix(grid.Owner); + var floatBounds = worldToGridLocal.TransformBox(worldBounds).Enlarged(grid.Comp.TileSize); + + var localBounds = new Box2i( + (int)MathF.Floor(floatBounds.Left), + (int)MathF.Floor(floatBounds.Bottom), + (int)MathF.Ceiling(floatBounds.Right), + (int)MathF.Ceiling(floatBounds.Top)); + + // for each tile and its gas ---> + foreach (var chunk in comp.Chunks.Values) + { + var enumerator = new GasChunkEnumerator(chunk); + + while (enumerator.MoveNext(out var tileGas)) + { + // Check and make sure the tile is within the viewport/screen + var tilePosition = chunk.Origin + (enumerator.X, enumerator.Y); + if (!localBounds.Contains(tilePosition)) + continue; + + // Get the distortion strength from the temperature and bail if it's not hot enough + var strength = GetHeatDistortionStrength(tileGas.ByteGasTemperature); + if (strength <= 0f) + continue; + + anyDistortion = true; + + // Encode the strength in the red channel + // alpha set to 1 as tile is active + worldHandle.DrawTextureRect( + _heatGradientTexture, + Box2.CenteredAround(tilePosition + grid.Comp.TileSizeHalfVector, + grid.Comp.TileSizeVector * ShaderSpilling), + new Color(strength, 0f, 0f)); + } + } + } + }, + // This clears the buffer to all zero first... + new Color(0, 0, 0, 0)); + + // no distortion, no need to render + if (!anyDistortion) + { + args.WorldHandle.UseShader(null); + args.WorldHandle.SetTransform(Matrix3x2.Identity); + return false; + } + + return true; + } + + protected override void Draw(in OverlayDrawArgs args) + { + var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources()); + + if (ScreenTexture is null || res.HeatTarget is null || res.HeatBlurTarget is null) + return; + + _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); + _shader.SetParameter("NOISE_TEXTURE", _noiseTexture); + + args.WorldHandle.UseShader(_shader); + args.WorldHandle.DrawTextureRect(res.HeatTarget.Texture, args.WorldBounds); + + args.WorldHandle.UseShader(null); + args.WorldHandle.SetTransform(Matrix3x2.Identity); + } + + protected override void DisposeBehavior() + { + _resources.Dispose(); + + _configManager.UnsubValueChanged(CCVars.ReducedMotion, SetReducedMotion); + base.DisposeBehavior(); + } + + /// + /// Gets the strength of the heat distortion effect based on the temperature of the tile. + /// The strength is a value between 0 and 1, where 0 means no distortion and 1 means maximum distortion. + /// + /// The temperature of the tile. + /// The strength of the heat distortion effect. + /// + private static float GetHeatDistortionStrength(ThermalByte temp) + { + if (!temp.TryGetTemperature(out var kelvinTemp)) + { + return 0f; + } + + var strength = (kelvinTemp - MinDistortionTemp) / (MaxDistortionTemp - MinDistortionTemp); + + return MathHelper.Clamp01(strength); + } + + internal sealed class CachedResources : IDisposable + { + public IRenderTexture? HeatTarget; + public IRenderTexture? HeatBlurTarget; + + public void Dispose() + { + HeatTarget?.Dispose(); + HeatBlurTarget?.Dispose(); + } + } +} diff --git a/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs b/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs index 37298b95fd..4d909c9fc8 100644 --- a/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs +++ b/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs @@ -72,17 +72,7 @@ public sealed class GasTileVisibleGasOverlay : Overlay { var gasPrototype = _atmosphereSystem.GetGas(_gasTileOverlaySystem.VisibleGasId[i]); - SpriteSpecifier overlay; - - if (!string.IsNullOrEmpty(gasPrototype.GasOverlaySprite) && - !string.IsNullOrEmpty(gasPrototype.GasOverlayState)) - overlay = new SpriteSpecifier.Rsi(new(gasPrototype.GasOverlaySprite), gasPrototype.GasOverlayState); - else if (!string.IsNullOrEmpty(gasPrototype.GasOverlayTexture)) - overlay = new SpriteSpecifier.Texture(new(gasPrototype.GasOverlayTexture)); - else - continue; - - switch (overlay) + switch (gasPrototype.GasOverlaySprite) { case SpriteSpecifier.Rsi animated: var rsi = _resourceCache.GetResource(animated.RsiPath).RSI; diff --git a/Content.Client/Atmos/Piping/Unary/Systems/GasCanisterSystem.cs b/Content.Client/Atmos/Piping/Unary/Systems/GasCanisterSystem.cs index cae184edbb..13513e2a95 100644 --- a/Content.Client/Atmos/Piping/Unary/Systems/GasCanisterSystem.cs +++ b/Content.Client/Atmos/Piping/Unary/Systems/GasCanisterSystem.cs @@ -1,4 +1,5 @@ using Content.Client.Atmos.UI; +using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Systems; @@ -14,6 +15,12 @@ public sealed class GasCanisterSystem : SharedGasCanisterSystem SubscribeLocalEvent(OnGasState); } + protected override void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + // Atmos not predicted :( + throw new NotImplementedException(); + } + private void OnGasState(Entity ent, ref AfterAutoHandleStateEvent args) { if (UI.TryGetOpenUi(ent.Owner, GasCanisterUiKey.Key, out var bui)) diff --git a/Content.Client/Atmos/UI/GasAnalyzerBoundUserInterface.cs b/Content.Client/Atmos/UI/GasAnalyzerBoundUserInterface.cs index 3a5df3f9a8..c8f2091d13 100644 --- a/Content.Client/Atmos/UI/GasAnalyzerBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasAnalyzerBoundUserInterface.cs @@ -1,41 +1,33 @@ -using Robust.Client.GameObjects; using Robust.Client.UserInterface; -using static Content.Shared.Atmos.Components.GasAnalyzerComponent; +using Content.Shared.Atmos.Components; -namespace Content.Client.Atmos.UI +namespace Content.Client.Atmos.UI; + +public sealed class GasAnalyzerBoundUserInterface : BoundUserInterface { - public sealed class GasAnalyzerBoundUserInterface : BoundUserInterface + [ViewVariables] + private GasAnalyzerWindow? _window; + + public GasAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { - [ViewVariables] - private GasAnalyzerWindow? _window; + } - public GasAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - } + protected override void Open() + { + base.Open(); - protected override void Open() - { - base.Open(); + _window = this.CreateWindowCenteredLeft(); + _window.OnClose += Close; + } - _window = this.CreateWindowCenteredLeft(); - _window.OnClose += Close; - } + protected override void ReceiveMessage(BoundUserInterfaceMessage message) + { + if (_window == null) + return; - protected override void ReceiveMessage(BoundUserInterfaceMessage message) - { - if (_window == null) - return; - if (message is not GasAnalyzerUserMessage cast) - return; - _window.Populate(cast); - } + if (message is not GasAnalyzerUserMessage cast) + return; - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - _window?.Dispose(); - } + _window.Populate(cast); } } diff --git a/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs b/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs index 63b4e6b0c6..94a9f23745 100644 --- a/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs +++ b/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs @@ -1,6 +1,8 @@ using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.Temperature; using Robust.Client.Graphics; using Robust.Client.UserInterface; @@ -8,7 +10,6 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; -using static Content.Shared.Atmos.Components.GasAnalyzerComponent; using Direction = Robust.Shared.Maths.Direction; namespace Content.Client.Atmos.UI @@ -16,25 +17,17 @@ namespace Content.Client.Atmos.UI [GenerateTypedNameReferences] public sealed partial class GasAnalyzerWindow : DefaultWindow { + private readonly SharedAtmosphereSystem _atmosphere; private NetEntity _currentEntity = NetEntity.Invalid; public GasAnalyzerWindow() { RobustXamlLoader.Load(this); + _atmosphere = IoCManager.Resolve().System(); } public void Populate(GasAnalyzerUserMessage msg) { - if (msg.Error != null) - { - CTopBox.AddChild(new Label - { - Text = Loc.GetString("gas-analyzer-window-error-text", ("errorText", msg.Error)), - FontColorOverride = Color.Red - }); - return; - } - if (msg.NodeGasMixes.Length == 0) { CTopBox.AddChild(new Label @@ -329,31 +322,31 @@ namespace Content.Client.Atmos.UI for (var j = 0; j < gasMix.Gases.Length; j++) { - var gas = gasMix.Gases[j]; - var color = Color.FromHex($"#{gas.Color}", Color.White); + var gasEntry = gasMix.Gases[j]; + var gasProto = _atmosphere.GetGas(gasEntry.Gas); // Add to the table tableKey.AddChild(new Label { - Text = Loc.GetString(gas.Name) + Text = Loc.GetString(gasProto.Name) }); tableVal.AddChild(new Label { Text = Loc.GetString("gas-analyzer-window-molarity-text", - ("mol", $"{gas.Amount:0.00}")), + ("mol", $"{gasEntry.Amount:0.00}")), Align = Label.AlignMode.Right, }); tablePercent.AddChild(new Label { Text = Loc.GetString("gas-analyzer-window-percentage-text", - ("percentage", $"{(gas.Amount / totalGasAmount * 100):0.0}")), + ("percentage", $"{(gasEntry.Amount / totalGasAmount * 100):0.0}")), Align = Label.AlignMode.Right }); // Add to the gas bar //TODO: highlight the currently hover one - gasBar.AddEntry(gas.Amount, color, tooltip: Loc.GetString("gas-analyzer-window-molarity-percentage-text", - ("gasName", gas.Name), - ("amount", $"{gas.Amount:0.##}"), - ("percentage", $"{(gas.Amount / totalGasAmount * 100):0.#}"))); + gasBar.AddEntry(gasEntry.Amount, gasProto.Color, tooltip: Loc.GetString("gas-analyzer-window-molarity-percentage-text", + ("gasName", Loc.GetString(gasProto.Name)), + ("amount", $"{gasEntry.Amount:0.##}"), + ("percentage", $"{(gasEntry.Amount / totalGasAmount * 100):0.#}"))); } dataContainer.AddChild(gasBar); diff --git a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs index 0456426b1f..56be898a01 100644 --- a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs @@ -74,7 +74,7 @@ namespace Content.Client.Atmos.UI _window.SetTankPressure(cast.TankPressure); _window.SetReleasePressureRange(component.MinReleasePressure, component.MaxReleasePressure); _window.SetReleasePressure(component.ReleasePressure); - _window.SetReleaseValve(component.ReleaseValve); + _window.SetReleaseValve(component.ReleaseValveOpen); } protected override void Dispose(bool disposing) diff --git a/Content.Client/Audio/AmbientSoundSystem.cs b/Content.Client/Audio/AmbientSoundSystem.cs index 9929751b22..92bfc677eb 100644 --- a/Content.Client/Audio/AmbientSoundSystem.cs +++ b/Content.Client/Audio/AmbientSoundSystem.cs @@ -235,8 +235,6 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem /// private void ProcessNearbyAmbience(TransformComponent playerXform) { - var query = GetEntityQuery(); - var metaQuery = GetEntityQuery(); var mapPos = _xformSystem.GetMapCoordinates(playerXform); // Remove out-of-range ambiences @@ -249,9 +247,9 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem if (comp.Enabled && // Don't keep playing sounds that have changed since. sound.Sound == comp.Sound && - query.TryGetComponent(owner, out var xform) && + TryComp(owner, out TransformComponent? xform) && xform.MapID == playerXform.MapID && - !metaQuery.GetComponent(owner).EntityPaused) + !Paused(owner)) { // TODO: This is just trydistance for coordinates. var distance = (xform.ParentUid == playerXform.ParentUid) @@ -294,7 +292,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem var comp = sourceEntity.Comp; if (_playingSounds.ContainsKey(sourceEntity) || - metaQuery.GetComponent(uid).EntityPaused) + Paused(uid)) continue; var audioParams = _params diff --git a/Content.Client/Audio/AmbientSoundTreeSystem.cs b/Content.Client/Audio/AmbientSoundTreeSystem.cs index 7a9439c9df..2844374452 100644 --- a/Content.Client/Audio/AmbientSoundTreeSystem.cs +++ b/Content.Client/Audio/AmbientSoundTreeSystem.cs @@ -23,8 +23,7 @@ public sealed class AmbientSoundTreeSystem : ComponentTreeSystem()); + entry.Component.TreeUid.Value); return ExtractAabb(in entry, pos, default); } diff --git a/Content.Client/Body/VisualBodySystem.cs b/Content.Client/Body/VisualBodySystem.cs index fba936ee58..cf1e09bf30 100644 --- a/Content.Client/Body/VisualBodySystem.cs +++ b/Content.Client/Body/VisualBodySystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Client.DisplacementMap; using Content.Shared.Body; using Content.Shared.CCVar; using Content.Shared.Humanoid.Markings; @@ -15,6 +16,7 @@ public sealed class VisualBodySystem : SharedVisualBodySystem { [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly DisplacementMapSystem _displacement = default!; [Dependency] private readonly MarkingManager _marking = default!; [Dependency] private readonly SpriteSystem _sprite = default!; @@ -167,8 +169,11 @@ public sealed class VisualBodySystem : SharedVisualBodySystem } } - private void ApplyMarkings(Entity ent, EntityUid target) + private void ApplyMarkings(Entity ent, Entity target) { + if (!Resolve(target, ref target.Comp)) + return; + var applied = new List(); foreach (var marking in AllMarkings(ent)) { @@ -178,6 +183,8 @@ public sealed class VisualBodySystem : SharedVisualBodySystem if (!_sprite.LayerMapTryGet(target, proto.BodyPart, out var index, true)) continue; + ent.Comp.MarkingsDisplacement.TryGetValue(proto.BodyPart, out var displacement); + for (var i = 0; i < proto.Sprites.Count; i++) { var sprite = proto.Sprites[i]; @@ -190,8 +197,8 @@ public sealed class VisualBodySystem : SharedVisualBodySystem if (!_sprite.LayerMapTryGet(target, layerId, out _, false)) { - var layer = _sprite.AddLayer(target, sprite, index + i + 1); - _sprite.LayerMapSet(target, layerId, layer); + var spriteLayer = _sprite.AddLayer(target, sprite, index + i + 1); + _sprite.LayerMapSet(target, layerId, spriteLayer); _sprite.LayerSetSprite(target, layerId, rsi); } @@ -199,6 +206,9 @@ public sealed class VisualBodySystem : SharedVisualBodySystem _sprite.LayerSetColor(target, layerId, marking.MarkingColors[i]); else _sprite.LayerSetColor(target, layerId, Color.White); + + if (displacement != null && proto.CanBeDisplaced) + _displacement.TryAddDisplacement(displacement, (target, target.Comp), index + i + 1, layerId, out _); } applied.Add(marking); @@ -206,8 +216,11 @@ public sealed class VisualBodySystem : SharedVisualBodySystem ent.Comp.AppliedMarkings = applied; } - private void RemoveMarkings(Entity ent, EntityUid target) + private void RemoveMarkings(Entity ent, Entity target) { + if (!Resolve(target, ref target.Comp)) + return; + foreach (var marking in ent.Comp.AppliedMarkings) { if (!_marking.TryGetMarking(marking, out var proto)) @@ -221,6 +234,13 @@ public sealed class VisualBodySystem : SharedVisualBodySystem var layerId = $"{proto.ID}-{rsi.RsiState}"; + // If this marking is one that can be displaced, we need to remove the displacement as well; otherwise + // altering a marking at runtime can lead to the renderer falling over. + // The Vulps must be shaved. + // (https://github.com/space-wizards/space-station-14/issues/40135). + if (proto.CanBeDisplaced) + _displacement.EnsureDisplacementIsNotOnSprite((target, target.Comp), layerId); + if (!_sprite.LayerMapTryGet(target, layerId, out var index, false)) continue; diff --git a/Content.Client/CardboardBox/CardboardBoxSystem.cs b/Content.Client/CardboardBox/CardboardBoxSystem.cs index ecebe16727..a179f14a3b 100644 --- a/Content.Client/CardboardBox/CardboardBoxSystem.cs +++ b/Content.Client/CardboardBox/CardboardBoxSystem.cs @@ -14,15 +14,12 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly SpriteSystem _sprite = default!; - - private EntityQuery _mobStateQuery; + [Dependency] private readonly EntityQuery _mobStateQuery = default!; public override void Initialize() { base.Initialize(); - _mobStateQuery = GetEntityQuery(); - SubscribeNetworkEvent(OnBoxEffect); } @@ -33,11 +30,7 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem if (!TryComp(source, out var box)) return; - var xformQuery = GetEntityQuery(); - - if (!xformQuery.TryGetComponent(source, out var xform)) - return; - + var xform = Transform(source); var sourcePos = _transform.GetMapCoordinates(source, xform); //Any mob that can move should be surprised? @@ -72,7 +65,7 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem var ent = Spawn(box.Effect, mapPos); - if (!xformQuery.TryGetComponent(ent, out var entTransform) || !TryComp(ent, out var sprite)) + if (!TryComp(ent, out TransformComponent? entTransform) || !TryComp(ent, out var sprite)) continue; _sprite.SetOffset((ent, sprite), new Vector2(0, 1)); diff --git a/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs b/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs index 64d809c0c5..25cfb1ae82 100644 --- a/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs +++ b/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs @@ -4,6 +4,7 @@ using Content.Shared.Changeling.Components; using Content.Shared.Changeling.Systems; using JetBrains.Annotations; using Robust.Client.UserInterface; +using Robust.Shared.Utility; namespace Content.Client.Changeling.UI; @@ -12,7 +13,9 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne { private SimpleRadialMenu? _menu; private static readonly Color SelectedOptionBackground = Palettes.Green.Element.WithAlpha(128); + private static readonly Color DisabledOptionBackground = Palettes.Slate.Element.WithAlpha(128); private static readonly Color SelectedOptionHoverBackground = Palettes.Green.HoveredElement.WithAlpha(128); + private static readonly Color DisabledOptionHoverBackground = Palettes.Slate.HoveredElement.WithAlpha(128); protected override void Open() { @@ -23,7 +26,6 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne _menu.OpenOverMouseScreenPosition(); } - public override void Update() { if (_menu == null) @@ -32,7 +34,7 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne if (!EntMan.TryGetComponent(Owner, out var lingIdentity)) return; - var models = ConvertToButtons(lingIdentity.ConsumedIdentities, lingIdentity?.CurrentIdentity); + var models = ConvertToButtons(lingIdentity.ConsumedIdentities.Keys, lingIdentity?.CurrentIdentity); _menu.SetButtons(models); } @@ -43,21 +45,41 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne ) { var buttons = new List(); + var dropButtons = new List(); + foreach (var identity in identities) { - if (!EntMan.TryGetComponent(identity, out var metadata)) - continue; - + // Options for selecting identities. var option = new RadialMenuActionOption(SendIdentitySelect, EntMan.GetNetEntity(identity)) { IconSpecifier = RadialMenuIconSpecifier.With(identity), - ToolTip = metadata.EntityName, - BackgroundColor = (currentIdentity == identity) ? SelectedOptionBackground : null, + ToolTip = Loc.GetString("changeling-transform-bui-select-entity", ("entity", identity)), + BackgroundColor = (currentIdentity == identity) ? SelectedOptionBackground : null, // mark as selected HoverBackgroundColor = (currentIdentity == identity) ? SelectedOptionHoverBackground : null }; buttons.Add(option); + + // Options for dropping identities. + var dropOption = new RadialMenuActionOption(SendIdentityDrop, EntMan.GetNetEntity(identity)) + { + IconSpecifier = RadialMenuIconSpecifier.With(identity), + ToolTip = (currentIdentity == identity) + ? Loc.GetString("changeling-transform-bui-drop-identity-cannot-drop") + : Loc.GetString("changeling-transform-bui-drop-identity-entity", ("entity", identity)), + BackgroundColor = (currentIdentity == identity) ? DisabledOptionBackground : null, // cannot drop your current identity + HoverBackgroundColor = (currentIdentity == identity) ? DisabledOptionHoverBackground : null + }; + dropButtons.Add(dropOption); } + // Menu category for dropping identities. + var dropMenuButton = new RadialMenuNestedLayerOption(dropButtons) + { + IconSpecifier = RadialMenuIconSpecifier.With(new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/delete.svg.192dpi.png"))), + ToolTip = Loc.GetString("changeling-transform-bui-drop-identity-menu") + }; + buttons.Add(dropMenuButton); + return buttons; } @@ -65,4 +87,9 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne { SendPredictedMessage(new ChangelingTransformIdentitySelectMessage(identityId)); } + + private void SendIdentityDrop(NetEntity identityId) + { + SendPredictedMessage(new ChangelingTransformIdentityDropMessage(identityId)); + } } diff --git a/Content.Client/Clickable/ClickableSystem.cs b/Content.Client/Clickable/ClickableSystem.cs index 8834f023f4..ae3487dc87 100644 --- a/Content.Client/Clickable/ClickableSystem.cs +++ b/Content.Client/Clickable/ClickableSystem.cs @@ -16,17 +16,9 @@ public sealed class ClickableSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transforms = default!; [Dependency] private readonly SpriteSystem _sprites = default!; - private EntityQuery _clickableQuery; - private EntityQuery _xformQuery; - private EntityQuery _fadingSpriteQuery; - - public override void Initialize() - { - base.Initialize(); - _clickableQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - _fadingSpriteQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _clickableQuery = default!; + [Dependency] private readonly EntityQuery _xformQuery = default!; + [Dependency] private readonly EntityQuery _fadingSpriteQuery = default!; /// /// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding diff --git a/Content.Client/Clothing/ClientClothingSystem.cs b/Content.Client/Clothing/ClientClothingSystem.cs index 1a6db7a1b6..9c37476676 100644 --- a/Content.Client/Clothing/ClientClothingSystem.cs +++ b/Content.Client/Clothing/ClientClothingSystem.cs @@ -223,7 +223,7 @@ public sealed class ClientClothingSystem : ClothingSystem { base.OnGotEquipped(uid, component, args); - RenderEquipment(args.Equipee, uid, args.Slot, clothingComponent: component); + RenderEquipment(args.EquipTarget, uid, args.Slot, clothingComponent: component); } private void RenderEquipment(EntityUid equipee, EntityUid equipment, string slot, diff --git a/Content.Client/Corvax/ExportSprites/EntityScreenshotGenerator.cs b/Content.Client/Corvax/ExportSprites/EntityScreenshotGenerator.cs index b64a962604..de427a1e43 100644 --- a/Content.Client/Corvax/ExportSprites/EntityScreenshotGenerator.cs +++ b/Content.Client/Corvax/ExportSprites/EntityScreenshotGenerator.cs @@ -1,17 +1,23 @@ using System.Linq; using System.Threading.Tasks; +using Content.Client.Gameplay; using Content.Shared.CCVar; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Corvax.GuideGenerator; -using Content.Client.Gameplay; +using Content.Shared.Prototypes; using Robust.Client; +using Robust.Client.GameObjects; using Robust.Client.State; using Robust.Client.Timing; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; using Robust.Shared.Map; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown; +using Robust.Shared.Serialization.Markdown.Mapping; +using Robust.Shared.Serialization.Markdown.Sequence; using Robust.Shared.Utility; namespace Content.Client.Corvax.ExportSprites; @@ -29,6 +35,7 @@ public sealed class EntityScreenshotGenerator [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IResourceManager _resourceManager = default!; + [Dependency] private readonly ISerializationManager _serialization = default!; [Dependency] private readonly IStateManager _stateManager = default!; private ISawmill _sawmill = default!; @@ -120,7 +127,7 @@ public sealed class EntityScreenshotGenerator var prototypes = _prototypeManager.EnumeratePrototypes() .Where(proto => !proto.Abstract && - proto.Components.ContainsKey("Sprite") && + HasExportableSprite(proto) && EntityProjectHelper.MatchesAllowedIds(proto.ID, allowedIds)) .OrderBy(proto => proto.ID) .ToList(); @@ -136,11 +143,18 @@ public sealed class EntityScreenshotGenerator try { - entity = _entityManager.SpawnEntity(proto.ID, new EntityCoordinates(previewGrid.Owner, default)); + if (proto.HasComponent(_entityManager.ComponentFactory)) + { + entity = _entityManager.SpawnEntity(proto.ID, new EntityCoordinates(previewGrid.Owner, default)); - await WaitForEntityAppearanceAsync(entity); - ApplyPrototypeAppearance(entity, proto); - await WaitForEntityAppearanceAsync(entity, 1); + await WaitForEntityAppearanceAsync(entity); + ApplyPrototypeAppearance(entity, proto); + await WaitForEntityAppearanceAsync(entity, 1); + } + else + { + entity = SpawnIconEntity(proto); + } await _renderService.Export(entity, Direction.South, outputDir / $"{proto.ID}.png"); exported++; @@ -241,4 +255,128 @@ public sealed class EntityScreenshotGenerator if (solution.GetPrimaryReagentId() is { } reagent) appearanceSystem.SetData(entity, SolutionContainerVisuals.BaseOverride, reagent.ToString(), appearance); } + + private bool HasExportableSprite(EntityPrototype prototype) + { + if (prototype.HasComponent(_entityManager.ComponentFactory)) + return true; + + return TryGetPrototypeIcon(prototype, out _); + } + + private EntityUid SpawnIconEntity(EntityPrototype prototype) + { + if (!TryGetPrototypeIcon(prototype, out var icon) || icon == null) + throw new InvalidOperationException($"Prototype {prototype.ID} has no exportable icon."); + + var entity = _entityManager.SpawnEntity(null, MapCoordinates.Nullspace); + var sprite = _entityManager.EnsureComponent(entity); + var spriteSystem = _entitySystemManager.GetEntitySystem(); + + spriteSystem.AddBlankLayer((entity, sprite), 0); + if (icon is SpriteSpecifier.EntityPrototype entityIcon) + spriteSystem.LayerSetTexture((entity, sprite), 0, spriteSystem.Frame0(new SpriteSpecifier.EntityPrototype(entityIcon.EntityPrototypeId))); + else + spriteSystem.LayerSetSprite((entity, sprite), 0, icon); + sprite.LayerSetShader(0, "unshaded"); + spriteSystem.LayerSetVisible((entity, sprite), 0, true); + + return entity; + } + + private bool TryGetPrototypeIcon(EntityPrototype prototype, out SpriteSpecifier? icon) + { + icon = null; + + foreach (var (_, entry) in prototype.Components) + { + if (TryExtractSpriteSpecifier(entry.Component.GetType(), entry.Mapping, out icon)) + return true; + } + + return false; + } + + private bool TryExtractSpriteSpecifier(Type? expectedType, DataNode? node, out SpriteSpecifier? icon) + { + icon = null; + + if (node == null) + return false; + + if (expectedType != null && + typeof(SpriteSpecifier).IsAssignableFrom(expectedType) && + TryParseSpriteSpecifier(node, out icon)) + { + return true; + } + + if (node is MappingDataNode mapping) + { + foreach (var (key, child) in mapping.Children) + { + Type? childType = null; + + if (expectedType != null && + _serialization.TryGetVariableType(expectedType, key, out var resolvedType)) + { + childType = resolvedType; + } + + if (TryExtractSpriteSpecifier(childType, child, out icon)) + return true; + } + + return false; + } + + if (node is SequenceDataNode sequence) + { + var elementType = GetSequenceElementType(expectedType); + foreach (var child in sequence.Sequence) + { + if (TryExtractSpriteSpecifier(elementType, child, out icon)) + return true; + } + } + + return false; + } + + private static Type? GetSequenceElementType(Type? type) + { + if (type == null) + return null; + + if (type.IsArray) + return type.GetElementType(); + + var genericArguments = type.GenericTypeArguments; + if (genericArguments.Length == 1) + return genericArguments[0]; + + return null; + } + + private bool TryParseSpriteSpecifier(DataNode node, out SpriteSpecifier? icon) + { + icon = null; + + try + { + icon = _serialization.Read(node, notNullableOverride: true); + if (icon == SpriteSpecifier.Invalid) + { + icon = null; + return false; + } + + return true; + } + catch + { + icon = null; + return false; + } + } } diff --git a/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs b/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs index 848279b9db..cd05548469 100644 --- a/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs +++ b/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs @@ -3,8 +3,8 @@ using System.Threading; using System.Threading.Tasks; using Robust.Client.GameObjects; using Robust.Client.Graphics; -using Robust.Client.Utility; using Robust.Client.UserInterface; +using Robust.Client.Utility; using Robust.Shared.ContentPack; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -27,6 +27,7 @@ public sealed class EntityScreenshotRenderService private EntityScreenshotRenderControl? _control; private bool _initialized; private readonly Dictionary<(ResPath Path, string State), Image> _rsiStateImageCache = new(); + private readonly Dictionary> _textureImageCache = new(); private ISawmill _sawmill = default!; public void Initialize() @@ -48,6 +49,13 @@ public sealed class EntityScreenshotRenderService _rsiStateImageCache.Clear(); + foreach (var image in _textureImageCache.Values) + { + image.Dispose(); + } + + _textureImageCache.Clear(); + if (_control == null) return; @@ -360,7 +368,7 @@ public sealed class EntityScreenshotRenderService private static int ToDelayMilliseconds(float seconds) { - return Math.Max(1, (int)MathF.Round(seconds * 1000f)); + return Math.Max(1, (int) MathF.Round(seconds * 1000f)); } private void WriteAnimationMetadata(ResPath animationDir, IReadOnlyList animationFrames) @@ -388,7 +396,9 @@ public sealed class EntityScreenshotRenderService return false; // Keep the old render-target path for uncommon transformed sprites. - if (spriteComp.Scale != Vector2.One || spriteComp.Rotation != Angle.Zero) + if (spriteComp.Scale != Vector2.One || + spriteComp.Rotation != Angle.Zero || + spriteComp.EnableDirectionOverride) return false; var size = renderBounds.Size; @@ -407,7 +417,7 @@ public sealed class EntityScreenshotRenderService return false; if (!TryGetLayerImage(spriteLayer, direction, out var sourceImage, out var sourceRect)) - continue; + return false; var drawColor = spriteComp.Color * spriteLayer.Color; var drawOffset = ToPixelOffset(spriteComp.Offset + spriteLayer.Offset) - renderBounds.Min; @@ -429,7 +439,7 @@ public sealed class EntityScreenshotRenderService private static void BlitImage( Image sourceImage, - Rectangle sourceRect, + PixelRect sourceRect, Color modulation, Span destination, Vector2i destinationSize, @@ -519,14 +529,17 @@ public sealed class EntityScreenshotRenderService SpriteComponent.Layer layer, Direction direction, out Image image, - out Rectangle sourceRect) + out PixelRect sourceRect) { image = default!; sourceRect = default; - // Raw texture layers need a separate cache path. Use render target fallback for them. if (layer.Texture != null) - return false; + { + image = GetTextureImage(layer.Texture); + sourceRect = new PixelRect(0, 0, image.Width, image.Height); + return true; + } var rsi = layer.ActualRsi; var stateId = ((ISpriteLayer) layer).RsiState; @@ -569,10 +582,33 @@ public sealed class EntityScreenshotRenderService var target = (int) rsiDirection * framesPerDirection + frame; var targetY = target / statesX; var targetX = target % statesX; - sourceRect = new Rectangle(targetX * frameWidth, targetY * frameHeight, frameWidth, frameHeight); + sourceRect = new PixelRect(targetX * frameWidth, targetY * frameHeight, frameWidth, frameHeight); return true; } + private Image GetTextureImage(Texture texture) + { + if (_textureImageCache.TryGetValue(texture, out var cached)) + return cached; + + var image = new Image(texture.Width, texture.Height); + var pixels = image.GetPixelSpan(); + + for (var y = 0; y < texture.Height; y++) + { + for (var x = 0; x < texture.Width; x++) + { + var color = texture.GetPixel(x, y); + pixels[y * texture.Width + x] = new Rgba32(color.RByte, color.GByte, color.BByte, color.AByte); + } + } + + _textureImageCache[texture] = image; + return image; + } + + private readonly record struct PixelRect(int Left, int Top, int Width, int Height); + private sealed class EntityScreenshotRenderControl : Control { private static readonly Color ExportBackgroundColor = new(128, 128, 128, 0); diff --git a/Content.Client/DisplacementMap/DisplacementMapSystem.cs b/Content.Client/DisplacementMap/DisplacementMapSystem.cs index 6986e1c868..14075caba3 100644 --- a/Content.Client/DisplacementMap/DisplacementMapSystem.cs +++ b/Content.Client/DisplacementMap/DisplacementMapSystem.cs @@ -2,14 +2,18 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.DisplacementMap; using Robust.Client.GameObjects; using Robust.Client.Graphics; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager; namespace Content.Client.DisplacementMap; public sealed class DisplacementMapSystem : EntitySystem { - [Dependency] private readonly ISerializationManager _serialization = default!; - [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly ISerializationManager _serialization = null!; + [Dependency] private readonly SpriteSystem _sprite = null!; + + //needs to be replaced later: see comment on line 48 + private static readonly ProtoId UnshadedID = "unshaded"; private static string? BuildDisplacementLayerKey(object key) { @@ -40,7 +44,16 @@ public sealed class DisplacementMapSystem : EntitySystem EnsureDisplacementIsNotOnSprite(sprite, key); if (data.ShaderOverride is not null) - sprite.Comp.LayerSetShader(index, data.ShaderOverride); + { + //TODO : this is a kinda janky workaround for the fact that the current rendering pipeline does not have + //proper support for multiple shaders on a given layer (or an ubershader to handle stacking all of the effects well) + //should be replaced by an engine-level solution, but this is an adequate temporary solution. + //what's that phrase about temporary solutions? + sprite.Comp.LayerSetShader(index, + (sprite.Comp[index] is SpriteComponent.Layer layer && layer.ShaderPrototype == UnshadedID) + ? data.ShaderOverrideUnshaded + : data.ShaderOverride); + } //allows you not to write it every time in the YML foreach (var pair in data.SizeMaps) diff --git a/Content.Client/DoAfter/DoAfterSystem.cs b/Content.Client/DoAfter/DoAfterSystem.cs index 5802059537..407cf3e7c1 100644 --- a/Content.Client/DoAfter/DoAfterSystem.cs +++ b/Content.Client/DoAfter/DoAfterSystem.cs @@ -50,9 +50,7 @@ public sealed class DoAfterSystem : SharedDoAfterSystem var time = GameTiming.CurTime; var comp = Comp(playerEntity.Value); - var xformQuery = GetEntityQuery(); - var handsQuery = GetEntityQuery(); - Update(playerEntity.Value, active, comp, time, xformQuery, handsQuery); + Update(playerEntity.Value, active, comp, time); } /// diff --git a/Content.Client/Doors/AirlockSystem.cs b/Content.Client/Doors/AirlockSystem.cs index a445378d98..53c5061f2a 100644 --- a/Content.Client/Doors/AirlockSystem.cs +++ b/Content.Client/Doors/AirlockSystem.cs @@ -61,6 +61,11 @@ public sealed class AirlockSystem : SharedAirlockSystem if (!comp.AnimatePanel) return; + // For some reason the open panel sprite is used for both open and + // closed sprites. I really don't get it. + door.OpenSpriteStates.Add((WiresVisualLayers.MaintenancePanel, comp.OpenPanelSpriteState)); + door.ClosedSpriteStates.Add((WiresVisualLayers.MaintenancePanel, comp.OpenPanelSpriteState)); + ((Animation)door.OpeningAnimation).AnimationTracks.Add(new AnimationTrackSpriteFlick() { LayerKey = WiresVisualLayers.MaintenancePanel, diff --git a/Content.Client/Doors/DoorSystem.cs b/Content.Client/Doors/DoorSystem.cs index 0f6537eead..cba9ba1d49 100644 --- a/Content.Client/Doors/DoorSystem.cs +++ b/Content.Client/Doors/DoorSystem.cs @@ -18,13 +18,14 @@ public sealed class DoorSystem : SharedDoorSystem { base.Initialize(); SubscribeLocalEvent(OnAppearanceChange); + SubscribeLocalEvent(OnAnimationCompleted); } protected override void OnComponentInit(Entity ent, ref ComponentInit args) { var comp = ent.Comp; - comp.OpenSpriteStates = new List<(DoorVisualLayers, string)>(2); - comp.ClosedSpriteStates = new List<(DoorVisualLayers, string)>(2); + comp.OpenSpriteStates = new List<(Enum, string)>(2); + comp.ClosedSpriteStates = new List<(Enum, string)>(2); comp.OpenSpriteStates.Add((DoorVisualLayers.Base, comp.OpenSpriteState)); comp.ClosedSpriteStates.Add((DoorVisualLayers.Base, comp.ClosedSpriteState)); @@ -78,6 +79,32 @@ public sealed class DoorSystem : SharedDoorSystem }; } + private void OnAnimationCompleted(Entity ent, ref AnimationCompletedEvent args) + { + if (args.Key != DoorComponent.OpenCloseKey || !TryComp(ent, out var sprite)) + return; + + switch (ent.Comp.State) + { + case DoorState.Open: + + foreach (var (layer, layerState) in ent.Comp.OpenSpriteStates) + { + _sprite.LayerSetRsiState((ent.Owner, sprite), layer, layerState); + } + + break; + case DoorState.Closed: + + foreach (var (layer, layerState) in ent.Comp.ClosedSpriteStates) + { + _sprite.LayerSetRsiState((ent.Owner, sprite), layer, layerState); + } + + break; + } + } + private void OnAppearanceChange(Entity entity, ref AppearanceChangeEvent args) { if (args.Sprite == null) @@ -89,9 +116,6 @@ public sealed class DoorSystem : SharedDoorSystem if (AppearanceSystem.TryGetData(entity, PaintableVisuals.Prototype, out var prototype, args.Component)) UpdateSpriteLayers((entity.Owner, args.Sprite), prototype); - if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey)) - _animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey); - // We are checking beforehand since some doors may not have an emagging visual layer, and we don't want LayerSetVisible to throw an error. if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false)) _sprite.LayerSetVisible(entity.Owner, DoorVisualLayers.BaseEmagging, state == DoorState.Emagging); @@ -106,15 +130,25 @@ public sealed class DoorSystem : SharedDoorSystem switch (state) { case DoorState.Open: + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey)) + return; + foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates) { + // Allow animations to play while it's open (e.g., pinion); + // the animation unsets this so we gotta set it again. + _sprite.LayerSetAutoAnimated((entity.Owner, sprite), layer, true); _sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState); } return; case DoorState.Closed: + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey)) + return; + foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates) { + _sprite.LayerSetAutoAnimated((entity.Owner, sprite), layer, true); _sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState); } @@ -123,24 +157,36 @@ public sealed class DoorSystem : SharedDoorSystem if (entity.Comp.OpeningAnimationTime == TimeSpan.Zero) return; - _animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.AnimationKey); + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey)) + return; + + _animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.OpenCloseKey); return; case DoorState.Closing: - if (entity.Comp.ClosingAnimationTime == TimeSpan.Zero || entity.Comp.CurrentlyCrushing.Count != 0) + if (entity.Comp.ClosingAnimationTime == TimeSpan.Zero) return; - _animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.AnimationKey); + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey)) + return; + + _animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.OpenCloseKey); return; case DoorState.Denying: - _animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.AnimationKey); + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.DenyKey)) + return; + + _animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.DenyKey); return; case DoorState.Emagging: + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.EmagKey)) + return; + // We are checking beforehand since some doors may not have an emagging visual layer. if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false)) - _animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.AnimationKey); + _animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.EmagKey); return; } diff --git a/Content.Client/Drunk/DrunkOverlay.cs b/Content.Client/Drunk/DrunkOverlay.cs index 692232776a..44d82f3794 100644 --- a/Content.Client/Drunk/DrunkOverlay.cs +++ b/Content.Client/Drunk/DrunkOverlay.cs @@ -1,8 +1,8 @@ +using Content.Shared.CCVar; using Content.Shared.Drunk; -using Content.Shared.StatusEffect; -using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; +using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -11,19 +11,23 @@ namespace Content.Client.Drunk; public sealed class DrunkOverlay : Overlay { - private static readonly ProtoId Shader = "Drunk"; + private static readonly ProtoId DrunkShader = "Drunk"; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IEntitySystemManager _sysMan = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; + private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffectsSystem; public override OverlaySpace Space => OverlaySpace.WorldSpace; public override bool RequestScreenTexture => true; private readonly ShaderInstance _drunkShader; public float CurrentBoozePower = 0.0f; + // Starting phase for the rotation effect. + // Needed so it doesn't always look the same for 0 motion. + public float Phase = 0f; private const float VisualThreshold = 10.0f; private const float PowerDivisor = 250.0f; @@ -37,12 +41,22 @@ public sealed class DrunkOverlay : Overlay private const float BoozePowerScale = 8f; - private float _visualScale = 0; + private float _visualScale = 0f; + private float _timeScale = 1f; + private float _distortionScale = 1f; public DrunkOverlay() { IoCManager.InjectDependencies(this); - _drunkShader = _prototypeManager.Index(Shader).InstanceUnique(); + _statusEffectsSystem = _entityManager.System(); + _drunkShader = _prototypeManager.Index(DrunkShader).InstanceUnique(); + _configManager.OnValueChanged(CCVars.ReducedMotion, OnReducedMotionChanged, invokeImmediately: true); + } + + private void OnReducedMotionChanged(bool reducedMotion) + { + _timeScale = reducedMotion ? 0.0f : 1.0f; + _distortionScale = reducedMotion ? 4.0f : 1.0f; // Make the offset stronger to compensate the lack of motion. } protected override void FrameUpdate(FrameEventArgs args) @@ -53,15 +67,14 @@ public sealed class DrunkOverlay : Overlay if (playerEntity == null) return; - var statusSys = _sysMan.GetEntitySystem(); - if (!statusSys.TryGetMaxTime(playerEntity.Value, out var status)) + if (!_statusEffectsSystem.TryGetMaxTime(playerEntity.Value, out var status)) return; var time = status.Item2; - var power = time == null ? MaxBoozePower : (float) Math.Min((time - _timing.CurTime).Value.TotalSeconds, MaxBoozePower); + var power = time == null ? MaxBoozePower : (float)Math.Min((time - _timing.CurTime).Value.TotalSeconds, MaxBoozePower); - CurrentBoozePower += BoozePowerScale * (power - CurrentBoozePower) * args.DeltaSeconds / (power+1); + CurrentBoozePower += BoozePowerScale * (power - CurrentBoozePower) * args.DeltaSeconds / (power + 1); } protected override bool BeforeDraw(in OverlayDrawArgs args) @@ -82,8 +95,12 @@ public sealed class DrunkOverlay : Overlay return; var handle = args.WorldHandle; + _drunkShader.SetParameter("SCREEN_TEXTURE", ScreenTexture); _drunkShader.SetParameter("boozePower", _visualScale); + _drunkShader.SetParameter("timeScale", _timeScale); + _drunkShader.SetParameter("distortionScale", _distortionScale); + _drunkShader.SetParameter("phase", Phase); handle.UseShader(_drunkShader); handle.DrawRect(args.WorldBounds, Color.White); handle.UseShader(null); diff --git a/Content.Client/Drunk/DrunkSystem.cs b/Content.Client/Drunk/DrunkSystem.cs index c5fab75e7d..2e1f8157aa 100644 --- a/Content.Client/Drunk/DrunkSystem.cs +++ b/Content.Client/Drunk/DrunkSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Player; +using Robust.Shared.Random; namespace Content.Client.Drunk; @@ -10,6 +11,7 @@ public sealed class DrunkSystem : SharedDrunkSystem { [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly IRobustRandom _random = default!; private DrunkOverlay _overlay = default!; @@ -29,7 +31,10 @@ public sealed class DrunkSystem : SharedDrunkSystem private void OnStatusApplied(Entity entity, ref StatusEffectAppliedEvent args) { if (!_overlayMan.HasOverlay()) + { + _overlay.Phase = _random.NextFloat(MathF.Tau); // random starting phase for movement effect _overlayMan.AddOverlay(_overlay); + } } private void OnStatusRemoved(Entity entity, ref StatusEffectRemovedEvent args) @@ -47,6 +52,7 @@ public sealed class DrunkSystem : SharedDrunkSystem private void OnPlayerAttached(Entity entity, ref StatusEffectRelayedEvent args) { _overlayMan.AddOverlay(_overlay); + } private void OnPlayerDetached(Entity entity, ref StatusEffectRelayedEvent args) diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 4bee9dc8d7..2f853a41d9 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -121,7 +121,6 @@ namespace Content.Client.Entry _prototypeManager.RegisterIgnore("noiseChannel"); _prototypeManager.RegisterIgnore("playerConnectionWhitelist"); _prototypeManager.RegisterIgnore("spaceBiome"); - _prototypeManager.RegisterIgnore("worldgenConfig"); _prototypeManager.RegisterIgnore("gameRule"); _prototypeManager.RegisterIgnore("worldSpell"); _prototypeManager.RegisterIgnore("entitySpell"); diff --git a/Content.Client/FeedbackPopup/ClientFeedbackManager.cs b/Content.Client/FeedbackPopup/ClientFeedbackManager.cs index dd390cc2d4..5b2b52b1b7 100644 --- a/Content.Client/FeedbackPopup/ClientFeedbackManager.cs +++ b/Content.Client/FeedbackPopup/ClientFeedbackManager.cs @@ -31,7 +31,7 @@ public sealed class ClientFeedbackManager : SharedFeedbackManager /// public override void Display(List>? prototypes) { - if (prototypes == null || !NetManager.IsClient) + if (prototypes == null) return; var count = _displayedPopups.Count; @@ -42,9 +42,6 @@ public sealed class ClientFeedbackManager : SharedFeedbackManager /// public override void Remove(List>? prototypes) { - if (!NetManager.IsClient) - return; - if (prototypes == null) { _displayedPopups.Clear(); diff --git a/Content.Client/Holopad/HolopadWindow.xaml.cs b/Content.Client/Holopad/HolopadWindow.xaml.cs index 25982b901c..cb618484e2 100644 --- a/Content.Client/Holopad/HolopadWindow.xaml.cs +++ b/Content.Client/Holopad/HolopadWindow.xaml.cs @@ -173,9 +173,9 @@ public sealed partial class HolopadWindow : FancyWindow var callerId = _telephoneSystem.GetFormattedCallerIdForEntity(telephone.LastCallerId.Item1, telephone.LastCallerId.Item2, Color.LightGray, "Default", 11); var holoapdId = _telephoneSystem.GetFormattedDeviceIdForEntity(telephone.LastCallerId.Item3, Color.LightGray, "Default", 11); - CallerIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId)); - HolopadIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(holoapdId)); - LockOutIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId)); + CallerIdText.SetMessage(FormattedMessage.FromMarkupPermissive(callerId)); + HolopadIdText.SetMessage(FormattedMessage.FromMarkupPermissive(holoapdId)); + LockOutIdText.SetMessage(FormattedMessage.FromMarkupPermissive(callerId)); // Sort holopads alphabetically var holopadArray = holopads.ToArray(); diff --git a/Content.Client/IconSmoothing/IconSmoothSystem.cs b/Content.Client/IconSmoothing/IconSmoothSystem.cs index a1c47ca6be..4b8ec7be79 100644 --- a/Content.Client/IconSmoothing/IconSmoothSystem.cs +++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs @@ -18,6 +18,8 @@ namespace Content.Client.IconSmoothing { [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly EntityQuery _iconSmoothQuery = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; private readonly Queue _dirtyEntities = new(); private readonly Queue _anchorChangedEntities = new(); @@ -106,13 +108,10 @@ namespace Content.Client.IconSmoothing { base.FrameUpdate(frameTime); - var xformQuery = GetEntityQuery(); - var smoothQuery = GetEntityQuery(); - // first process anchor state changes. while (_anchorChangedEntities.TryDequeue(out var uid)) { - if (!xformQuery.TryGetComponent(uid, out var xform)) + if (!TryComp(uid, out TransformComponent? xform)) continue; if (xform.MapID == MapId.Nullspace) @@ -123,7 +122,7 @@ namespace Content.Client.IconSmoothing continue; } - DirtyNeighbours(uid, comp: null, xform, smoothQuery); + DirtyNeighbours(uid, comp: null, xform); } // Next, update actual sprites. @@ -132,19 +131,16 @@ namespace Content.Client.IconSmoothing _generation += 1; - var spriteQuery = GetEntityQuery(); - // Performance: This could be spread over multiple updates, or made parallel. while (_dirtyEntities.TryDequeue(out var uid)) { - CalculateNewSprite(uid, spriteQuery, smoothQuery, xformQuery); + CalculateNewSprite(uid); } } - public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, TransformComponent? transform = null, EntityQuery? smoothQuery = null) + public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, TransformComponent? transform = null) { - smoothQuery ??= GetEntityQuery(); - if (!smoothQuery.Value.Resolve(uid, ref comp) || !comp.Running) + if (!_iconSmoothQuery.Resolve(uid, ref comp) || !comp.Running) return; _dirtyEntities.Enqueue(uid); @@ -206,11 +202,7 @@ namespace Content.Client.IconSmoothing _anchorChangedEntities.Enqueue(uid); } - private void CalculateNewSprite(EntityUid uid, - EntityQuery spriteQuery, - EntityQuery smoothQuery, - EntityQuery xformQuery, - IconSmoothComponent? smooth = null) + private void CalculateNewSprite(EntityUid uid, IconSmoothComponent? smooth = null) { TransformComponent? xform; Entity? gridEntity = null; @@ -218,7 +210,7 @@ namespace Content.Client.IconSmoothing // The generation check prevents updating an entity multiple times per tick. // As it stands now, it's totally possible for something to get queued twice. // Generation on the component is set after an update so we can cull updates that happened this generation. - if (!smoothQuery.Resolve(uid, ref smooth, false) + if (!_iconSmoothQuery.Resolve(uid, ref smooth, false) || smooth.Mode == IconSmoothingMode.NoSprite || smooth.UpdateGeneration == _generation || !smooth.Enabled @@ -226,7 +218,7 @@ namespace Content.Client.IconSmoothing { if (smooth is { Enabled: true } && TryComp(uid, out var edge) && - xformQuery.TryGetComponent(uid, out xform)) + TryComp(uid, out xform)) { var directions = DirectionFlag.None; @@ -237,13 +229,13 @@ namespace Content.Client.IconSmoothing gridEntity = (gridUid, grid); - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)))) directions |= DirectionFlag.North; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)))) directions |= DirectionFlag.South; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)))) directions |= DirectionFlag.East; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)))) directions |= DirectionFlag.West; } @@ -253,10 +245,10 @@ namespace Content.Client.IconSmoothing return; } - xform = xformQuery.GetComponent(uid); + xform = Transform(uid); smooth.UpdateGeneration = _generation; - if (!spriteQuery.TryGetComponent(uid, out var sprite)) + if (!_spriteQuery.TryGetComponent(uid, out var sprite)) { Log.Error($"Encountered a icon-smoothing entity without a sprite: {ToPrettyString(uid)}"); RemCompDeferred(uid, smooth); @@ -281,13 +273,13 @@ namespace Content.Client.IconSmoothing switch (smooth.Mode) { case IconSmoothingMode.Corners: - CalculateNewSpriteCorners(gridEntity, smooth, spriteEnt, xform, smoothQuery); + CalculateNewSpriteCorners(gridEntity, smooth, spriteEnt, xform); break; case IconSmoothingMode.CardinalFlags: - CalculateNewSpriteCardinal(gridEntity, smooth, spriteEnt, xform, smoothQuery); + CalculateNewSpriteCardinal(gridEntity, smooth, spriteEnt, xform); break; case IconSmoothingMode.Diagonal: - CalculateNewSpriteDiagonal(gridEntity, smooth, spriteEnt, xform, smoothQuery); + CalculateNewSpriteDiagonal(gridEntity, smooth, spriteEnt, xform); break; default: throw new ArgumentOutOfRangeException(); @@ -295,7 +287,7 @@ namespace Content.Client.IconSmoothing } private void CalculateNewSpriteDiagonal(Entity? gridEntity, IconSmoothComponent smooth, - Entity sprite, TransformComponent xform, EntityQuery smoothQuery) + Entity sprite, TransformComponent xform) { if (gridEntity == null) { @@ -320,7 +312,7 @@ namespace Content.Client.IconSmoothing for (var i = 0; i < neighbors.Length; i++) { var neighbor = (Vector2i)rotation.RotateVec(neighbors[i]); - matching = matching && MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos + neighbor), smoothQuery); + matching = matching && MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos + neighbor)); } if (matching) @@ -333,7 +325,7 @@ namespace Content.Client.IconSmoothing } } - private void CalculateNewSpriteCardinal(Entity? gridEntity, IconSmoothComponent smooth, Entity sprite, TransformComponent xform, EntityQuery smoothQuery) + private void CalculateNewSpriteCardinal(Entity? gridEntity, IconSmoothComponent smooth, Entity sprite, TransformComponent xform) { var dirs = CardinalConnectDirs.None; @@ -347,13 +339,13 @@ namespace Content.Client.IconSmoothing var grid = gridEntity.Value.Comp; var pos = _mapSystem.TileIndicesFor(gridUid, grid, xform.Coordinates); - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)))) dirs |= CardinalConnectDirs.North; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)))) dirs |= CardinalConnectDirs.South; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)))) dirs |= CardinalConnectDirs.East; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)))) dirs |= CardinalConnectDirs.West; _sprite.LayerSetRsiState(sprite.AsNullable(), 0, $"{smooth.StateBase}{(int)dirs}"); @@ -372,11 +364,11 @@ namespace Content.Client.IconSmoothing CalculateEdge(sprite, directions, sprite); } - private bool MatchingEntity(IconSmoothComponent smooth, AnchoredEntitiesEnumerator candidates, EntityQuery smoothQuery) + private bool MatchingEntity(IconSmoothComponent smooth, AnchoredEntitiesEnumerator candidates) { while (candidates.MoveNext(out var entity)) { - if (smoothQuery.TryGetComponent(entity, out var other) && + if (_iconSmoothQuery.TryGetComponent(entity, out var other) && other.SmoothKey != null && (other.SmoothKey == smooth.SmoothKey || smooth.AdditionalKeys.Contains(other.SmoothKey)) && other.Enabled) @@ -388,11 +380,11 @@ namespace Content.Client.IconSmoothing return false; } - private void CalculateNewSpriteCorners(Entity? gridEntity, IconSmoothComponent smooth, Entity spriteEnt, TransformComponent xform, EntityQuery smoothQuery) + private void CalculateNewSpriteCorners(Entity? gridEntity, IconSmoothComponent smooth, Entity spriteEnt, TransformComponent xform) { var (cornerNE, cornerNW, cornerSW, cornerSE) = gridEntity == null ? (CornerFill.None, CornerFill.None, CornerFill.None, CornerFill.None) - : CalculateCornerFill(gridEntity.Value, smooth, xform, smoothQuery); + : CalculateCornerFill(gridEntity.Value, smooth, xform); // TODO figure out a better way to set multiple sprite layers. // This will currently re-calculate the sprite bounding box 4 times. @@ -422,20 +414,20 @@ namespace Content.Client.IconSmoothing CalculateEdge(spriteEnt, directions, sprite); } - private (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(Entity gridEntity, IconSmoothComponent smooth, TransformComponent xform, EntityQuery smoothQuery) + private (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(Entity gridEntity, IconSmoothComponent smooth, TransformComponent xform) { var gridUid = gridEntity.Owner; var grid = gridEntity.Comp; var pos = _mapSystem.TileIndicesFor(gridUid, grid, xform.Coordinates); - var n = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery); - var ne = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthEast)), smoothQuery); - var e = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery); - var se = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthEast)), smoothQuery); - var s = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery); - var sw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthWest)), smoothQuery); - var w = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery); - var nw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthWest)), smoothQuery); + var n = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North))); + var ne = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthEast))); + var e = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East))); + var se = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthEast))); + var s = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South))); + var sw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthWest))); + var w = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West))); + var nw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthWest))); // ReSharper disable InconsistentNaming var cornerNE = CornerFill.None; diff --git a/Content.Client/Interaction/DragDropSystem.cs b/Content.Client/Interaction/DragDropSystem.cs index a1af9f9e20..7ff8dd150f 100644 --- a/Content.Client/Interaction/DragDropSystem.cs +++ b/Content.Client/Interaction/DragDropSystem.cs @@ -48,6 +48,7 @@ public sealed class DragDropSystem : SharedDragDropSystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; // how often to recheck possible targets (prevents calling expensive // check logic each update) @@ -433,11 +434,9 @@ public sealed class DragDropSystem : SharedDragDropSystem var bounds = new Box2(mousePos.Position - expansion, mousePos.Position + expansion); var pvsEntities = _lookup.GetEntitiesIntersecting(mousePos.MapId, bounds); - var spriteQuery = GetEntityQuery(); - foreach (var entity in pvsEntities) { - if (!spriteQuery.TryGetComponent(entity, out var inRangeSprite) || + if (!_spriteQuery.TryGetComponent(entity, out var inRangeSprite) || !inRangeSprite.Visible || entity == _draggedEntity) { diff --git a/Content.Client/Inventory/ClientInventorySystem.cs b/Content.Client/Inventory/ClientInventorySystem.cs index 73f837d0d8..8cbb33d165 100644 --- a/Content.Client/Inventory/ClientInventorySystem.cs +++ b/Content.Client/Inventory/ClientInventorySystem.cs @@ -73,8 +73,8 @@ namespace Content.Client.Inventory private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent args) { - UpdateSlot(args.Equipee, component, args.Slot); - if (args.Equipee != _playerManager.LocalEntity) + UpdateSlot(args.EquipTarget, component, args.Slot); + if (args.EquipTarget != _playerManager.LocalEntity) return; var update = new SlotSpriteUpdate(null, args.SlotGroup, args.Slot, false); OnSpriteUpdate?.Invoke(update); @@ -82,8 +82,8 @@ namespace Content.Client.Inventory private void OnDidEquip(InventorySlotsComponent component, DidEquipEvent args) { - UpdateSlot(args.Equipee, component, args.Slot); - if (args.Equipee != _playerManager.LocalEntity) + UpdateSlot(args.EquipTarget, component, args.Slot); + if (args.EquipTarget != _playerManager.LocalEntity) return; var update = new SlotSpriteUpdate(args.Equipment, args.SlotGroup, args.Slot, HasComp(args.Equipment)); diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml b/Content.Client/Lathe/UI/LatheMenu.xaml index a5c8f6a85c..84f1d7835a 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml +++ b/Content.Client/Lathe/UI/LatheMenu.xaml @@ -1,8 +1,9 @@ - @@ -156,4 +157,4 @@ - + diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml.cs b/Content.Client/Lathe/UI/LatheMenu.xaml.cs index f6688a63af..adb115d352 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml.cs +++ b/Content.Client/Lathe/UI/LatheMenu.xaml.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Text; using Content.Client.Materials; +using Content.Client.UserInterface.Controls; using Content.Shared.Lathe; using Content.Shared.Lathe.Prototypes; using Content.Shared.Research.Prototypes; @@ -8,7 +9,6 @@ using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -16,7 +16,7 @@ using Robust.Shared.Utility; namespace Content.Client.Lathe.UI; [GenerateTypedNameReferences] -public sealed partial class LatheMenu : DefaultWindow +public sealed partial class LatheMenu : FancyWindow { [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -75,6 +75,7 @@ public sealed partial class LatheMenu : DefaultWindow public void SetEntity(EntityUid uid) { Entity = uid; + this.SetInfoFromEntity(_entityManager, Entity); if (_entityManager.TryGetComponent(Entity, out var latheComponent)) { diff --git a/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs b/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs index 4919a5e8f2..0f583898de 100644 --- a/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs +++ b/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs @@ -58,7 +58,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem var entityCoords = new EntityCoordinates(ent.Owner, part.Offset); var ghostEnt = Spawn(_ghostPrototype, entityCoords); - if (!XformQuery.TryGetComponent(ghostEnt, out var xform)) + if (!TryComp(ghostEnt, out TransformComponent? xform)) break; xform.LocalRotation = part.Rotation; diff --git a/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs b/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs index 51ff5c1a00..211225119b 100644 --- a/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs +++ b/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs @@ -2,9 +2,8 @@ using System.Linq; using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.Atmos; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Damage.Components; -using Content.Shared.Damage.Systems; using Content.Shared.EntityConditions.Conditions; using Content.Shared.FixedPoint; using Content.Shared.Medical.Cryogenics; @@ -20,6 +19,7 @@ public sealed partial class CryoPodWindow : FancyWindow { [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + private readonly SharedAtmosphereSystem _atmosphere = default!; public event Action? OnEjectPatientPressed; public event Action? OnEjectBeakerPressed; @@ -29,6 +29,7 @@ public sealed partial class CryoPodWindow : FancyWindow { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); + _atmosphere = _entityManager.System(); EjectPatientButton.OnPressed += _ => OnEjectPatientPressed?.Invoke(); EjectBeakerButton.OnPressed += _ => OnEjectBeakerPressed?.Invoke(); Inject1.OnPressed += _ => OnInjectPressed?.Invoke(1); @@ -71,16 +72,16 @@ public sealed partial class CryoPodWindow : FancyWindow { var totalGasAmount = msg.GasMix.Gases.Sum(gas => gas.Amount); - foreach (var gas in msg.GasMix.Gases) + foreach (var gasEntry in msg.GasMix.Gases) { - var color = Color.FromHex($"#{gas.Color}", Color.White); - var percent = gas.Amount / totalGasAmount * 100; - var localizedName = Loc.GetString(gas.Name); + var gasProto = _atmosphere.GetGas(gasEntry.Gas); + var percent = gasEntry.Amount / totalGasAmount * 100; + var localizedName = Loc.GetString(gasProto.Name); var tooltip = Loc.GetString("gas-analyzer-window-molarity-percentage-text", ("gasName", localizedName), - ("amount", $"{gas.Amount:0.##}"), + ("amount", $"{gasEntry.Amount:0.##}"), ("percentage", $"{percent:0.#}")); - GasMixChart.AddEntry(gas.Amount, color, tooltip: tooltip); + GasMixChart.AddEntry(gasEntry.Amount, gasProto.Color, tooltip: tooltip); } } diff --git a/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs b/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs index eb60e4fbb6..c806ae5aed 100644 --- a/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs +++ b/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs @@ -10,15 +10,12 @@ namespace Content.Client.Movement.Systems; public sealed class ClientSpriteMovementSystem : SharedSpriteMovementSystem { [Dependency] private readonly SpriteSystem _sprite = default!; - - private EntityQuery _spriteQuery; + [Dependency] private readonly EntityQuery _spriteQuery = default!; public override void Initialize() { base.Initialize(); - _spriteQuery = GetEntityQuery(); - SubscribeLocalEvent(OnAfterAutoHandleState); } diff --git a/Content.Client/Movement/Systems/FloorOcclusionSystem.cs b/Content.Client/Movement/Systems/FloorOcclusionSystem.cs index 6520d98ac9..3e1084bebb 100644 --- a/Content.Client/Movement/Systems/FloorOcclusionSystem.cs +++ b/Content.Client/Movement/Systems/FloorOcclusionSystem.cs @@ -12,14 +12,12 @@ public sealed class FloorOcclusionSystem : SharedFloorOcclusionSystem [Dependency] private readonly IPrototypeManager _proto = default!; - private EntityQuery _spriteQuery; + [Dependency] private readonly EntityQuery _spriteQuery = default!; public override void Initialize() { base.Initialize(); - _spriteQuery = GetEntityQuery(); - SubscribeLocalEvent(OnOcclusionStartup); SubscribeLocalEvent(OnOcclusionShutdown); SubscribeLocalEvent(OnOcclusionAuto); diff --git a/Content.Client/Nutrition/EntitySystems/CreamPieSystem.cs b/Content.Client/Nutrition/EntitySystems/CreamPieSystem.cs new file mode 100644 index 0000000000..96a63cc694 --- /dev/null +++ b/Content.Client/Nutrition/EntitySystems/CreamPieSystem.cs @@ -0,0 +1,66 @@ +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Robust.Client.GameObjects; + +namespace Content.Client.Nutrition.EntitySystems; + +public sealed class CreamPieSystem : SharedCreamPieSystem +{ + [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly AppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnComponentShutdown); + SubscribeLocalEvent(OnAppearanceChange); + SubscribeLocalEvent(OnAfterAutoHandleState); + } + + private void OnComponentInit(Entity ent, ref ComponentInit args) + { + UpdateAppearance(ent); + } + + private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) + { + _sprite.RemoveLayer(ent.Owner, CreamPiedVisualLayer.Key); + } + + private void OnAppearanceChange(Entity ent, ref AppearanceChangeEvent args) + { + UpdateAppearance((ent.Owner, ent.Comp, args.Sprite, args.Component)); + } + + private void OnAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + // Update when the sprite datafield is changed so that changelings can transform properly. + UpdateAppearance(ent); + } + + private void UpdateAppearance(Entity ent) + { + if (!Resolve(ent, ref ent.Comp2, false) || !Resolve(ent, ref ent.Comp3, false)) + return; + + var creamPied = ent.Comp1; + var sprite = ent.Comp2; + var appearance = ent.Comp3; + + // If there is no sprite to use, remove the layer. Otherwise ensure that it exists and set the visuals accordingly. + int index; + if (creamPied.Sprite == null) + { + _sprite.RemoveLayer((ent.Owner, sprite), CreamPiedVisualLayer.Key); + return; + } + + index = _sprite.LayerMapReserve((ent.Owner, sprite), CreamPiedVisualLayer.Key); + + _appearance.TryGetData(ent.Owner, CreamPiedVisuals.Creamed, out var isCreamPied, appearance); + _sprite.LayerSetSprite((ent.Owner, sprite), index, creamPied.Sprite); + _sprite.LayerSetVisible((ent.Owner, sprite), index, isCreamPied); + } +} diff --git a/Content.Client/Nutrition/EntitySystems/CreamPiedSystem.cs b/Content.Client/Nutrition/EntitySystems/CreamPiedSystem.cs deleted file mode 100644 index 24632b71fa..0000000000 --- a/Content.Client/Nutrition/EntitySystems/CreamPiedSystem.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Content.Shared.Nutrition.EntitySystems; -using JetBrains.Annotations; - -namespace Content.Client.Nutrition.EntitySystems -{ - [UsedImplicitly] - public sealed class CreamPiedSystem : SharedCreamPieSystem - { - } -} diff --git a/Content.Client/Outline/TargetOutlineSystem.cs b/Content.Client/Outline/TargetOutlineSystem.cs index 73d45a17a9..23752fbb9b 100644 --- a/Content.Client/Outline/TargetOutlineSystem.cs +++ b/Content.Client/Outline/TargetOutlineSystem.cs @@ -27,6 +27,7 @@ public sealed class TargetOutlineSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; private bool _enabled = false; @@ -130,11 +131,10 @@ public sealed class TargetOutlineSystem : EntitySystem var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition).Position; var bounds = new Box2(mousePos - LookupVector, mousePos + LookupVector); var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentEye.Position.MapId, bounds, LookupFlags.Approximate | LookupFlags.Static); - var spriteQuery = GetEntityQuery(); foreach (var entity in pvsEntities) { - if (!spriteQuery.TryGetComponent(entity, out var sprite) || !sprite.Visible) + if (!_spriteQuery.TryGetComponent(entity, out var sprite) || !sprite.Visible) continue; // Check the predicate diff --git a/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs b/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs index 1319a3d74e..f0cc9b2f59 100644 --- a/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs +++ b/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.Chemistry.Components; using Content.Shared.Polymorph.Components; using Content.Shared.Polymorph.Systems; using Robust.Client.GameObjects; -using Robust.Shared.Player; namespace Content.Client.Polymorph.Systems; @@ -13,16 +12,13 @@ public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SpriteSystem _sprite = default!; - private EntityQuery _appearanceQuery; - private EntityQuery _spriteQuery; + [Dependency] private readonly EntityQuery _appearanceQuery = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; public override void Initialize() { base.Initialize(); - _appearanceQuery = GetEntityQuery(); - _spriteQuery = GetEntityQuery(); - SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent(OnStartup); diff --git a/Content.Client/Radiation/Systems/RadiationSystem.cs b/Content.Client/Radiation/Systems/RadiationSystem.cs index f4f109adc7..f719b7b5b8 100644 --- a/Content.Client/Radiation/Systems/RadiationSystem.cs +++ b/Content.Client/Radiation/Systems/RadiationSystem.cs @@ -5,7 +5,7 @@ using Robust.Client.Graphics; namespace Content.Client.Radiation.Systems; -public sealed class RadiationSystem : EntitySystem +public sealed class RadiationSystem : SharedRadiationSystem { [Dependency] private readonly IOverlayManager _overlayMan = default!; diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs index e7d01713e5..3408a2af26 100644 --- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs +++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs @@ -74,8 +74,7 @@ public sealed partial class ReplaySpectatorSystem if ((Direction & DirectionFlag.East) != 0) effectiveDir &= ~DirectionFlag.West; - var query = GetEntityQuery(); - var xform = query.GetComponent(player); + var xform = Transform(player); var pos = _transform.GetWorldPosition(xform); if (!xform.ParentUid.IsValid()) diff --git a/Content.Client/Silicons/Borgs/BorgSystem.Battery.cs b/Content.Client/Silicons/Borgs/BorgSystem.Battery.cs index 52398921d4..6b946f9cf8 100644 --- a/Content.Client/Silicons/Borgs/BorgSystem.Battery.cs +++ b/Content.Client/Silicons/Borgs/BorgSystem.Battery.cs @@ -13,16 +13,11 @@ public sealed partial class BorgSystem // Don't put this on the component because we only need to track the time for a single entity // and we don't want to TryComp it every single tick. private TimeSpan _nextAlertUpdate = TimeSpan.Zero; - private EntityQuery _chassisQuery; - private EntityQuery _slotQuery; public void InitializeBattery() { SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); - - _chassisQuery = GetEntityQuery(); - _slotQuery = GetEntityQuery(); } private void OnPlayerAttached(Entity ent, ref LocalPlayerAttachedEvent args) diff --git a/Content.Client/Silicons/Borgs/BorgSystem.cs b/Content.Client/Silicons/Borgs/BorgSystem.cs index 517027d082..09e6e387f7 100644 --- a/Content.Client/Silicons/Borgs/BorgSystem.cs +++ b/Content.Client/Silicons/Borgs/BorgSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Mobs; using Content.Shared.Power.EntitySystems; using Content.Shared.PowerCell; +using Content.Shared.PowerCell.Components; using Content.Shared.Silicons.Borgs; using Content.Shared.Silicons.Borgs.Components; using Robust.Client.GameObjects; @@ -22,6 +23,8 @@ public sealed partial class BorgSystem : SharedBorgSystem [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly EntityQuery _chassisQuery = default!; + [Dependency] private readonly EntityQuery _slotQuery = default!; public override void Initialize() { diff --git a/Content.Client/Singularity/SingularityOverlay.cs b/Content.Client/Singularity/SingularityOverlay.cs index 39b0758bf1..fd888fb6dd 100644 --- a/Content.Client/Singularity/SingularityOverlay.cs +++ b/Content.Client/Singularity/SingularityOverlay.cs @@ -1,9 +1,11 @@ +using System.Numerics; +using Content.Shared.CCVar; using Content.Shared.Singularity.Components; using Robust.Client.Graphics; using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.Prototypes; -using System.Numerics; namespace Content.Client.Singularity { @@ -13,6 +15,7 @@ namespace Content.Client.Singularity [Dependency] private readonly IEntityManager _entMan = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; private SharedTransformSystem? _xformSystem = null; /// @@ -28,6 +31,8 @@ namespace Content.Client.Singularity private readonly ShaderInstance _shader; + private bool _reducedMotion; + public SingularityOverlay() { IoCManager.InjectDependencies(this); @@ -35,6 +40,8 @@ namespace Content.Client.Singularity _shader.SetParameter("maxDistance", MaxDistance * EyeManager.PixelsPerMeter); _entMan.EventBus.SubscribeEvent(EventSource.Local, this, OnProjectFromScreenToMap); ZIndex = 101; // Should be drawn after the placement overlay so admins placing items near the singularity can tell where they're going. + + _configManager.OnValueChanged(CCVars.ReducedMotion, (b) => { _reducedMotion = b; }, invokeImmediately: true); } private readonly Vector2[] _positions = new Vector2[MaxCount]; @@ -44,6 +51,8 @@ namespace Content.Client.Singularity protected override bool BeforeDraw(in OverlayDrawArgs args) { + if (_reducedMotion) + return false; if (args.Viewport.Eye == null) return false; if (_xformSystem is null && !_entMan.TrySystem(out _xformSystem)) @@ -102,6 +111,8 @@ namespace Content.Client.Singularity /// private void OnProjectFromScreenToMap(ref PixelToMapEvent args) { // Mostly copypasta from the singularity shader. + if (_reducedMotion) + return; if (args.Viewport.Eye == null) return; var maxDistance = MaxDistance * EyeManager.PixelsPerMeter; diff --git a/Content.Client/Sprite/SpriteFadeSystem.cs b/Content.Client/Sprite/SpriteFadeSystem.cs index b347411040..bdc49bd25f 100644 --- a/Content.Client/Sprite/SpriteFadeSystem.cs +++ b/Content.Client/Sprite/SpriteFadeSystem.cs @@ -27,16 +27,15 @@ public sealed class SpriteFadeSystem : EntitySystem [Dependency] private readonly IInputManager _inputManager = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; + [Dependency] private readonly EntityQuery _fadeQuery = default!; + [Dependency] private readonly EntityQuery _fadingQuery = default!; + [Dependency] private readonly EntityQuery _fixturesQuery = default!; private List<(MapCoordinates Point, bool ExcludeBoundingBox)> _points = new(); private readonly HashSet _comps = new(); - private EntityQuery _spriteQuery; - private EntityQuery _fadeQuery; - private EntityQuery _fadingQuery; - private EntityQuery _fixturesQuery; - private const float TargetAlpha = 0.4f; private const float ChangeRate = 1f; @@ -44,11 +43,6 @@ public sealed class SpriteFadeSystem : EntitySystem { base.Initialize(); - _spriteQuery = GetEntityQuery(); - _fadeQuery = GetEntityQuery(); - _fadingQuery = GetEntityQuery(); - _fixturesQuery = GetEntityQuery(); - SubscribeLocalEvent(OnFadingShutdown); } diff --git a/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs b/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs index 85c6b8e066..4f58919d78 100644 --- a/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs +++ b/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs @@ -5,14 +5,12 @@ namespace Content.Client.Sticky.Visualizers; public sealed class StickyVisualizerSystem : VisualizerSystem { - private EntityQuery _spriteQuery; + [Dependency] private readonly EntityQuery _spriteQuery = default!; public override void Initialize() { base.Initialize(); - _spriteQuery = GetEntityQuery(); - SubscribeLocalEvent(OnInit); } diff --git a/Content.Client/Store/StoreSystem.cs b/Content.Client/Store/StoreSystem.cs new file mode 100644 index 0000000000..cb6e42a0d7 --- /dev/null +++ b/Content.Client/Store/StoreSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Store; + +namespace Content.Client.Store; + +public sealed class StoreSystem : SharedStoreSystem; diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs index d8236604bf..36eec4a671 100644 --- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs +++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs @@ -1,7 +1,6 @@ +using System.Linq; using Content.Shared.Store; using JetBrains.Annotations; -using System.Linq; -using Content.Shared.Store.Components; using Robust.Client.UserInterface; using Robust.Shared.Prototypes; @@ -11,6 +10,7 @@ namespace Content.Client.Store.Ui; public sealed class StoreBoundUserInterface : BoundUserInterface { private IPrototypeManager _prototypeManager = default!; + private readonly StoreSystem _storeSystem = default!; [ViewVariables] private StoreMenu? _menu; @@ -23,6 +23,7 @@ public sealed class StoreBoundUserInterface : BoundUserInterface public StoreBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { + _storeSystem = EntMan.System(); } protected override void Open() @@ -30,12 +31,12 @@ public sealed class StoreBoundUserInterface : BoundUserInterface base.Open(); _menu = this.CreateWindow(); - if (EntMan.TryGetComponent(Owner, out var store)) - _menu.Title = Loc.GetString(store.Name); + if (_storeSystem.TryGetStore(Owner, out var store)) + _menu.Title = Loc.GetString(store.Value.Comp.Name); _menu.OnListingButtonPressed += (_, listing) => { - SendMessage(new StoreBuyListingMessage(listing.ID)); + SendMessage(new StoreBuyListingMessage(listing.ID, EntMan.GetNetEntity(Owner))); }; _menu.OnCategoryButtonPressed += (_, category) => diff --git a/Content.Client/Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs b/Content.Client/Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs index d1d24db96d..a9ffa9e8a8 100644 --- a/Content.Client/Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs +++ b/Content.Client/Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs @@ -2,7 +2,7 @@ using Content.Client.Stylesheets.Palette; namespace Content.Client.Stylesheets.Stylesheets; -public sealed partial class NanotrasenStylesheet +public partial class NanotrasenStylesheet { //WL-Change-start public override ColorPalette PrimaryPalette => Palettes.WL2; diff --git a/Content.Client/SubFloor/TrayScannerSystem.cs b/Content.Client/SubFloor/TrayScannerSystem.cs index 4c67890f6a..a23d67a98b 100644 --- a/Content.Client/SubFloor/TrayScannerSystem.cs +++ b/Content.Client/SubFloor/TrayScannerSystem.cs @@ -20,6 +20,8 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly TrayScanRevealSystem _trayScanReveal = default!; + [Dependency] private readonly EntityQuery _trayScannerQuery = default!; + [Dependency] private readonly EntityQuery _subFloorHideQuery = default!; private const string TRayAnimationKey = "trays"; private const double AnimationLength = 0.3; @@ -35,16 +37,14 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem // TODO: Multiple viewports or w/e var player = _player.LocalEntity; - var xformQuery = GetEntityQuery(); - if (!xformQuery.TryGetComponent(player, out var playerXform)) + if (!TryComp(player, out TransformComponent? playerXform)) return; - var playerPos = _transform.GetWorldPosition(playerXform, xformQuery); + var playerPos = _transform.GetWorldPosition(playerXform); var playerMap = playerXform.MapID; var range = 0f; HashSet> inRange; - var scannerQuery = GetEntityQuery(); // TODO: Should probably sub to player attached changes / inventory changes but inventory's // API is extremely skrungly. If this ever shows up on dottrace ping me and laugh. @@ -57,7 +57,7 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem { foreach (var ent in slot.ContainedEntities) { - if (!scannerQuery.TryGetComponent(ent, out var sneakScanner) || !sneakScanner.Enabled) + if (!_trayScannerQuery.TryGetComponent(ent, out var sneakScanner) || !sneakScanner.Enabled) continue; canSee = true; @@ -71,7 +71,7 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem if (!_hands.TryGetHeldItem(player.Value, hand, out var heldEntity)) continue; - if (!scannerQuery.TryGetComponent(heldEntity, out var heldScanner) || !heldScanner.Enabled) + if (!_trayScannerQuery.TryGetComponent(heldEntity, out var heldScanner) || !heldScanner.Enabled) continue; range = MathF.Max(heldScanner.Range, range); @@ -93,13 +93,12 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem } var revealedQuery = AllEntityQuery(); - var subfloorQuery = GetEntityQuery(); while (revealedQuery.MoveNext(out var uid, out _, out var sprite)) { // Revealing // Add buffer range to avoid flickers. - if (subfloorQuery.TryGetComponent(uid, out var subfloor) && + if (_subFloorHideQuery.TryGetComponent(uid, out var subfloor) && inRange.Contains((uid, subfloor))) { // Due to the fact client is predicting this server states will reset it constantly diff --git a/Content.Client/UserInterface/BuiPreTickUpdateSystem.cs b/Content.Client/UserInterface/BuiPreTickUpdateSystem.cs index 330cb51dcc..a25d89543d 100644 --- a/Content.Client/UserInterface/BuiPreTickUpdateSystem.cs +++ b/Content.Client/UserInterface/BuiPreTickUpdateSystem.cs @@ -33,15 +33,7 @@ public sealed class BuiPreTickUpdateSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = null!; [Dependency] private readonly UserInterfaceSystem _uiSystem = null!; [Dependency] private readonly IGameTiming _gameTiming = null!; - - private EntityQuery _userQuery; - - public override void Initialize() - { - base.Initialize(); - - _userQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _userQuery = default!; public void RunUpdates() { diff --git a/Content.Client/UserInterface/Controls/RadialMenu.cs b/Content.Client/UserInterface/Controls/RadialMenu.cs index 959a60ef4f..0cc207dd89 100644 --- a/Content.Client/UserInterface/Controls/RadialMenu.cs +++ b/Content.Client/UserInterface/Controls/RadialMenu.cs @@ -228,7 +228,6 @@ public class RadialMenu : BaseWindow /// Base class for radial menu buttons. Excludes all actions except clicks and alt-clicks /// from interactions. /// -[Virtual] public abstract class RadialMenuButtonBase : BaseButton { /// diff --git a/Content.Client/UserInterface/Controls/SlotControl.cs b/Content.Client/UserInterface/Controls/SlotControl.cs index 2b43f2397d..d55b4acf55 100644 --- a/Content.Client/UserInterface/Controls/SlotControl.cs +++ b/Content.Client/UserInterface/Controls/SlotControl.cs @@ -9,7 +9,6 @@ using Robust.Shared.Prototypes; namespace Content.Client.UserInterface.Controls { - [Virtual] public abstract class SlotControl : Control, IEntityControl { public static int DefaultButtonSize = 64; diff --git a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs index 19e578fda3..52951b8eb6 100644 --- a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs +++ b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs @@ -46,7 +46,7 @@ namespace Content.Client.UserInterface.Systems.Atmos.GasTank if (EntMan.TryGetComponent(Owner, out GasTankComponent? component)) { var canConnect = EntMan.System().CanConnectToInternals((Owner, component)); - _window?.Update(canConnect, component.IsConnected, component.OutputPressure); + _window?.Update(canConnect, component.IsConnected, component.ReleasePressure); } if (state is GasTankBoundUserInterfaceState cast) diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs index 8094a77b6b..cd18c3e696 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs @@ -11,7 +11,6 @@ public interface IItemslotUIContainer public bool TryAddButton(SlotControl control); } -[Virtual] public abstract class ItemSlotUIContainer : GridContainer, IItemslotUIContainer where T : SlotControl { private readonly Dictionary _buttons = new(); diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 77b8531888..e0bf99d1cb 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -34,6 +34,7 @@ namespace Content.Client.Verbs [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; private float _lookupSize; @@ -159,10 +160,9 @@ namespace Content.Client.Verbs if (container == null && (visibility & MenuVisibility.InContainer) == 0) return entities.Count != 0; - var spriteQuery = GetEntityQuery(); for (var i = entities.Count - 1; i >= 0; i--) { - if (!spriteQuery.TryGetComponent(entities[i], out var spriteComponent) || !spriteComponent.Visible) + if (!_spriteQuery.TryGetComponent(entities[i], out var spriteComponent) || !spriteComponent.Visible) entities.RemoveSwap(i); } diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs index 8971508969..c792910fcf 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs @@ -32,7 +32,7 @@ public sealed partial class MeleeWeaponSystem if (localPos == Vector2.Zero || animation == null) return; - if (!_xformQuery.TryGetComponent(user, out var userXform) || userXform.MapID == MapId.Nullspace) + if (!TryComp(user, out TransformComponent? userXform) || userXform.MapID == MapId.Nullspace) return; var animationUid = Spawn(animation, userXform.Coordinates); @@ -64,7 +64,7 @@ public sealed partial class MeleeWeaponSystem } _sprite.SetRotation((animationUid, sprite), localPos.ToWorldAngle()); - var xform = _xformQuery.GetComponent(animationUid); + var xform = Transform(animationUid); TrackUserComponent track; switch (arcComponent.Animation) diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index 420e18748f..5ecbaf63b8 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -35,14 +35,12 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem [Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; - private EntityQuery _xformQuery; - private const string MeleeLungeKey = "melee-lunge"; public override void Initialize() { base.Initialize(); - _xformQuery = GetEntityQuery(); + SubscribeNetworkEvent(OnMeleeLunge); UpdatesOutsidePrediction = true; } @@ -177,7 +175,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem private void ClientHeavyAttack(EntityUid user, EntityCoordinates coordinates, EntityUid meleeUid, MeleeWeaponComponent component) { // Only run on first prediction to avoid the potential raycast entities changing. - if (!_xformQuery.TryGetComponent(user, out var userXform) || + if (!TryComp(user, out TransformComponent? userXform) || !Timing.IsFirstTimePredicted) { return; diff --git a/Content.Client/Weather/WeatherSystem.cs b/Content.Client/Weather/WeatherSystem.cs index 4b63c05991..a7d8343fb2 100644 --- a/Content.Client/Weather/WeatherSystem.cs +++ b/Content.Client/Weather/WeatherSystem.cs @@ -20,19 +20,15 @@ public sealed class WeatherSystem : SharedWeatherSystem [Dependency] private readonly MapSystem _mapSystem = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - private EntityQuery _audioQuery; - private EntityQuery _gridQuery; - private EntityQuery _roofQuery; + [Dependency] private readonly EntityQuery _audioQuery = default!; + [Dependency] private readonly EntityQuery _gridQuery = default!; + [Dependency] private readonly EntityQuery _roofQuery = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentShutdown); - - _audioQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - _roofQuery = GetEntityQuery(); } private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) diff --git a/Content.IntegrationTests/Fixtures/Attributes/EnsureCVarAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/EnsureCVarAttribute.cs new file mode 100644 index 0000000000..50da744ecf --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/EnsureCVarAttribute.cs @@ -0,0 +1,74 @@ +#nullable enable +using System.Reflection; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using Robust.Shared.Configuration; + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Ensures the given CVar, on the given side (or both), is the given value. +/// Attribute version of , and stores the old value the same way. +/// +/// This only works with fixtures. +/// The side to set the CVar on, or both. +/// The type the CVar is defined on. +/// The name of the static field defining the CVar. +/// The value to set the CVar to. +/// +/// +/// [Test] +/// [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.FlavorText), true)] +/// public async Task MyTest() +/// { +/// // CVar is set for you inside the test, and automatically un-set on teardown. +/// } +/// +/// +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] +public sealed class EnsureCVarAttribute(Side side, Type definitionType, string fieldName, object value) : Attribute, IGameTestModifier, IApplyToTest +{ + public const string ClientEnsuredCVarsProperty = "ClientEnsuredCVars"; + public const string ServerEnsuredCVarsProperty = "ServerEnsuredCVars"; + + Task IGameTestModifier.ApplyToTest(GameTest test) + { + var cvar = LookupCVar(); + + test.PreTestAddOverride(side, cvar.Name, value); + + return Task.CompletedTask; + } + + private CVarDef LookupCVar() + { + var field = definitionType.GetField(fieldName, BindingFlags.Static | BindingFlags.Public); + if (field is null) + throw new ArgumentException($"Couldn't find a public, static field named {fieldName} on {definitionType}"); + + var obj = field.GetValue(null); + + if (obj is not CVarDef cvar) + { + throw new ArgumentException( + $"Expected a CVar definition on {definitionType}.{fieldName}, but it was a {obj?.GetType().FullName ?? "null"}"); + } + + if (value.GetType() != cvar.DefaultValue.GetType()) + throw new NotSupportedException($"Cannot set {cvar.Name} to {value}, it's the wrong type."); + + return cvar; + } + + void IApplyToTest.ApplyToTest(Test test) + { + var cvar = LookupCVar(); + + if ((side & Side.Client) != 0) + test.Properties.Add(ClientEnsuredCVarsProperty, $"{cvar.Name} = {value}"); + + if ((side & Side.Server) != 0) + test.Properties.Add(ServerEnsuredCVarsProperty, $"{cvar.Name} = {value}"); + } +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/IGameTestModifier.cs b/Content.IntegrationTests/Fixtures/Attributes/IGameTestModifier.cs new file mode 100644 index 0000000000..70cd904f3f --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/IGameTestModifier.cs @@ -0,0 +1,20 @@ +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Marks an attribute as a modifier for fixtures. +/// These attributes can be applied to both test methods and fixtures. +/// +/// +/// GameTest modifiers are encouraged to also implement IApplyToTest and add properties to the test +/// indicating their presence. +/// +public interface IGameTestModifier +{ + /// + /// Method called by GameTest on itself when applying modifiers. + /// + /// The test being modified + /// Async task to await. + Task ApplyToTest(GameTest test); +} + diff --git a/Content.IntegrationTests/Fixtures/Attributes/IGameTestPairConfigModifier.cs b/Content.IntegrationTests/Fixtures/Attributes/IGameTestPairConfigModifier.cs new file mode 100644 index 0000000000..e47b99b146 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/IGameTestPairConfigModifier.cs @@ -0,0 +1,25 @@ +#nullable enable + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Interface used for pair configuration attributes. +/// This allows such attributes to modify the pair settings, and also describe what parts of pairs they modify +/// so odd configuration choices can be spotted. +/// +public interface IGameTestPairConfigModifier +{ + /// + /// Whether this modifier is exclusive and should conflict with other exclusive modifiers. + /// Essentially, fail immediately if other IGameTestPairConfigModifier attributes are present if this is set. + /// + bool Exclusive { get; } + + /// + /// Called when GameTest needs its modified by the modifier. + /// + /// The test we're applying to. + /// The settings object to modify. + void ApplyToPairSettings(GameTest test, ref PoolSettings settings); +} + diff --git a/Content.IntegrationTests/Fixtures/Attributes/PairConfigAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/PairConfigAttribute.cs new file mode 100644 index 0000000000..d197539997 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/PairConfigAttribute.cs @@ -0,0 +1,53 @@ +#nullable enable +using System.Reflection; + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Configures the test pair using settings from the given type (by default the current test) and static property member. +/// +/// The type to look up the member on, if any. +/// The static property to read the settings from. +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +public sealed class PairConfigAttribute(Type? sourceType, string sourceMember) : Attribute, IGameTestPairConfigModifier +{ + public bool Exclusive => true; + + public readonly Type? SourceType = sourceType; + public readonly string SourceMember = sourceMember; + + private const BindingFlags PropertyBindingFlags = BindingFlags.Static + | BindingFlags.Public + | BindingFlags.NonPublic + | BindingFlags.FlattenHierarchy; + + public PairConfigAttribute(string sourceMember) : this(null, sourceMember) + { + } + + public void ApplyToPairSettings(GameTest test, ref PoolSettings settings) + { + var sourceType = SourceType ?? test.GetType(); + + var property = sourceType.GetProperty(SourceMember, PropertyBindingFlags); + + if (property is null) + { + if (sourceType.GetField(SourceMember, PropertyBindingFlags) is not null) + { + throw new ArgumentException( + $"Couldn't find static property {SourceMember} on {sourceType.Name}, but could find a field. Only properties are allowed."); + } + + throw new ArgumentException($"Couldn't find static property {SourceMember} on {sourceType.Name}"); + } + + if (!property.PropertyType.IsAssignableTo(typeof(PoolSettings))) + { + throw new ArgumentException( + $"{sourceType.Name}.{SourceMember} is not assignable to {nameof(PoolSettings)} and cannot be used."); + } + + settings = (PoolSettings)property.GetValue(null)!; + } +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/RunOnSideAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/RunOnSideAttribute.cs new file mode 100644 index 0000000000..75a22e9334 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/RunOnSideAttribute.cs @@ -0,0 +1,79 @@ +#nullable enable +using Content.IntegrationTests.NUnit.Utilities; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using NUnit.Framework.Internal.Commands; + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Ensures a test method runs on the given side (client or server, not neither nor both). +/// +/// +/// This only works for fixtures. +/// +/// +[AttributeUsage(AttributeTargets.Method)] +public sealed class RunOnSideAttribute : Attribute, IWrapTestMethod, IImplyFixture, IApplyToTest +{ + public const string RunOnSideProperty = "RanOnSide"; + + /// + /// Which side to run the inner test code on, if not the test thread. + /// + public Side RunOnSide { get; } + + public RunOnSideAttribute(Side side) + { + RunOnSide = side; + if (side is not Side.Client and not Side.Server) + throw new NotSupportedException("Test run-on-side can only be the client or server, not both or neither."); + } + + TestCommand ICommandWrapper.Wrap(TestCommand command) + { + return new SidedTestCommand(command, RunOnSide); + } + + private sealed class SidedTestCommand : DelegatingTestCommand + { + private readonly Side _side; + + public SidedTestCommand(TestCommand inner, Side side) : base(inner) + { + _side = side; + } + + public override TestResult Execute(TestExecutionContext context) + { + innerCommand.Test.EnsureFixtureIsGameTest(typeof(RunOnSideAttribute), out var gt); + + if (_side is not Side.Client and not Side.Server) + throw new NotSupportedException($"Sided tests need to specify a specific side. {Test}"); + + if (_side is Side.Client) + { + gt.Client.WaitAssertion(() => + { + context.CurrentResult = innerCommand.Execute(context); + }) + .Wait(); + } + else + { + gt.Server.WaitAssertion(() => + { + context.CurrentResult = innerCommand.Execute(context); + }) + .Wait(); + } + + return context.CurrentResult; + } + } + + public void ApplyToTest(Test test) + { + test.Properties.Add(RunOnSideProperty, RunOnSide.ToString()); + } +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/Side.cs b/Content.IntegrationTests/Fixtures/Attributes/Side.cs new file mode 100644 index 0000000000..06f1e90acb --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/Side.cs @@ -0,0 +1,27 @@ +#nullable enable +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// A flag enum representing a side of a testpair. +/// +[Flags] +public enum Side : byte +{ + /// + /// Bitflag representing the client side of a testpair. + /// + Client = 1, + /// + /// Bitflag representing the server side of a testpair. + /// + Server = 2, + + /// + /// A value indicating no side was specified. You shouldn't use this outside of checking for it as an error. + /// + Neither = 0, + /// + /// A value indicating both sides were specified. + /// + Both = Client | Server, +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/SidedDependencyAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/SidedDependencyAttribute.cs new file mode 100644 index 0000000000..00fe7a952f --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/SidedDependencyAttribute.cs @@ -0,0 +1,23 @@ +#nullable enable + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Marks a field on a fixture as needing to be populated with an IoC dependency from the given side. +/// +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +public sealed class SidedDependencyAttribute : Attribute +{ + public SidedDependencyAttribute(Side side) + { + Side = side; + + if (side is not Side.Client and not Side.Server) + { + throw new NotSupportedException($"Expected either the client or the server as a side, got {side}."); + } + } + + public Side Side { get; } +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs new file mode 100644 index 0000000000..4acbe8e87b --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs @@ -0,0 +1,53 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// +/// An attribute meant to attach an issue (usually related to the test) to a given test or test fixture. +/// This sets the TrackingIssue property on the test, and helps developers find why a test exists or why it +/// is broken. +/// +/// +/// This attribute should be used if a test corresponds directly to a bug in some way, either demonstrating it or +/// ensuring it remains fixed. Only URLs should be provided, lone issue numbers are not accepted. +/// +/// +/// If the bug was never given an issue, the fix PR containing the test is another acceptable thing to link, and the +/// PR should clearly explain the bug it is fixing for future readers. +/// +/// +public sealed class TrackingIssueAttribute : PropertyAttribute +{ + /// + /// Domains we allow for tracking issues, to avoid people putting discord or discourse links. + /// + private static readonly string[] _validDomains = + [ + "github.com" + ]; + + private static readonly Regex GithubStyleIssueMatch = new(@"^\/[a-z\d\-\$\#]*\/[a-z\d\-\$\#]*\/(issues|pulls)\/\d*$", + RegexOptions.Compiled | RegexOptions.NonBacktracking | RegexOptions.IgnoreCase); + + public TrackingIssueAttribute([StringSyntax(StringSyntaxAttribute.Uri)] string url) : base(url) + { + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + throw new ArgumentException($"Expected a valid URL for {nameof(TrackingIssueAttribute)}, got {url}"); + + // Assert the domain is reasonable. + if (!_validDomains.Contains(uri.Host, StringComparer.InvariantCultureIgnoreCase)) + { + throw new ArgumentException( + $"Didn't recognize the domain used for the tracking issue, got {uri.Host}. We support: {string.Join(", ", _validDomains)}"); + } + + // Assert that the URL is reasonable. + if (!GithubStyleIssueMatch.IsMatch(uri.AbsolutePath)) + { + throw new ArgumentException( + $"Didn't recognize the provided github link, it should point to a specific pull request or issue. Got {uri.AbsolutePath}"); + } + } +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.CVars.cs b/Content.IntegrationTests/Fixtures/GameTest.CVars.cs new file mode 100644 index 0000000000..6ecde6fb6d --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.CVars.cs @@ -0,0 +1,83 @@ +#nullable enable +using System.Collections.Generic; +using Content.IntegrationTests.Fixtures.Attributes; +using Robust.Shared.Configuration; + +namespace Content.IntegrationTests.Fixtures; + +// REMARK: You may be wondering why this doesn't bother storing the old CVars. +// This is because TestPair actually has some not-well-known functionality to +// automatically restore CVars to what they were pre-test for you. +// +// So instead of rolling that twice, this lets TestPair handle it. +public abstract partial class GameTest +{ + [SidedDependency(Side.Server)] private readonly IConfigurationManager _serverCfg = default!; + [SidedDependency(Side.Client)] private readonly IConfigurationManager _clientCfg = default!; + + private readonly Dictionary _clientCVarOverrides = new(); + private readonly Dictionary _serverCVarOverrides = new(); + + /// + /// Adds a setup-time override for a given cvar, for use by s. + /// + public void PreTestAddOverride(Side side, string cVar, object value) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (_setupDone) + throw new NotSupportedException("Cannot use PreTest functions after test SetUp."); + + if (side is Side.Neither) + throw new NotSupportedException($"Must specify a side, or both, for {nameof(PreTestAddOverride)}"); + + if ((side & Side.Server) != 0) + _serverCVarOverrides.Add(cVar, value); + + if ((side & Side.Client) != 0) + _clientCVarOverrides.Add(cVar, value); + } + + private async Task DoPreTestOverrides() + { + foreach (var (cvar, value) in _clientCVarOverrides) + { + await OverrideCVarByName(Side.Client, cvar, value, false); + } + + foreach (var (cvar, value) in _serverCVarOverrides) + { + await OverrideCVarByName(Side.Server, cvar, value, false); + } + + await Pair.RunUntilSynced(); + } + + /// + /// Sets a given CVar for the provided side. + /// + /// Does its own cleanup, you do not need to set the CVar back yourself. + public async Task OverrideCVar(Side side, CVarDef cvar, T value, bool sync = true) + where T: notnull + { + await OverrideCVarByName(side, cvar.Name, value, sync); + } + + private async Task OverrideCVarByName(Side side, string cVar, object value, bool sync) + { + if (side is Side.Client) + { + _clientCfg.SetCVar(cVar, value); + } + else if (side is Side.Server) + { + _serverCfg.SetCVar(cVar, value); + } + else + { + throw new NotSupportedException($"Expected a specific side, got {side}."); + } + + if (sync) + await Pair.RunUntilSynced(); + } +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.CommonPoolSettings.cs b/Content.IntegrationTests/Fixtures/GameTest.CommonPoolSettings.cs new file mode 100644 index 0000000000..c6525192c5 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.CommonPoolSettings.cs @@ -0,0 +1,12 @@ +namespace Content.IntegrationTests.Fixtures; + +public abstract partial class GameTest +{ + /// + /// All-default-settings PoolSettings, with the client and server disconnected. + /// + protected static PoolSettings PsDisconnected => new() + { + Connected = false, + }; +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.Entities.cs b/Content.IntegrationTests/Fixtures/GameTest.Entities.cs new file mode 100644 index 0000000000..2afdeec8ef --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.Entities.cs @@ -0,0 +1,228 @@ +#nullable enable +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; + +namespace Content.IntegrationTests.Fixtures; + +public abstract partial class GameTest +{ + /// + /// Contains all server entities spawned using GameTest proxy methods. + /// + private readonly List _serverEntitiesToClean = new(); + + /// + /// Contains all client entities spawned using GameTest proxy methods. + /// + private readonly List _clientEntitiesToClean = new(); + + private async Task CleanUpEntities() + { + await Task.WhenAll( + Server.WaitAssertion(() => + { + foreach (var junk in _serverEntitiesToClean) + { + if (!SEntMan.Deleted(junk)) + SEntMan.DeleteEntity(junk); + } + }), + Client.WaitAssertion(() => + { + foreach (var junk in _clientEntitiesToClean) + { + if (!CEntMan.Deleted(junk)) + CEntMan.DeleteEntity(junk); + } + }) + ); + } + + /// + /// Returns a string representation of an entity for the server. + /// + public string SToPrettyString(EntityUid uid) + { + return Pair.Server.EntMan.ToPrettyString(uid); + } + + /// + /// Returns a string representation of an entity for the client. + /// + public string CToPrettyString(EntityUid uid) + { + return Pair.Client.EntMan.ToPrettyString(uid); + } + + /// + /// Converts a server EntityUid into the client-side equivalent entity. + /// + public EntityUid ToClientUid(EntityUid serverUid) + { + return Pair.ToClientUid(serverUid); + } + + /// + /// Converts a client EntityUid into the server-side equivalent entity. + /// + public EntityUid ToServerUid(EntityUid clientUid) + { + return Pair.ToServerUid(clientUid); + } + + /// + /// Retrieves the given component from an entity on the server. + /// + public T SComp(EntityUid target) + where T : IComponent + { + return SEntMan.GetComponent(target); + } + + /// + /// Attempts to retrieve the given component from an entity on the server. + /// + public bool STryComp(EntityUid? target, [NotNullWhen(true)] out T? component) + where T : IComponent + { + return SEntMan.TryGetComponent(target, out component); + } + + /// + /// Retrieves the given component from an entity on the client. + /// + public T CComp(EntityUid target) + where T : IComponent + { + return CEntMan.GetComponent(target); + } + + /// + /// Attempts to retrieve the given component from an entity on the server. + /// + public bool CTryComp(EntityUid? target, [NotNullWhen(true)] out T? component) + where T : IComponent + { + return SEntMan.TryGetComponent(target, out component); + } + + /// + /// Pairs an EntityUid with the given component, from the server. + /// + public Entity SEntity(EntityUid target) + where T : IComponent + { + return new(target, SEntMan.GetComponent(target)); + } + + /// + /// Pairs an EntityUid with the given component, from the client. + /// + public Entity CEntity(EntityUid target) + where T : IComponent + { + return new(target, CEntMan.GetComponent(target)); + } + + /// + /// Spawns an entity on the server. + /// + /// This tracks the entity for post-test cleanup. + public EntityUid SSpawn(string? id) + { + var res = SEntMan.Spawn(id); + _serverEntitiesToClean.Add(res); + return res; + } + + /// + /// Spawns an entity on the server at a location. + /// + /// This tracks the entity for post-test cleanup. + public EntityUid SSpawnAtPosition(string? id, EntityCoordinates coordinates) + { + var res = SEntMan.SpawnAtPosition(id, coordinates); + _serverEntitiesToClean.Add(res); + return res; + } + + /// + /// Spawns an entity on the client. + /// + /// This tracks the entity for post-test cleanup. + public EntityUid CSpawn(string? id) + { + var res = CEntMan.Spawn(id); + _clientEntitiesToClean.Add(res); + return res; + } + + /// + /// Spawns an entity on the server at a location. + /// + /// This tracks the entity for post-test cleanup. + public EntityUid CSpawnAtPosition(string? id, EntityCoordinates coordinates) + { + var res = CEntMan.SpawnAtPosition(id, coordinates); + _clientEntitiesToClean.Add(res); + return res; + } + + /// + /// Asynchronously spawns an entity on the server. + /// + public async Task Spawn(string? id) + { + var ent = EntityUid.Invalid; + + await Server.WaitPost(() => ent = SSpawn(id)); + + return ent; + } + + /// + /// Asynchronously spawns an entity on the server at the given position. + /// + public async Task SpawnAtPosition(string? id, EntityCoordinates coords) + { + var ent = EntityUid.Invalid; + + await Server.WaitPost(() => ent = SSpawnAtPosition(id, coords)); + + return ent; + } + + /// + /// Deletes an entity on the server immediately. + /// + public void SDeleteNow(EntityUid id) + { + SEntMan.DeleteEntity(id); + } + + /// + /// Deletes an entity on the client immediately. + /// + public void CDeleteNow(EntityUid id) + { + CEntMan.DeleteEntity(id); + } + + /// + /// Queues an entity for deletion at the end of the tick on the server. + /// + public void SQueueDel(EntityUid id) + { + SEntMan.QueueDeleteEntity(id); + } + + /// + /// Queues an entity for deletion at the end of the tick on the client. + /// + public void CQueueDel(EntityUid id) + { + CEntMan.QueueDeleteEntity(id); + } +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.Pair.cs b/Content.IntegrationTests/Fixtures/GameTest.Pair.cs new file mode 100644 index 0000000000..8d3a88bb7c --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.Pair.cs @@ -0,0 +1,36 @@ +namespace Content.IntegrationTests.Fixtures; + +public abstract partial class GameTest +{ + /// + /// Runs the client and server for the given number of ticks, in lockstep. + /// + /// + /// Do not use this as a barrier for client-server synchronization, use . + /// + public Task RunTicksSync(int ticks) + { + return Pair.RunTicksSync(ticks); + } + + /// + /// Runs the pairs just long enough for PVS to send entities, ensuring the client's current tick is what the + /// server's was at call time. + /// + public async Task RunUntilSynced() + { + await Pair.RunUntilSynced(); + } + + /// + /// Runs the test pair for a number of (simulated) seconds. + /// + /// + /// Does not actually take N seconds to evaluate, the game ticks as fast as possible. + /// Do not use this as a barrier for client-server synchronization, use . + /// + public Task RunSeconds(float seconds) + { + return Pair.RunSeconds(seconds); + } +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.cs b/Content.IntegrationTests/Fixtures/GameTest.cs new file mode 100644 index 0000000000..55903cf4a2 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.cs @@ -0,0 +1,265 @@ +#nullable enable +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Threading; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.IntegrationTests.NUnit.Constraints; +using Content.IntegrationTests.Pair; +using Content.IntegrationTests.Utility; +using NUnit.Framework.Interfaces; +using Robust.Client.Timing; +using Robust.Shared.GameObjects; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Robust.Shared.Utility; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.Fixtures; + +/// +/// +/// A test fixture with an integrated test pair, +/// proxy methods for efficient test writing, utilities for ensuring tests clean up correctly, +/// and dependency injection (). +/// +/// +/// Tests using GameTest support some additional class and method level attributes, namely +/// . +/// Attributes can be used to control how the test runs. +/// +/// +/// +/// +[TestFixture] +[FixtureLifeCycle(LifeCycle.InstancePerTestCase)] +[Property(TestProperties.TestFrameKind, nameof(GameTest))] +[SuppressMessage("Structure", "NUnit1028:The non-test method is public")] +public abstract partial class GameTest +{ + /// + /// Set if the test manually marks itself dirty. + /// + private bool _pairDestroyed; + + /// + /// Tests-testing-tests assistant to run right before the pair is returned. + /// + public event Action? PreFinalizeHook; + + /// + /// The main thread of the game server. + /// + public Thread ServerThread { get; private set; } = null!; // NULLABILITY: This is always set during test setup. + /// + /// The main thread of the game client. + /// + public Thread ClientThread { get; private set; } = null!; // NULLABILITY: This is always set during test setup. + + /// + /// Settings for the client/server pair. + /// By default, this gets you a client and server that have connected together. + /// + /// + /// Always return a new instance whenever this is read. In other words, no backing field please. Arrow syntax only. + /// + public virtual PoolSettings PoolSettings => new() { Connected = true }; + + /// + /// The client and server pair. + /// + public TestPair Pair { get; private set; } = default!; // NULLABILITY: This is always set during test setup. + + /// + /// The game server instance. + /// + public RobustIntegrationTest.ServerIntegrationInstance Server => Pair.Server; + + /// + /// The game client instance. + /// + public RobustIntegrationTest.ClientIntegrationInstance Client => Pair.Client; + + /// + /// The test player's server session, if any. + /// + public ICommonSession? ServerSession => Pair.Player; + + /// + /// The server-side entity manager. + /// + [SidedDependency(Side.Server)] + public IEntityManager SEntMan = null!; + + /// + /// The client-side entity manager. + /// + [SidedDependency(Side.Client)] + public IEntityManager CEntMan = null!; + + /// + /// The server-side prototype manager. + /// + [SidedDependency(Side.Server)] + public IPrototypeManager SProtoMan = null!; + + /// + /// The client-side prototype manager. + /// + [SidedDependency(Side.Client)] + public IPrototypeManager CProtoMan = null!; + + /// + /// The server-side game-timing manager. + /// + [SidedDependency(Side.Server)] + public IGameTiming SGameTiming = null!; + + /// + /// The client-side game-timing manager. + /// + [SidedDependency(Side.Client)] + public IClientGameTiming CGameTiming = null!; + + /// + /// The test map we're using, if any. + /// + public TestMapData? TestMap => Pair.TestMap; + + private bool _setupDone = false; + + /// + /// Primary setup task for the fixture. + /// Custom setup must run after this. + /// + [SetUp] + public virtual async Task DoSetup() + { + _pairDestroyed = false; + var testContext = TestContext.CurrentContext; + + + var test = testContext.Test; + + var settings = PoolSettings; + + var pairAttribs = test.Method!.GetCustomAttributes(false); + var pairSuiteAttribs = test.Method!.TypeInfo.GetCustomAttributes(true); + + if (pairAttribs.Length > 1 && pairAttribs.Any(x => x.Exclusive)) + { + throw new InvalidOperationException( + "More than one exclusive pair config attribute is present on the test member."); + } + + if (pairSuiteAttribs.Length > 1 && pairSuiteAttribs.Any(x => x.Exclusive)) + { + throw new InvalidOperationException( + "More than one exclusive pair config attribute is present on the test fixture."); + } + + foreach (var attribute in pairSuiteAttribs.Concat(pairAttribs)) + { + attribute.ApplyToPairSettings(this, ref settings); + } + + Pair = await PoolManager.GetServerClient(settings, new NUnitTestContextWrap(testContext, TestContext.Out)); + + Task.WaitAll( + Server.WaitPost(() => ServerThread = Thread.CurrentThread), + Client.WaitPost(() => ClientThread = Thread.CurrentThread) + ); + + await Pair.ReallyBeIdle(5); // Arbitrary setup time wait. + + InjectDependencies(this); + + var attribs = test.Method!.GetCustomAttributes(false); + var suiteAttribs = test.Method!.TypeInfo.GetCustomAttributes(true); + + foreach (var attribute in suiteAttribs.Concat(attribs)) + { + await attribute.ApplyToTest(this); + } + + _setupDone = true; + + await DoPreTestOverrides(); + + await Pair.RunUntilSynced(); + } + + /// + /// Injects dependencies into the target object. + /// + /// + /// This is called on the GameTest itself automatically. Don't call it twice on the same object. + /// + /// The object to inject into. + public void InjectDependencies(object target) + { + foreach (var field in target.GetType().GetAllFields()) + { + if (field.GetCustomAttribute() is { } depAttrib) + { + // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression + if (depAttrib.Side is Side.Server) + { + field.SetValue(target, Server.EntMan.EntitySysManager.DependencyCollection.ResolveType(field.FieldType)); + } + else + { + // Must be initially connected for this... + if (Client.Session is not null) + field.SetValue(target, Client.EntMan.EntitySysManager.DependencyCollection.ResolveType(field.FieldType)); + else + field.SetValue(target, Client.InstanceDependencyCollection.ResolveType(field.FieldType)); + } + } + } + } + + /// + /// Primary teardown task for the fixture. + /// Custom teardown must run before this. + /// + [TearDown] + public virtual async Task DoTeardown() + { + try + { + // In some cool future we might be able to make this only throw out the pair + // if the test threw exceptions. But that'd require fixing all of them to do cleanup properly on failure. + // + // So not yet. + if (TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed) + { + _pairDestroyed = true; // Blow it up, we failed and it might be screwed. + return; + } + + // Roll forward til sync for teardown. + await Pair.RunUntilSynced(); + + await CleanUpEntities(); + + // And other teardown logic will go here. Eventually. + + } + catch (Exception) + { + _pairDestroyed = true; + throw; + } + finally + { + PreFinalizeHook?.Invoke(); + + if (!_pairDestroyed) + await Pair.CleanReturnAsync(); + else + await Pair.DisposeAsync(); + } + } +} diff --git a/Content.IntegrationTests/NUnit/Constraints/CompConstraint.cs b/Content.IntegrationTests/NUnit/Constraints/CompConstraint.cs new file mode 100644 index 0000000000..c474d0c2d5 --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/CompConstraint.cs @@ -0,0 +1,33 @@ +using NUnit.Framework.Constraints; +using NUnit.Framework.Internal; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +/// +/// A prefix constraint like , for entity components. +/// +/// +public sealed class CompConstraint(Type tComp, IIntegrationInstance instance, IConstraint baseConstraint) + : PrefixConstraint(baseConstraint, $"component {tComp.Name}") +{ + public override ConstraintResult ApplyTo(TActual actual) + { + if (!ConstraintHelpers.TryActualAsEnt(actual, instance, out var ent, out var error)) + { + if (error) + { + throw new NotImplementedException( + $"The input type {typeof(TActual)} to {nameof(CompExistsConstraint)} is not a supported entity id."); + } + + return new ConstraintResult(this, actual, ConstraintStatus.Failure); + } + + if (!instance.EntMan.TryGetComponent(ent, tComp, out var comp)) + return new ConstraintResult(this, actual, ConstraintStatus.Failure); + + var baseResult = Reflect.InvokeApplyTo(constraint: baseConstraint, tComp, comp); + return new ConstraintResult(this, baseResult.ActualValue, baseResult.Status); + } +} diff --git a/Content.IntegrationTests/NUnit/Constraints/CompConstraintExtensions.cs b/Content.IntegrationTests/NUnit/Constraints/CompConstraintExtensions.cs new file mode 100644 index 0000000000..1586c52e03 --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/CompConstraintExtensions.cs @@ -0,0 +1,48 @@ +#nullable enable +using Content.IntegrationTests.NUnit.Operators; +using NUnit.Framework.Constraints; +using Robust.Shared.GameObjects; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +/// +/// Provides Has.Comp<T>(side), +/// a constraint that allows you to check for the presence of, or operate on, a component. +/// +/// +/// +/// // Assert that the server sided entity myEntity has ItemComponent on the server. +/// Assert.That(myEntity, Has.Comp<ItemComponent>(Server)); +/// +/// +public static class CompConstraintExtensions +{ + extension(Has) + { + public static ResolvableConstraintExpression Comp(IIntegrationInstance instance) + where T : IComponent + { + return new ConstraintExpression().Comp(instance); + } + + public static ResolvableConstraintExpression Comp(Type t, IIntegrationInstance instance) + { + return new ConstraintExpression().Comp(t, instance); + } + } + + extension(ConstraintExpression expr) + { + public ResolvableConstraintExpression Comp(IIntegrationInstance instance) + where T : IComponent + { + return expr.Append(new CompOperator(typeof(T), instance)); + } + + public ResolvableConstraintExpression Comp(Type t, IIntegrationInstance instance) + { + return expr.Append(new CompOperator(t, instance)); + } + } +} diff --git a/Content.IntegrationTests/NUnit/Constraints/CompExistsConstraint.cs b/Content.IntegrationTests/NUnit/Constraints/CompExistsConstraint.cs new file mode 100644 index 0000000000..eaff1eae8a --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/CompExistsConstraint.cs @@ -0,0 +1,30 @@ +#nullable enable +using NUnit.Framework.Constraints; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +/// +/// Constraint for whether a component exists. +/// +/// +public sealed class CompExistsConstraint(Type component, IIntegrationInstance instance) : Constraint +{ + public override ConstraintResult ApplyTo(TActual actual) + { + if (!ConstraintHelpers.TryActualAsEnt(actual, instance, out var ent, out var error)) + { + if (error) + { + throw new NotImplementedException( + $"The input type {typeof(TActual)} to {nameof(CompExistsConstraint)} is not a supported entity id."); + } + + return new ConstraintResult(this, actual, ConstraintStatus.Failure); + } + + return new ConstraintResult(this, actual, instance.EntMan.HasComponent(ent, component)); + } + + public override string Description => $"has the component {component.Name}"; +} diff --git a/Content.IntegrationTests/NUnit/Constraints/ConstraintHelpers.cs b/Content.IntegrationTests/NUnit/Constraints/ConstraintHelpers.cs new file mode 100644 index 0000000000..f7f810eda4 --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/ConstraintHelpers.cs @@ -0,0 +1,66 @@ +#nullable enable +using System.Diagnostics.CodeAnalysis; +using Content.IntegrationTests.NUnit.Utilities; +using Robust.Shared.GameObjects; +using Robust.Shared.Toolshed.TypeParsers; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +public static class ConstraintHelpers +{ + /// + /// A constraint implementation helper to convert TActual into an entityuid. + /// + /// The input value to try to get an entity uid from. + /// The integration test instance to resolve the entity from. + /// The resulting entity uid. + /// Whether TActual is recognized to begin with. + /// The type to cast out of. + public static bool TryActualAsEnt(TActual t, IIntegrationInstance instance, [NotNullWhen(true)] out EntityUid? ent, out bool validType) + { + if (t is EntityUid u) + { + ent = u; + validType = false; + return true; + } + + if (t is IAsType asTy) + { + ent = asTy.AsType(); + validType = false; + return true; + } + + if (t is IResolvesToEntity resolvable) + { + if (instance is IServerIntegrationInstance) + { + ent = resolvable.SEntity; + } + else if (instance is IClientIntegrationInstance) + { + ent = resolvable.CEntity; + } + else + { + throw new NotSupportedException($"{t.GetType()} is not a valid kind of IIntegrationInstance"); + } + + validType = false; + return ent is not null; + } + + if (t is null) + { + ent = null; + validType = false; + return false; + } + + ent = null; + validType = true; // Dunno what this type is! + return false; + } +} diff --git a/Content.IntegrationTests/NUnit/Constraints/LifeStageConstraint.cs b/Content.IntegrationTests/NUnit/Constraints/LifeStageConstraint.cs new file mode 100644 index 0000000000..0539a6b54e --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/LifeStageConstraint.cs @@ -0,0 +1,136 @@ +#nullable enable +using NUnit.Framework.Constraints; +using Robust.Shared.GameObjects; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +/// +/// A constraint for an entity's lifestage. +/// +/// +public sealed class LifeStageConstraint(EntityLifeStage stage, IIntegrationInstance instance) : Constraint +{ + public override ConstraintResult ApplyTo(TActual actual) + { + if (!ConstraintHelpers.TryActualAsEnt(actual, instance, out var ent, out var error)) + { + if (error) + { + throw new NotImplementedException( + $"The input type {typeof(TActual)} to {nameof(CompExistsConstraint)} is not a supported entity id."); + } + + return new ConstraintResult(this, actual, ConstraintStatus.Failure); + } + + var lifestage = instance.EntMan.GetComponentOrNull(ent.Value)?.EntityLifeStage; + + return new ConstraintResult(this, + lifestage, + lifestage == stage || (lifestage is null && stage is EntityLifeStage.Deleted)); + } + + public override string Description => stage switch + { + EntityLifeStage.PreInit => "preinitialized", + EntityLifeStage.Initializing => "initializing", + EntityLifeStage.Initialized => "initialized", + EntityLifeStage.MapInitialized => "map initialized", + EntityLifeStage.Terminating => "terminating", + EntityLifeStage.Deleted => "deleted", + _ => throw new ArgumentOutOfRangeException(nameof(stage), stage, null), + }; +} + +/// +/// Provides constraints for testing if an entity is in the given lifestage. +/// +/// +/// +/// // Assert that the server sided entity myEntity is MapInitialized. +/// Assert.That(myEntity, Is.MapInitialized(Server)); +/// +/// +public static class LifeStageConstraintExtensions +{ + extension(Is) + { + public static LifeStageConstraint LifeStage(EntityLifeStage stage, IIntegrationInstance instance) + { + return new LifeStageConstraint(stage, instance); + } + + public static LifeStageConstraint PreInit(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.PreInit, instance); + } + + public static LifeStageConstraint Initializing(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.Initializing, instance); + } + + public static LifeStageConstraint Initialized(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.Initialized, instance); + } + + public static LifeStageConstraint MapInitialized(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.MapInitialized, instance); + } + + public static LifeStageConstraint Terminating(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.Terminating, instance); + } + + public static LifeStageConstraint Deleted(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.Deleted, instance); + } + } + + extension(ConstraintExpression expr) + { + public LifeStageConstraint LifeStage(EntityLifeStage stage, IIntegrationInstance instance) + { + var c = new LifeStageConstraint(stage, instance); + + expr.Append(c); + + return c; + } + + public LifeStageConstraint PreInit(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.PreInit, instance); + } + + public LifeStageConstraint Initializing(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.Initializing, instance); + } + + public LifeStageConstraint Initialized(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.Initialized, instance); + } + + public LifeStageConstraint MapInitialized(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.MapInitialized, instance); + } + + public LifeStageConstraint Terminating(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.Terminating, instance); + } + + public LifeStageConstraint Deleted(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.Deleted, instance); + } + } +} diff --git a/Content.IntegrationTests/NUnit/Operators/CompOperator.cs b/Content.IntegrationTests/NUnit/Operators/CompOperator.cs new file mode 100644 index 0000000000..c52bcac37e --- /dev/null +++ b/Content.IntegrationTests/NUnit/Operators/CompOperator.cs @@ -0,0 +1,31 @@ +using Content.IntegrationTests.NUnit.Constraints; +using NUnit.Framework.Constraints; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Operators; + +/// +/// An operator for use by nunit constraint resolution. +/// +/// +public sealed class CompOperator : SelfResolvingOperator +{ + private readonly Type _tComp; + private readonly IIntegrationInstance _instance; + + public CompOperator(Type tComp, IIntegrationInstance instance) + { + _tComp = tComp; + _instance = instance; + + left_precedence = right_precedence = 1; + } + + public override void Reduce(ConstraintBuilder.ConstraintStack stack) + { + if (RightContext is null or BinaryOperator) + stack.Push(new CompExistsConstraint(_tComp, _instance)); + else + stack.Push(new CompConstraint(_tComp, _instance, stack.Pop())); + } +} diff --git a/Content.IntegrationTests/NUnit/Utilities/IResolvesToEntity.cs b/Content.IntegrationTests/NUnit/Utilities/IResolvesToEntity.cs new file mode 100644 index 0000000000..bf039c597c --- /dev/null +++ b/Content.IntegrationTests/NUnit/Utilities/IResolvesToEntity.cs @@ -0,0 +1,19 @@ +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.NUnit.Utilities; + +/// +/// An interface for objects that NUnit constraints should treat as a sided entity. +/// +public interface IResolvesToEntity +{ + /// + /// The server-sided entity, if any. + /// + EntityUid? SEntity { get; } + /// + /// The client-sided entity, if any. + /// + EntityUid? CEntity { get; } +} + diff --git a/Content.IntegrationTests/NUnit/Utilities/ITestExtensions.cs b/Content.IntegrationTests/NUnit/Utilities/ITestExtensions.cs new file mode 100644 index 0000000000..878748949f --- /dev/null +++ b/Content.IntegrationTests/NUnit/Utilities/ITestExtensions.cs @@ -0,0 +1,28 @@ +using Content.IntegrationTests.Fixtures; +using NUnit.Framework.Interfaces; + +namespace Content.IntegrationTests.NUnit.Utilities; + +public static class ITestExtensions +{ + extension(T test) + where T : ITest + { + /// + /// Ensures the given fixture is a , and if not gives a nice error message. + /// + /// The caller's type, usually an attribute. + /// The . + /// Thrown when the given test isn't a + public void EnsureFixtureIsGameTest(Type callingType, out GameTest gt) + { + if (test.Fixture is not GameTest gameTest) + { + throw new NotSupportedException( + $"The fixture {test.Fixture?.GetType()} needs to be a GameTest for {callingType.Name} to work."); + } + + gt = gameTest; + } + } +} diff --git a/Content.IntegrationTests/PoolManager.Cvars.cs b/Content.IntegrationTests/PoolManager.Cvars.cs index b457d4a40b..189e37fa39 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -25,7 +25,6 @@ public static partial class PoolManager (CCVars.ArrivalsShuttles.Name, "false"), (CCVars.EmergencyShuttleEnabled.Name, "false"), (CCVars.ProcgenPreload.Name, "false"), - (CCVars.WorldgenEnabled.Name, "false"), (CCVars.GatewayGeneratorEnabled.Name, "false"), (CCVars.GameDummyTicker.Name, "true"), (CCVars.GameLobbyEnabled.Name, "false"), diff --git a/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs b/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs index a0c8c775b1..0279673910 100644 --- a/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs +++ b/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs @@ -1,16 +1,17 @@ +#nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Content.Shared.Access; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Robust.Shared.GameObjects; -using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Access { - [TestFixture] [TestOf(typeof(AccessReaderComponent))] - public sealed class AccessReaderTest + public sealed class AccessReaderTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -21,91 +22,85 @@ namespace Content.IntegrationTests.Tests.Access - type: AccessReader "; + [SidedDependency(Side.Server)] private readonly AccessReaderSystem _system = null!; + [Test] + [RunOnSide(Side.Server)] public async Task TestTags() { - await using var pair = await PoolManager.GetServerClient(); - var server = pair.Server; - var entityManager = server.ResolveDependency(); + var ent = SSpawn("TestAccessReader"); + var reader = new Entity(ent, SComp(ent)); - await server.WaitAssertion(() => + // test empty + Assert.Multiple(() => { - var system = entityManager.System(); - var ent = entityManager.SpawnEntity("TestAccessReader", MapCoordinates.Nullspace); - var reader = new Entity(ent, entityManager.GetComponent(ent)); - - // test empty - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "Bar" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True); - }); - - // test deny - system.AddDenyTag(reader, "A"); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "Foo" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True); - }); - system.ClearDenyTags(reader); - - // test one list - system.TryAddAccess(reader, "A"); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); - }); - system.TryClearAccesses(reader); - - // test one list - two items - system.TryAddAccess(reader, new HashSet> { "A", "B" }); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); - }); - system.TryClearAccesses(reader); - - // test two list - var accesses = new List>>() { - new HashSet> () { "A" }, - new HashSet> () { "B", "C" } - }; - system.TryAddAccesses(reader, accesses); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "C", "B" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "C", "B", "A" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); - }); - system.TryClearAccesses(reader); - - // test deny list - system.TryAddAccess(reader, new HashSet> { "A" }); - system.AddDenyTag(reader, "B"); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); - }); - system.TryClearAccesses(reader); - system.ClearDenyTags(reader); + Assert.That(_system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "Bar" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True); }); - await pair.CleanReturnAsync(); + + // test deny + _system.AddDenyTag(reader, "A"); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "Foo" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True); + }); + _system.ClearDenyTags(reader); + + // test one list + _system.TryAddAccess(reader, "A"); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); + }); + _system.TryClearAccesses(reader); + + // test one list - two items + _system.TryAddAccess(reader, new HashSet> { "A", "B" }); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); + }); + _system.TryClearAccesses(reader); + + // test two list + var accesses = new List>>() { + new HashSet> () { "A" }, + new HashSet> () { "B", "C" } + }; + _system.TryAddAccesses(reader, accesses); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "C", "B" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "C", "B", "A" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); + }); + _system.TryClearAccesses(reader); + + // test deny list + _system.TryAddAccess(reader, new HashSet> { "A" }); + _system.AddDenyTag(reader, "B"); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); + }); + _system.TryClearAccesses(reader); + _system.ClearDenyTags(reader); } } diff --git a/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs b/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs index 45addff00b..9680e48939 100644 --- a/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs +++ b/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs @@ -1,4 +1,7 @@ +#nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Content.Shared.Actions; using Content.Shared.Eye; using Robust.Server.GameObjects; @@ -7,15 +10,18 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Actions; [TestFixture] -public sealed class ActionPvsDetachTest +public sealed class ActionPvsDetachTest : GameTest { + [SidedDependency(Side.Server)] private readonly SharedActionsSystem _sActionsSys = null!; + [SidedDependency(Side.Client)] private readonly SharedActionsSystem _cActionsSys = null!; + [Test] public async Task TestActionDetach() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); - var (server, client) = pair; - var sys = server.System(); - var cSys = client.System(); + var pair = Pair; + var (server, client) = (Server, Client); + var sys = _sActionsSys; + var cSys = _cActionsSys; // Spawn mob that has some actions EntityUid ent = default; @@ -60,6 +66,5 @@ public sealed class ActionPvsDetachTest Assert.That(cSys.GetActions(cEnt).Count(), Is.EqualTo(initActions)); await server.WaitPost(() => server.EntMan.DeleteEntity(map.MapUid)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs index 0dbec0c83a..1fa005496e 100644 --- a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs +++ b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs @@ -1,4 +1,6 @@ +#nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Actions; using Content.Shared.Actions.Components; using Content.Shared.CombatMode; @@ -11,15 +13,17 @@ namespace Content.IntegrationTests.Tests.Actions; /// This tests checks that actions properly get added to an entity's actions component.. /// [TestFixture] -public sealed class ActionsAddedTest +public sealed class ActionsAddedTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings { Connected = true, DummyTicker = false }; + // TODO add magboot test (inventory action) // TODO add ghost toggle-fov test (client-side action) [Test] public async Task TestCombatActionsAdded() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false }); + var pair = Pair; var server = pair.Server; var client = pair.Client; var sEntMan = server.ResolveDependency(); @@ -67,7 +71,5 @@ public sealed class ActionsAddedTest // required, because integration tests do not respect the [NonSerialized] attribute and will simply events by reference. Assert.That(ReferenceEquals(sAct.Comp, cAct.Comp), Is.False); Assert.That(ReferenceEquals(sQuery.GetComponent(sAct).Event, cQuery.GetComponent(cAct).Event), Is.False); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs index 772af337a1..71cb0fd7ed 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Administration.Logs; using Content.Server.Database; using Content.Server.GameTicking; @@ -12,9 +14,9 @@ namespace Content.IntegrationTests.Tests.Administration.Logs; [TestFixture] [TestOf(typeof(AdminLogSystem))] -public sealed class AddTests +public sealed class AddTests : GameTest { - public static PoolSettings LogTestSettings = new() + public override PoolSettings PoolSettings => new() { AdminLogsEnabled = true, DummyTicker = false, @@ -24,7 +26,7 @@ public sealed class AddTests [Test] public async Task AddAndGetSingleLog() { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -33,7 +35,7 @@ public sealed class AddTests var guid = Guid.NewGuid(); await pair.CreateTestMap(); - var coordinates = pair.TestMap.GridCoords; + var coordinates = pair.TestMap!.GridCoords; await server.WaitPost(() => { var entity = sEntities.SpawnEntity(null, coordinates); @@ -62,14 +64,12 @@ public sealed class AddTests return false; }); - - await pair.CleanReturnAsync(); } [Test] public async Task AddAndGetUnformattedLog() { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sDatabase = server.ResolveDependency(); @@ -127,15 +127,13 @@ public sealed class AddTests json.Dispose(); } - - await pair.CleanReturnAsync(); } [Test] [TestCase(500)] public async Task BulkAddLogs(int amount) { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -158,14 +156,12 @@ public sealed class AddTests var messages = await sAdminLogSystem.CurrentRoundLogs(); return messages.Count >= amount; }); - - await pair.CleanReturnAsync(); } [Test] public async Task AddPlayerSessionLog() { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sPlayers = server.ResolveDependency(); @@ -195,20 +191,91 @@ public sealed class AddTests Assert.That(logs.First().Players, Does.Contain(playerGuid)); return true; }); - await pair.CleanReturnAsync(); } + [Test] + public async Task DuplicatePlayerDoesNotThrowTest() + { + var pair = Pair; + var server = pair.Server; + + var sPlayers = server.ResolveDependency(); + var sAdminLogSystem = server.ResolveDependency(); + + var guid = Guid.NewGuid(); + + await server.WaitPost(() => + { + var player = sPlayers.Sessions.Single(); + + sAdminLogSystem.Add(LogType.Unknown, $"{player} {player} test log: {guid}"); + }); + + await PoolManager.WaitUntil(server, async () => + { + var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter + { + Search = guid.ToString() + }); + + if (logs.Count == 0) + { + return false; + } + + return true; + }); + } + + [Test] + public async Task DuplicatePlayerIdDoesNotThrowTest() + { + var pair = Pair; + var server = pair.Server; + + var sPlayers = server.ResolveDependency(); + + var sAdminLogSystem = server.ResolveDependency(); + + var guid = Guid.NewGuid(); + + await server.WaitPost(() => + { + var player = sPlayers.Sessions.Single(); + + sAdminLogSystem.Add(LogType.Unknown, $"{player:first} {player:second} test log: {guid}"); + }); + + await PoolManager.WaitUntil(server, async () => + { + var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter + { + Search = guid.ToString() + }); + + if (logs.Count == 0) + { + return false; + } + + return true; + }); + } +} + +public sealed class PreRoundAddTests : GameTest +{ + public override PoolSettings PoolSettings => new PoolSettings + { + Dirty = true, + InLobby = true, + AdminLogsEnabled = true + }; + [Test] public async Task PreRoundAddAndGetSingle() { - var setting = new PoolSettings - { - Dirty = true, - InLobby = true, - AdminLogsEnabled = true - }; - - await using var pair = await PoolManager.GetServerClient(setting); + var pair = Pair; var server = pair.Server; var sDatabase = server.ResolveDependency(); @@ -262,81 +329,6 @@ public sealed class AddTests json.Dispose(); } - await pair.CleanReturnAsync(); } - [Test] - public async Task DuplicatePlayerDoesNotThrowTest() - { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); - var server = pair.Server; - - var sPlayers = server.ResolveDependency(); - var sAdminLogSystem = server.ResolveDependency(); - - var guid = Guid.NewGuid(); - - await server.WaitPost(() => - { - var player = sPlayers.Sessions.Single(); - - sAdminLogSystem.Add(LogType.Unknown, $"{player} {player} test log: {guid}"); - }); - - await PoolManager.WaitUntil(server, async () => - { - var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter - { - Search = guid.ToString() - }); - - if (logs.Count == 0) - { - return false; - } - - return true; - }); - - await pair.CleanReturnAsync(); - Assert.Pass(); - } - - [Test] - public async Task DuplicatePlayerIdDoesNotThrowTest() - { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); - var server = pair.Server; - - var sPlayers = server.ResolveDependency(); - - var sAdminLogSystem = server.ResolveDependency(); - - var guid = Guid.NewGuid(); - - await server.WaitPost(() => - { - var player = sPlayers.Sessions.Single(); - - sAdminLogSystem.Add(LogType.Unknown, $"{player:first} {player:second} test log: {guid}"); - }); - - await PoolManager.WaitUntil(server, async () => - { - var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter - { - Search = guid.ToString() - }); - - if (logs.Count == 0) - { - return false; - } - - return true; - }); - - await pair.CleanReturnAsync(); - Assert.Pass(); - } } diff --git a/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs index 6f907f425e..2517defe96 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Administration.Logs; using Content.Shared.Administration.Logs; using Content.Shared.Database; @@ -7,14 +8,21 @@ namespace Content.IntegrationTests.Tests.Administration.Logs; [TestFixture] [TestOf(typeof(AdminLogSystem))] -public sealed class FilterTests +public sealed class FilterTests : GameTest { + public override PoolSettings PoolSettings => new() + { + AdminLogsEnabled = true, + DummyTicker = false, + Connected = true + }; + [Test] [TestCase(DateOrder.Ascending)] [TestCase(DateOrder.Descending)] public async Task Date(DateOrder order) { - await using var pair = await PoolManager.GetServerClient(AddTests.LogTestSettings); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -96,6 +104,5 @@ public sealed class FilterTests return firstFound && secondFound; }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Administration/Logs/QueryTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/QueryTests.cs index 5a58757d53..55b36ebae1 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/QueryTests.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/QueryTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Administration.Logs; using Content.Server.GameTicking; using Content.Shared.Database; @@ -11,12 +12,19 @@ namespace Content.IntegrationTests.Tests.Administration.Logs; [TestFixture] [TestOf(typeof(AdminLogSystem))] -public sealed class QueryTests +public sealed class QueryTests : GameTest { + public override PoolSettings PoolSettings => new() + { + AdminLogsEnabled = true, + DummyTicker = false, + Connected = true + }; + [Test] public async Task QuerySingleLog() { - await using var pair = await PoolManager.GetServerClient(AddTests.LogTestSettings); + var pair = Pair; var server = pair.Server; var sSystems = server.ResolveDependency(); @@ -55,7 +63,5 @@ public sealed class QueryTests return false; }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs index b74c35ba11..2eaa073819 100644 --- a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Atmos.Monitor; using Robust.Shared.Prototypes; @@ -5,7 +6,7 @@ namespace Content.IntegrationTests.Tests.Atmos { [TestFixture] [TestOf(typeof(AtmosAlarmThreshold))] - public sealed class AlarmThresholdTest + public sealed class AlarmThresholdTest : GameTest { private const string AlarmThresholdTestDummyId = "AlarmThresholdTestDummy"; @@ -26,7 +27,7 @@ namespace Content.IntegrationTests.Tests.Atmos [Test] public async Task TestAlarmThreshold() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -136,7 +137,6 @@ namespace Content.IntegrationTests.Tests.Atmos Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Normal)); } }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs b/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs index 6481e377c9..d44bfe7ae4 100644 --- a/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Atmos.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Atmos.Prototypes; @@ -6,12 +7,12 @@ using Content.Shared.Atmos.Prototypes; namespace Content.IntegrationTests.Tests.Atmos; [TestOf(typeof(Atmospherics))] -public sealed class ConstantsTest +public sealed class ConstantsTest : GameTest { [Test] public async Task TotalGasesTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.EntMan; var protoManager = server.ProtoMan; @@ -34,9 +35,6 @@ public sealed class ConstantsTest // enum mapping gases to their Id Assert.That(Enum.GetValues(), Has.Length.EqualTo(Atmospherics.TotalNumberOfGases), $"Gas enum size is not equal to TotalNumberOfGases."); - // localized abbreviations for UI purposes - Assert.That(Atmospherics.GasAbbreviations, Has.Count.EqualTo(Atmospherics.TotalNumberOfGases), - $"GasAbbreviations size is not equal to TotalNumberOfGases."); // the ID for each gas has to correspond to a value in the Gas enum (converted to a string) foreach (var gas in gasProtos) @@ -45,7 +43,6 @@ public sealed class ConstantsTest } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs b/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs index 07caf447bd..eda9061281 100644 --- a/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Robust.Shared.GameObjects; @@ -8,7 +9,7 @@ namespace Content.IntegrationTests.Tests.Atmos; [TestFixture] [TestOf(typeof(Atmospherics))] -public sealed class GasArrayTest +public sealed class GasArrayTest : GameTest { private const string GasTankTestDummyId = "GasTankTestDummy"; @@ -42,7 +43,7 @@ public sealed class GasArrayTest [Test] public async Task TestGasArrayDeserialization() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var compFactory = server.ResolveDependency(); @@ -80,6 +81,5 @@ public sealed class GasArrayTest } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs b/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs index 1cb8fd8b6f..70bbd0ac0c 100644 --- a/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs @@ -1,4 +1,5 @@ -using Content.Server.Atmos; +using Content.IntegrationTests.Fixtures; +using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; using Content.Shared.Atmos; using Robust.Shared.GameObjects; @@ -7,12 +8,12 @@ namespace Content.IntegrationTests.Tests.Atmos { [TestFixture] [TestOf(typeof(GasMixture))] - public sealed class GasMixtureTest + public sealed class GasMixtureTest : GameTest { [Test] public async Task TestMerge() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var atmosphereSystem = server.ResolveDependency().GetEntitySystem(); @@ -56,8 +57,6 @@ namespace Content.IntegrationTests.Tests.Atmos Assert.That(a.GetMoles(Gas.Oxygen), Is.EqualTo(50)); }); }); - - await pair.CleanReturnAsync(); } [Test] @@ -69,7 +68,7 @@ namespace Content.IntegrationTests.Tests.Atmos [TestCase(Atmospherics.BreathPercentage)] public async Task RemoveRatio(float ratio) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitAssertion(() => @@ -103,8 +102,6 @@ namespace Content.IntegrationTests.Tests.Atmos Assert.That(a.GetMoles(Gas.Nitrogen), Is.EqualTo(100 - b.GetMoles(Gas.Nitrogen))); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs b/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs index 45ccddfad9..9b43e85396 100644 --- a/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs @@ -1,4 +1,5 @@ using Content.Server.Atmos.EntitySystems; +using Content.IntegrationTests.Fixtures; using Content.Server.Atmos.Piping.EntitySystems; using Content.Shared.Atmos.Components; using Robust.Shared.GameObjects; @@ -6,14 +7,14 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Atmos; [TestFixture] -public sealed class GridJoinTest +public sealed class GridJoinTest : GameTest { private const string CanisterProtoId = "AirCanister"; [Test] public async Task TestGridJoinAtmosphere() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -46,7 +47,5 @@ public sealed class GridJoinTest // Make sure that the canister is now properly tracked as on-grid Assert.That(atmosDeviceSystem.IsJoinedOffGrid(canisterEnt), Is.False); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs b/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs index ac80f4a105..7269b1b473 100644 --- a/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs @@ -37,7 +37,7 @@ public sealed class SharedGasSpecificHeatsTest { Connected = true, }; - _pair = await PoolManager.GetServerClient(poolSettings); + _pair = await PoolManager.GetServerClient(poolSettings, new NUnitTestContextWrap(TestContext.CurrentContext, TestContext.Out)); _sEntMan = Server.ResolveDependency(); _cEntMan = Client.ResolveDependency(); @@ -62,12 +62,12 @@ public sealed class SharedGasSpecificHeatsTest var clientSpecificHeats = Array.Empty(); await Server.WaitPost(delegate { - serverSpecificHeats = _sAtmos.GasSpecificHeats; + serverSpecificHeats = _sAtmos.GasMolarHeatCapacities; }); await Client.WaitPost(delegate { - clientSpecificHeats = _cAtmos.GasSpecificHeats; + clientSpecificHeats = _cAtmos.GasMolarHeatCapacities; }); Assert.That(serverSpecificHeats, diff --git a/Content.IntegrationTests/Tests/Body/GibbingTest.cs b/Content.IntegrationTests/Tests/Body/GibbingTest.cs index a727487940..a3f3c1bcc3 100644 --- a/Content.IntegrationTests/Tests/Body/GibbingTest.cs +++ b/Content.IntegrationTests/Tests/Body/GibbingTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Body; using Content.Shared.Gibbing; using Robust.Shared.GameObjects; @@ -6,7 +7,7 @@ namespace Content.IntegrationTests.Tests.Body; [TestFixture] [TestOf(typeof(GibbableOrganSystem))] -public sealed class GibletTest +public sealed class GibletTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -33,7 +34,7 @@ public sealed class GibletTest [Test] public async Task GibletCountTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -54,7 +55,5 @@ public sealed class GibletTest Assert.That(entityManager.HasComponent(giblet), Is.True); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Body/HandOrganTest.cs b/Content.IntegrationTests/Tests/Body/HandOrganTest.cs index 560dfbf64a..3d885186f1 100644 --- a/Content.IntegrationTests/Tests/Body/HandOrganTest.cs +++ b/Content.IntegrationTests/Tests/Body/HandOrganTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Body; using Content.Shared.Hands.Components; using Robust.Shared.Containers; @@ -9,7 +10,7 @@ namespace Content.IntegrationTests.Tests.Body; [TestFixture] [TestOf(typeof(HandOrganSystem))] -public sealed class HandOrganTest +public sealed class HandOrganTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -46,7 +47,7 @@ public sealed class HandOrganTest [Test] public async Task HandInsertionAndRemovalTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -81,7 +82,5 @@ public sealed class HandOrganTest Assert.That(hands.Count, Is.EqualTo(expectedCount)); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs index d9cce764ab..444bd159f3 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs @@ -12,7 +12,7 @@ public sealed partial class BuckleTest [Test] public async Task BuckleInteractUnbuckleOther() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -55,14 +55,12 @@ public sealed partial class BuckleTest Assert.That(strap.BuckledEntities, Does.Not.Contain(victim)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task BuckleInteractBuckleUnbuckleSelf() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -102,7 +100,5 @@ public sealed partial class BuckleTest Assert.That(strap.BuckledEntities, Does.Not.Contain(user)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index b42f42922a..9c12da3fe8 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Shared.Buckle; using Content.Shared.ActionBlocker; using Content.Shared.Buckle.Components; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Buckle [TestFixture] [TestOf(typeof(BuckleComponent))] [TestOf(typeof(StrapComponent))] - public sealed partial class BuckleTest + public sealed partial class BuckleTest : GameTest { private const string BuckleDummyId = "BuckleDummy"; private const string StrapDummyId = "StrapDummy"; @@ -50,7 +51,7 @@ namespace Content.IntegrationTests.Tests.Buckle [Test] public async Task BuckleUnbuckleCooldownRangeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -228,14 +229,12 @@ namespace Content.IntegrationTests.Tests.Buckle Assert.That(strap.BuckledEntities, Is.Empty); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task BuckledDyingDropItemsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -298,14 +297,12 @@ namespace Content.IntegrationTests.Tests.Buckle buckleSystem.Unbuckle(human, human); Assert.That(buckle.Buckled, Is.False); }); - - await pair.CleanReturnAsync(); } [Test] public async Task ForceUnbuckleBuckleTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -373,7 +370,6 @@ namespace Content.IntegrationTests.Tests.Buckle Assert.That(buckle.Buckled); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs index df85e61550..0c8fc2f0fb 100644 --- a/Content.IntegrationTests/Tests/CargoTest.cs +++ b/Content.IntegrationTests/Tests/CargoTest.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Cargo.Components; using Content.Server.Cargo.Systems; using Content.Server.Nutrition.Components; @@ -17,7 +18,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class CargoTest +public sealed class CargoTest : GameTest { private static readonly HashSet> Ignored = [ @@ -28,7 +29,7 @@ public sealed class CargoTest [Test] public async Task NoCargoOrderArbitrage() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -54,13 +55,11 @@ public sealed class CargoTest } }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task NoCargoBountyArbitrageTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -94,14 +93,12 @@ public sealed class CargoTest mapSystem.DeleteMap(mapId); }); - - await pair.CleanReturnAsync(); } [Test] public async Task NoStaticPriceAndStackPrice() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoManager = server.ProtoMan; @@ -133,8 +130,6 @@ public sealed class CargoTest } } }); - - await pair.CleanReturnAsync(); } /// @@ -144,7 +139,7 @@ public sealed class CargoTest [Test] public async Task NoSliceableBountyArbitrageTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -209,8 +204,6 @@ public sealed class CargoTest } mapSystem.DeleteMap(mapId); }); - - await pair.CleanReturnAsync(); } [TestPrototypes] @@ -233,7 +226,7 @@ public sealed class CargoTest [Test] public async Task StackPrice() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -245,14 +238,12 @@ public sealed class CargoTest var price = priceSystem.GetPrice(ent); Assert.That(price, Is.EqualTo(100.0)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task MobPrice() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var componentFactory = pair.Server.ResolveDependency(); @@ -266,7 +257,5 @@ public sealed class CargoTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs b/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs index 59948c8b17..9ac45549a2 100644 --- a/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Tests.Interaction; using Content.Shared.Chemistry.Reagent; using Robust.Shared.Reflection; @@ -8,12 +9,12 @@ namespace Content.IntegrationTests.Tests.Chemistry; [TestFixture] [TestOf(typeof(ReagentData))] -public sealed class ReagentDataTest +public sealed class ReagentDataTest : GameTest { [Test] public async Task ReagentDataIsSerializable() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var reflection = pair.Server.ResolveDependency(); Assert.Multiple(() => @@ -24,7 +25,5 @@ public sealed class ReagentDataTest Assert.That(instance.HasCustomAttribute(), $"{instance} must have the serializable attribute."); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs index 5b5829d386..f188fd0f66 100644 --- a/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; @@ -9,7 +10,7 @@ namespace Content.IntegrationTests.Tests.Chemistry; [TestFixture] [TestOf(typeof(ChemicalReactionSystem))] -public sealed class SolutionRoundingTest +public sealed class SolutionRoundingTest : GameTest { // This test tests two things: // * A rounding error in reaction code while I was making chloral hydrate @@ -72,7 +73,7 @@ public sealed class SolutionRoundingTest [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -121,7 +122,5 @@ public sealed class SolutionRoundingTest Is.EqualTo((FixedPoint2) 30)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs index 6f50f54103..ffaca012a3 100644 --- a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.FixedPoint; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Chemistry; // reactions can change this assumption [TestFixture] [TestOf(typeof(SharedSolutionContainerSystem))] -public sealed class SolutionSystemTests +public sealed class SolutionSystemTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -53,7 +54,7 @@ public sealed class SolutionSystemTests [Test] public async Task TryAddTwoNonReactiveReagent() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -88,8 +89,6 @@ public sealed class SolutionSystemTests Assert.That(oil, Is.EqualTo(oilQuantity)); }); }); - - await pair.CleanReturnAsync(); } // This test mimics current behavior @@ -97,7 +96,7 @@ public sealed class SolutionSystemTests [Test] public async Task TryAddTooMuchNonReactiveReagent() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -133,15 +132,13 @@ public sealed class SolutionSystemTests Assert.That(oil, Is.EqualTo(FixedPoint2.Zero)); }); }); - - await pair.CleanReturnAsync(); } // Unlike TryAddSolution this adds and two solution without then splits leaving only threshold in original [Test] public async Task TryMixAndOverflowTooMuchReagent() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; @@ -188,15 +185,13 @@ public sealed class SolutionSystemTests Assert.That(oilOverFlow, Is.EqualTo(oilQuantity - oilMix)); }); }); - - await pair.CleanReturnAsync(); } // TryMixAndOverflow will fail if Threshold larger than MaxVolume [Test] public async Task TryMixAndOverflowTooBigOverflow() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -226,14 +221,12 @@ public sealed class SolutionSystemTests .TryMixAndOverflow(solutionEnt.Value, oilAdded, threshold, out _), Is.False); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestTemperatureCalculations() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); const float temp = 100.0f; @@ -264,7 +257,5 @@ public sealed class SolutionSystemTests solutionOne.AddSolution(solutionTwo, protoMan); Assert.That(solutionOne.GetHeatCapacity(protoMan) * solutionOne.Temperature, Is.EqualTo(thermalEnergyOne + thermalEnergyTwo)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs index 0037670556..e720387425 100644 --- a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs @@ -5,6 +5,7 @@ using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Utility; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Shared.Chemistry.EntitySystems; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Chemistry { [TestFixture] [TestOf(typeof(ReactionPrototype))] - public sealed class TryAllReactionsTest + public sealed class TryAllReactionsTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -33,7 +34,7 @@ namespace Content.IntegrationTests.Tests.Chemistry [Description("Tries an individual reaction to see if it succeeds.")] public async Task TryReaction(string reaction) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -134,8 +135,6 @@ namespace Content.IntegrationTests.Tests.Chemistry server.EntMan.DeleteEntity(beaker); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs b/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs index e2bff03501..037224fec5 100644 --- a/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs +++ b/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs @@ -1,35 +1,37 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Administration.UI; using Content.Server.EUI; using Robust.Server.Player; namespace Content.IntegrationTests.Tests.Cleanup; -public sealed class EuiManagerTest +public sealed class EuiManagerTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + Connected = true, + Dirty = true + }; + [Test] + [Retry(2)] + // Even though we are using the server EUI here, we actually want to see if the client EUIManager crashes public async Task EuiManagerRecycleWithOpenWindowTest() { - // Even though we are using the server EUI here, we actually want to see if the client EUIManager crashes - for (var i = 0; i < 2; i++) + var pair = Pair; + var server = pair.Server; + + var sPlayerManager = server.ResolveDependency(); + var eui = server.ResolveDependency(); + + await server.WaitAssertion(() => { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true - }); - var server = pair.Server; + var clientSession = sPlayerManager.Sessions.Single(); + var ui = new AdminAnnounceEui(); + eui.OpenEui(ui, clientSession); + }); - var sPlayerManager = server.ResolveDependency(); - var eui = server.ResolveDependency(); - - await server.WaitAssertion(() => - { - var clientSession = sPlayerManager.Sessions.Single(); - var ui = new AdminAnnounceEui(); - eui.OpenEui(ui, clientSession); - }); - await pair.CleanReturnAsync(); - } + await RunUntilSynced(); } } diff --git a/Content.IntegrationTests/Tests/ClickableTest.cs b/Content.IntegrationTests/Tests/ClickableTest.cs index aaac421ed1..fb20cd9038 100644 --- a/Content.IntegrationTests/Tests/ClickableTest.cs +++ b/Content.IntegrationTests/Tests/ClickableTest.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Client.Clickable; +using Content.IntegrationTests.Fixtures; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class ClickableTest + public sealed class ClickableTest : GameTest { private const double DirSouth = 0; private const double DirNorth = Math.PI; @@ -44,7 +45,7 @@ namespace Content.IntegrationTests.Tests [TestCase("ClickTestRotatingCornerInvisibleNoRot", 0.25f, 0.25f, DirSouthEastJustShy, 1, ExpectedResult = true)] public async Task Test(string prototype, float clickPosX, float clickPosY, double angle, float scale) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -66,7 +67,7 @@ namespace Content.IntegrationTests.Tests }); // Let client sync up. - await pair.RunTicksSync(5); + await RunUntilSynced(); var hit = false; var clientEnt = clientEntManager.GetEntity(serverEntManager.GetNetEntity(serverEnt)); @@ -89,8 +90,6 @@ namespace Content.IntegrationTests.Tests serverEntManager.DeleteEntity(serverEnt); }); - await pair.CleanReturnAsync(); - return hit; } } diff --git a/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs b/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs index 9edc115eee..c21bcf922a 100644 --- a/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs @@ -1,8 +1,9 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Cloning; namespace Content.IntegrationTests.Tests.Cloning; -public sealed class CloningSettingsPrototypeTest +public sealed class CloningSettingsPrototypeTest : GameTest { /// /// Checks that the components named in every are valid components known to the server. @@ -12,7 +13,7 @@ public sealed class CloningSettingsPrototypeTest [Test] public async Task ValidatePrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; var compFactory = server.EntMan.ComponentFactory; @@ -40,7 +41,5 @@ public sealed class CloningSettingsPrototypeTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs index 3fa7e64f1a..566edd47ca 100644 --- a/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs +++ b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Maps; using Content.Shared.CCVar; using Robust.Shared.Configuration; @@ -6,7 +7,7 @@ using Robust.Shared.Console; namespace Content.IntegrationTests.Tests.Commands; [TestFixture] -public sealed class ForceMapTest +public sealed class ForceMapTest : GameTest { private const string DefaultMapName = "Empty"; private const string BadMapName = "asdf_asd-fa__sdfAsd_f"; // Hopefully no one ever names a map this... @@ -44,7 +45,7 @@ public sealed class ForceMapTest [Test] public async Task TestForceMapCommand() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -82,7 +83,5 @@ public sealed class ForceMapTest // Cleanup configManager.SetCVar(CCVars.GameMap, DefaultMapName); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs b/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs index d430325e31..eb18d8e1ae 100644 --- a/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs +++ b/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Objectives; using Content.Shared.Mind; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Player; namespace Content.IntegrationTests.Tests.Commands; -public sealed class ObjectiveCommandsTest +public sealed class ObjectiveCommandsTest : GameTest { private const string ObjectiveProtoId = "MindCommandsTestObjective"; @@ -27,6 +28,11 @@ public sealed class ObjectiveCommandsTest - type: DieCondition """; + public override PoolSettings PoolSettings => new () + { + Connected = false + }; + /// /// Creates a dummy session, and assigns it a mind, then /// tests using addobjective, lsobjectives, @@ -35,7 +41,7 @@ public sealed class ObjectiveCommandsTest [Test] public async Task AddListRemoveObjectiveTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; var playerMan = server.ResolveDependency(); @@ -66,7 +72,5 @@ public sealed class ObjectiveCommandsTest await pair.WaitCommand($"rmobjective {playerSession.Name} 0"); Assert.That(mindComp.Objectives, Is.Empty, "rmobjective failed to remove objective"); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs index 5f77af1b10..2d84eebf96 100644 --- a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs +++ b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Database; using Robust.Server.Console; using Robust.Server.Player; @@ -8,14 +9,14 @@ namespace Content.IntegrationTests.Tests.Commands { [TestFixture] [TestOf(typeof(PardonCommand))] - public sealed class PardonCommand + public sealed class PardonCommand : GameTest { private static readonly TimeSpan MarginOfError = TimeSpan.FromMinutes(1); [Test] public async Task PardonTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -148,8 +149,6 @@ namespace Content.IntegrationTests.Tests.Commands await client.WaitPost(() => netMan.ClientConnect(null!, 0, null!)); await pair.RunTicksSync(5); Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(1)); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs index 308f2797c4..15d93c40ae 100644 --- a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs @@ -1,4 +1,5 @@ -using Content.Shared.Administration.Systems; +using Content.IntegrationTests.Fixtures; +using Content.Shared.Administration.Systems; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -14,7 +15,7 @@ namespace Content.IntegrationTests.Tests.Commands { [TestFixture] [TestOf(typeof(RejuvenateSystem))] - public sealed class RejuvenateTest + public sealed class RejuvenateTest : GameTest { private static readonly ProtoId TestDamageGroup = "Toxin"; @@ -36,7 +37,7 @@ namespace Content.IntegrationTests.Tests.Commands [Test] public async Task RejuvenateDeadTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); var prototypeManager = server.ResolveDependency(); @@ -92,7 +93,6 @@ namespace Content.IntegrationTests.Tests.Commands Assert.That(damSystem.GetTotalDamage((human, damageable)), Is.EqualTo(FixedPoint2.Zero)); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs index 72a05b5246..bcacc8011b 100644 --- a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.GameTicking.Commands; using Content.Shared.CCVar; @@ -10,25 +11,27 @@ namespace Content.IntegrationTests.Tests.Commands { [TestFixture] [TestOf(typeof(RestartRoundNowCommand))] - public sealed class RestartRoundNowTest + public sealed class RestartRoundNowTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Dirty = true + }; + [Test] [TestCase(true)] [TestCase(false)] public async Task RestartRoundAfterStart(bool lobbyEnabled) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Dirty = true - }); + var pair = Pair; var server = pair.Server; var configManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var gameTicker = entityManager.System(); - await pair.RunTicksSync(5); + await pair.RunUntilSynced(); GameTick tickBeforeRestart = default; @@ -58,8 +61,7 @@ namespace Content.IntegrationTests.Tests.Commands Assert.That(tickBeforeRestart, Is.LessThan(tickAfterRestart)); }); - await pair.RunTicksSync(5); - await pair.CleanReturnAsync(); + await pair.RunUntilSynced(); } } } diff --git a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs index 7740a03ad9..29ce5bfb66 100644 --- a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs +++ b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Shared._WL.Execution; //WL-Change +using Content.IntegrationTests.Fixtures; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -22,7 +23,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Commands; [TestFixture, Ignore("WL-Changes: Damn golems and IPC")] //WL-Change -public sealed class SuicideCommandTests +public sealed class SuicideCommandTests : GameTest { [TestPrototypes] @@ -58,6 +59,13 @@ public sealed class SuicideCommandTests private static readonly ProtoId CannotSuicideTag = "CannotSuicide"; private static readonly ProtoId DamageType = "Slash"; + public override PoolSettings PoolSettings => new PoolSettings + { + Connected = true, + Dirty = true, + DummyTicker = false + }; + /// /// Run the suicide command in the console /// Should successfully kill the player and ghost them @@ -65,12 +73,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicide() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -105,8 +108,6 @@ public sealed class SuicideCommandTests !ghostComp.CanReturnToBody); }); }); - - await pair.CleanReturnAsync(); } /// @@ -116,12 +117,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicideWhileDamaged() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -167,8 +163,6 @@ public sealed class SuicideCommandTests Assert.That(damageableSystem.GetTotalDamage(player), Is.EqualTo(lethalDamageThreshold)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -178,12 +172,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicideWhenCannotSuicide() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -218,8 +207,6 @@ public sealed class SuicideCommandTests !ghostComp.CanReturnToBody); }); }); - - await pair.CleanReturnAsync(); } @@ -229,12 +216,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicideByHeldItem() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -293,8 +275,6 @@ public sealed class SuicideCommandTests Assert.That(damageableSystem.GetAllDamage((player, damageableComp)).DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -304,12 +284,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicideByHeldItemSpreadDamage() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -368,7 +343,5 @@ public sealed class SuicideCommandTests Assert.That(damageableSystem.GetAllDamage((player, damageableComp)).DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold / 2)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ConfigPresetTests.cs b/Content.IntegrationTests/Tests/ConfigPresetTests.cs index ebeea7f391..224a5677c0 100644 --- a/Content.IntegrationTests/Tests/ConfigPresetTests.cs +++ b/Content.IntegrationTests/Tests/ConfigPresetTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using Content.IntegrationTests.Fixtures; using Content.Server.Entry; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; @@ -7,12 +8,12 @@ using Robust.Shared.ContentPack; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class ConfigPresetTests +public sealed class ConfigPresetTests : GameTest { [Test] public async Task TestLoadAll() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var resources = server.ResolveDependency(); @@ -70,7 +71,5 @@ public sealed class ConfigPresetTests Assert.Fail($"CVar {name} was not reset to its original value."); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs b/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs index c59b42d638..8c68e5b192 100644 --- a/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs +++ b/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs @@ -1,4 +1,5 @@ using System.Text; +using Content.IntegrationTests.Fixtures; using Content.Server.Construction.Completions; using Content.Shared.Construction; using Content.Shared.Construction.Prototypes; @@ -7,7 +8,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Construction { [TestFixture] - public sealed class ConstructionActionValid + public sealed class ConstructionActionValid : GameTest { private bool IsValid(IGraphAction action, IPrototypeManager protoMan, out string prototype) { @@ -47,7 +48,7 @@ namespace Content.IntegrationTests.Tests.Construction [Test] public async Task ConstructionGraphSpawnPrototypeValid() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -84,13 +85,12 @@ namespace Content.IntegrationTests.Tests.Construction }); Assert.That(valid, Is.True, $"One or more SpawnPrototype actions specified invalid entity prototypes!\n{message}"); - await pair.CleanReturnAsync(); } [Test] public async Task ConstructionGraphEdgeValid() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -118,7 +118,6 @@ namespace Content.IntegrationTests.Tests.Construction }); Assert.That(valid, Is.True, $"One or more edges specified invalid node targets!\n{message}"); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs b/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs index 9441443b22..49cc76e945 100644 --- a/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Server.Construction.Components; using Content.Shared.Construction.Prototypes; @@ -7,7 +8,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Construction { [TestFixture] - public sealed class ConstructionPrototypeTest + public sealed class ConstructionPrototypeTest : GameTest { // discount linter for construction graphs // TODO: Create serialization validators for these? @@ -25,7 +26,7 @@ namespace Content.IntegrationTests.Tests.Construction [Description("Tests that a given entity specifies a valid node for construction, and optionally a valid one for deconstruction.")] public async Task ConstructionComponentValid(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -49,8 +50,6 @@ namespace Content.IntegrationTests.Tests.Construction $"Invalid deconstruction node \"{target}\" on graph \"{graph.ID}\" for construction entity \"{proto.ID}\"!"); } }); - - await pair.CleanReturnAsync(); } [Test] @@ -59,7 +58,7 @@ namespace Content.IntegrationTests.Tests.Construction [Description("Tests that a given construction prototype has a valid starting and target node, and a valid path between them.")] public async Task ConstructionFormsValidGraph(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -95,7 +94,6 @@ namespace Content.IntegrationTests.Tests.Construction $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an entity prototype ({next.Entity}) without a ConstructionComponent."); #pragma warning restore NUnit2045 }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs index 37c4b0c9b5..cb7e401601 100644 --- a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs +++ b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Storage.EntitySystems; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Maths; namespace Content.IntegrationTests.Tests { - public sealed class ContainerOcclusionTest + public sealed class ContainerOcclusionTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -34,7 +35,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task TestA() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -69,14 +70,12 @@ namespace Content.IntegrationTests.Tests Assert.That(light.ContainerOccluded); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestB() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -112,14 +111,12 @@ namespace Content.IntegrationTests.Tests Assert.That(light.ContainerOccluded, Is.False); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestAb() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -157,8 +154,6 @@ namespace Content.IntegrationTests.Tests Assert.That(light.ContainerOccluded); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/ContrabandTest.cs b/Content.IntegrationTests/Tests/ContrabandTest.cs index c52ef293e1..9f0fb1d499 100644 --- a/Content.IntegrationTests/Tests/ContrabandTest.cs +++ b/Content.IntegrationTests/Tests/ContrabandTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Contraband; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -5,12 +6,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class ContrabandTest +public sealed class ContrabandTest : GameTest { [Test] public async Task EntityShowDepartmentsAndJobs() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var client = pair.Client; var protoMan = client.ResolveDependency(); var componentFactory = client.ResolveDependency(); @@ -41,7 +42,5 @@ public sealed class ContrabandTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs index 9777054625..758bbe0a50 100644 --- a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Damageable [TestFixture] [TestOf(typeof(DamageableComponent))] [TestOf(typeof(DamageableSystem))] - public sealed class DamageableTest + public sealed class DamageableTest : GameTest { private const string TestDamageableEntityId = "TestDamageableEntityId"; private const string TestGroup1 = "TestGroup1"; @@ -95,7 +96,7 @@ namespace Content.IntegrationTests.Tests.Damageable [Test] public async Task TestDamageableComponents() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntityManager = server.ResolveDependency(); @@ -254,7 +255,6 @@ namespace Content.IntegrationTests.Tests.Damageable sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, -100)); Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero)); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Damageable/MobThresholdsTest.cs b/Content.IntegrationTests/Tests/Damageable/MobThresholdsTest.cs index b359f05569..c4179135cd 100644 --- a/Content.IntegrationTests/Tests/Damageable/MobThresholdsTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/MobThresholdsTest.cs @@ -1,10 +1,11 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Shared.Alert; using Content.Shared.Mobs.Components; namespace Content.IntegrationTests.Tests.Damageable; -public sealed class MobThresholdsTest +public sealed class MobThresholdsTest : GameTest { private static string[] _entitiesWithThresholds = GameDataScrounger.EntitiesWithComponent("MobThresholds"); @@ -14,7 +15,7 @@ public sealed class MobThresholdsTest [Description("Ensures every entity with mob thresholds has valid mob state configuration corresponding to some AlertPrototype.")] public async Task ValidateMobThresholds(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; @@ -33,7 +34,5 @@ public sealed class MobThresholdsTest Assert.That(alertStates, Does.Contain(state), $"{proto.ID} does not have an alert state for mob state {state}"); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Damageable/StaminaComponentTest.cs b/Content.IntegrationTests/Tests/Damageable/StaminaComponentTest.cs index 09be373a4c..b4a278529d 100644 --- a/Content.IntegrationTests/Tests/Damageable/StaminaComponentTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/StaminaComponentTest.cs @@ -1,11 +1,12 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Shared.Damage.Components; using Content.Shared.FixedPoint; namespace Content.IntegrationTests.Tests.Damageable; -public sealed class StaminaComponentTest +public sealed class StaminaComponentTest : GameTest { private static string[] _entitiesWithStamina = GameDataScrounger.EntitiesWithComponent("Stamina"); @@ -15,7 +16,7 @@ public sealed class StaminaComponentTest [Description("Ensures every entity with Stamina has a valid stamina configuration.")] public async Task ValidateStamina(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; @@ -46,7 +47,5 @@ public sealed class StaminaComponentTest #pragma warning restore NUnit2041 } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/DeleteInventoryTest.cs b/Content.IntegrationTests/Tests/DeleteInventoryTest.cs index 49d54bbecf..bbaf11a052 100644 --- a/Content.IntegrationTests/Tests/DeleteInventoryTest.cs +++ b/Content.IntegrationTests/Tests/DeleteInventoryTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Clothing.Components; using Content.Shared.Clothing.EntitySystems; using Content.Shared.Inventory; @@ -7,14 +8,14 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class DeleteInventoryTest + public sealed class DeleteInventoryTest : GameTest { // Test that when deleting an entity with an InventoryComponent, // any equipped items also get deleted. [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); var entMgr = server.ResolveDependency(); @@ -44,7 +45,6 @@ namespace Content.IntegrationTests.Tests // Assert that child item was also deleted. Assert.That(item.Deleted, Is.True); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs index f5010eefdc..a83891b8fc 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -13,12 +14,12 @@ namespace Content.IntegrationTests.Tests.Destructible [TestFixture] [TestOf(typeof(DamageGroupTrigger))] [TestOf(typeof(AndTrigger))] - public sealed class DestructibleDamageGroupTest + public sealed class DestructibleDamageGroupTest : GameTest { [Test] public async Task AndTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -193,7 +194,6 @@ namespace Content.IntegrationTests.Tests.Destructible // No new thresholds reached as triggers once is set to true and it already triggered before Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs index 70baaea95a..47f44ad496 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -12,12 +13,12 @@ namespace Content.IntegrationTests.Tests.Destructible [TestFixture] [TestOf(typeof(DamageTypeTrigger))] [TestOf(typeof(AndTrigger))] - public sealed class DestructibleDamageTypeTest + public sealed class DestructibleDamageTypeTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -188,7 +189,6 @@ namespace Content.IntegrationTests.Tests.Destructible // No new thresholds reached as triggers once is set to true and it already triggered before Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs index df98294ee9..0aa0449682 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Destructible.Thresholds.Behaviors; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; @@ -10,12 +11,12 @@ using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototy namespace Content.IntegrationTests.Tests.Destructible { - public sealed class DestructibleDestructionTest + public sealed class DestructibleDestructionTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -89,7 +90,6 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(found, Is.True, $"Unable to find {SpawnedEntityId} nearby for destructible test; found {entitiesInRange.Count} entities."); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs index 4460affedf..da56a43a4a 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Destructible; using Content.Server.Destructible.Thresholds; using Content.Server.Destructible.Thresholds.Behaviors; @@ -19,12 +20,12 @@ namespace Content.IntegrationTests.Tests.Destructible [TestFixture] [TestOf(typeof(DestructibleComponent))] [TestOf(typeof(DamageThreshold))] - public sealed class DestructibleThresholdActivationTest + public sealed class DestructibleThresholdActivationTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntityManager = server.ResolveDependency(); @@ -289,7 +290,6 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/DeviceLinking/DeviceLinkingTest.cs b/Content.IntegrationTests/Tests/DeviceLinking/DeviceLinkingTest.cs index c62b46ab6d..7a2a880bae 100644 --- a/Content.IntegrationTests/Tests/DeviceLinking/DeviceLinkingTest.cs +++ b/Content.IntegrationTests/Tests/DeviceLinking/DeviceLinkingTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Server.DeviceLinking.Systems; using Content.Shared.DeviceLinking; @@ -7,7 +8,7 @@ using Robust.Shared.Maths; namespace Content.IntegrationTests.Tests.DeviceLinking; -public sealed class DeviceLinkingTest +public sealed class DeviceLinkingTest : GameTest { private const string PortTesterProtoId = "DeviceLinkingSinkPortTester"; @@ -29,7 +30,7 @@ public sealed class DeviceLinkingTest [Description("Ensures all devices that can sink signals will not cause exceptions when signaled.")] public async Task DeviceLinkSinkAllPortsTest(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; var compFact = server.ResolveDependency(); @@ -77,7 +78,5 @@ public sealed class DeviceLinkingTest } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs index fdc0e1a4d4..6ecdffc58c 100644 --- a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs +++ b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Shared.DeviceNetwork; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork [TestOf(typeof(DeviceNetworkComponent))] [TestOf(typeof(WiredNetworkComponent))] [TestOf(typeof(WirelessNetworkComponent))] - public sealed class DeviceNetworkTest + public sealed class DeviceNetworkTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -50,7 +51,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork [Test] public async Task NetworkDeviceSendAndReceive() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -104,13 +105,12 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork { Assert.That(payload, Is.EquivalentTo(deviceNetTestSystem.LastPayload)); }); - await pair.CleanReturnAsync(); } [Test] public async Task WirelessNetworkDeviceSendAndReceive() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; @@ -188,14 +188,12 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork { Assert.That(payload, Is.Not.EqualTo(deviceNetTestSystem.LastPayload).AsCollection); }); - - await pair.CleanReturnAsync(); } [Test] public async Task WiredNetworkDeviceSendAndReceive() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; @@ -271,8 +269,6 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork { Assert.That(payload, Is.EqualTo(deviceNetTestSystem.LastPayload).AsCollection); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index 52b669b09d..f7b20819c2 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -1,6 +1,7 @@ #nullable enable annotations using System.Linq; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Disposal.Unit; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -16,7 +17,7 @@ namespace Content.IntegrationTests.Tests.Disposal [TestOf(typeof(DisposalHolderComponent))] [TestOf(typeof(DisposalEntryComponent))] [TestOf(typeof(DisposalUnitComponent))] - public sealed class DisposalUnitTest + public sealed class DisposalUnitTest : GameTest { [Reflect(false)] private sealed class DisposalUnitTestSystem : EntitySystem @@ -145,7 +146,7 @@ namespace Content.IntegrationTests.Tests.Disposal [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -240,8 +241,6 @@ namespace Content.IntegrationTests.Tests.Disposal // Re-pressurizing Flush(disposalUnit, unitComponent, false, disposalSystem); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs b/Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs index 32f8b58542..d9619076e0 100644 --- a/Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs +++ b/Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Robust.Shared.GameObjects; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.DoAfter { [TestFixture] [TestOf(typeof(DoAfterComponent))] - public sealed partial class DoAfterServerTest + public sealed partial class DoAfterServerTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -35,7 +36,7 @@ namespace Content.IntegrationTests.Tests.DoAfter [Test] public async Task TestSerializable() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); var refMan = server.ResolveDependency(); @@ -55,14 +56,12 @@ namespace Content.IntegrationTests.Tests.DoAfter } }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestFinished() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -84,14 +83,12 @@ namespace Content.IntegrationTests.Tests.DoAfter await server.WaitRunTicks(1); Assert.That(ev.Cancelled, Is.False); - - await pair.CleanReturnAsync(); } [Test] public async Task TestCancelled() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.EntMan; var timing = server.ResolveDependency(); @@ -113,8 +110,6 @@ namespace Content.IntegrationTests.Tests.DoAfter await server.WaitRunTicks(3); Assert.That(ev.Cancelled); - - await pair.CleanReturnAsync(); } /// @@ -124,7 +119,7 @@ namespace Content.IntegrationTests.Tests.DoAfter [Test] public async Task TestGetInteractingEntities() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.EntMan; var timing = server.ResolveDependency(); @@ -175,8 +170,6 @@ namespace Content.IntegrationTests.Tests.DoAfter entityManager.DeleteEntity(target); entityManager.DeleteEntity(target2); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs index 69fe66039b..af03de169f 100644 --- a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs +++ b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Doors.Systems; using Content.Shared.Doors.Components; using Robust.Shared.GameObjects; @@ -11,7 +12,7 @@ namespace Content.IntegrationTests.Tests.Doors { [TestFixture] [TestOf(typeof(AirlockComponent))] - public sealed class AirlockTest + public sealed class AirlockTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -54,7 +55,7 @@ namespace Content.IntegrationTests.Tests.Doors [Test] public async Task OpenCloseDestroyTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -104,16 +105,12 @@ namespace Content.IntegrationTests.Tests.Doors entityManager.DeleteEntity(airlock); }); }); - - server.RunTicks(5); - - await pair.CleanReturnAsync(); } [Test] public async Task AirlockBlockTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -179,7 +176,6 @@ namespace Content.IntegrationTests.Tests.Doors { Assert.That(Math.Abs(xformSystem.GetWorldPosition(airlockPhysicsDummy).X - 1), Is.GreaterThan(0.01f)); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/DummyIconTest.cs b/Content.IntegrationTests/Tests/DummyIconTest.cs index 62197bb319..dcb5d315dc 100644 --- a/Content.IntegrationTests/Tests/DummyIconTest.cs +++ b/Content.IntegrationTests/Tests/DummyIconTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Robust.Client.GameObjects; using Robust.Client.ResourceManagement; using Robust.Shared.Prototypes; @@ -7,12 +8,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class DummyIconTest + public sealed class DummyIconTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var client = pair.Client; var prototypeManager = client.ResolveDependency(); var resourceCache = client.ResolveDependency(); @@ -32,7 +33,6 @@ namespace Content.IntegrationTests.Tests proto.ID); } }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 9b0e7729f5..13293b7d46 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Robust.Shared; using Robust.Shared.Audio.Components; using Robust.Shared.Configuration; @@ -16,17 +18,28 @@ namespace Content.IntegrationTests.Tests { [TestFixture] [TestOf(typeof(EntityUid))] - public sealed class EntityTest + public sealed class EntityTest : GameTest { private static readonly ProtoId SpawnerCategory = "Spawner"; + public override PoolSettings PoolSettings => new() + { + Connected = true, + Dirty = true + }; + + public static PoolSettings Disconnected => new() + { + Dirty = true, + }; + [Test] + [PairConfig(nameof(Disconnected))] public async Task SpawnAndDeleteAllEntitiesOnDifferentMaps() { // This test dirties the pair as it simply deletes ALL entities when done. Overhead of restarting the round // is minimal relative to the rest of the test. - var settings = new PoolSettings { Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var server = pair.Server; var entityMan = server.ResolveDependency(); @@ -79,17 +92,14 @@ namespace Content.IntegrationTests.Tests Assert.That(entityMan.EntityCount, Is.Zero); }); - - await pair.CleanReturnAsync(); } [Test] + [PairConfig(nameof(Disconnected))] public async Task SpawnAndDeleteAllEntitiesInTheSameSpot() { - // This test dirties the pair as it simply deletes ALL entities when done. Overhead of restarting the round - // is minimal relative to the rest of the test. - var settings = new PoolSettings { Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; + Assert.That(pair.Client.Session, Is.Null); var server = pair.Server; var map = await pair.CreateTestMap(); @@ -134,8 +144,6 @@ namespace Content.IntegrationTests.Tests Assert.That(entityMan.EntityCount, Is.Zero); }); - - await pair.CleanReturnAsync(); } /// @@ -145,10 +153,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task SpawnAndDirtyAllEntities() { - // This test dirties the pair as it simply deletes ALL entities when done. Overhead of restarting the round - // is minimal relative to the rest of the test. - var settings = new PoolSettings { Connected = true, Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -182,7 +187,7 @@ namespace Content.IntegrationTests.Tests } }); - await pair.RunTicksSync(15); + await pair.RunUntilSynced(); // Make sure the client actually received the entities // 500 is completely arbitrary. Note that the client & sever entity counts aren't expected to match. @@ -209,8 +214,6 @@ namespace Content.IntegrationTests.Tests Assert.That(sEntMan.EntityCount, Is.Zero); }); - - await pair.CleanReturnAsync(); } /// @@ -230,8 +233,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task SpawnAndDeleteEntityCountTest() { - var settings = new PoolSettings { Connected = true, Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var mapSys = pair.Server.System(); var server = pair.Server; var client = pair.Client; @@ -317,8 +319,6 @@ namespace Content.IntegrationTests.Tests BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan)); } }); - - await pair.CleanReturnAsync(); } private static string BuildDiffString(IEnumerable oldEnts, IEnumerable newEnts, IEntityManager entMan) @@ -385,14 +385,11 @@ namespace Content.IntegrationTests.Tests "StationData", // errors when removed mid-round "StationJobs", "Actor", // We aren't testing actor components, those need their player session set. - "BlobFloorPlanBuilder", // Implodes if unconfigured. - "DebrisFeaturePlacerController", // Above. - "LoadedChunk", // Worldgen chunk loading malding. "BiomeSelection", // Whaddya know, requires config. "ActivatableUI", // Requires enum key }; - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); var componentFactory = server.ResolveDependency(); @@ -445,8 +442,6 @@ namespace Content.IntegrationTests.Tests } }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs b/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs index 01f52f7d83..7a486e6839 100644 --- a/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs @@ -1,9 +1,10 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Shared.Explosion; namespace Content.IntegrationTests.Tests.Explosion; -public sealed class ExplosionPrototypeTest +public sealed class ExplosionPrototypeTest : GameTest { private static string[] _explosionKinds = GameDataScrounger.PrototypesOfKind(); @@ -13,7 +14,7 @@ public sealed class ExplosionPrototypeTest [Description("Ensures various properties of ExplosionPrototype are correctly configured.")] public async Task Validate(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; @@ -40,7 +41,5 @@ public sealed class ExplosionPrototypeTest Assert.That(proto.IntensityPerState, Is.Positive); Assert.That(proto.FireStates, Is.Positive); } - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs b/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs index 99354e16c1..142ec21ddb 100644 --- a/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs +++ b/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Prototypes; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests; /// Tests to see if any entity prototypes specify solution fill level sprites that don't exist. /// [TestFixture] -public sealed class FillLevelSpriteTest +public sealed class FillLevelSpriteTest : GameTest { private static readonly string[] HandStateNames = ["left", "right"]; private static readonly string[] EquipStateNames = ["back", "suitstorage"]; @@ -20,7 +21,7 @@ public sealed class FillLevelSpriteTest [Test] public async Task FillLevelSpritesExist() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var client = pair.Client; var protoMan = client.ResolveDependency(); var componentFactory = client.ResolveDependency(); @@ -101,7 +102,5 @@ public sealed class FillLevelSpriteTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs b/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs index 1afed38966..3d9ff61a2d 100644 --- a/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs @@ -7,12 +7,13 @@ using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; namespace Content.IntegrationTests.Tests.Fluids; [TestFixture] [TestOf(typeof(AbsorbentComponent))] -public sealed class AbsorbentTest +public sealed class AbsorbentTest : GameTest { private const string UserDummyId = "UserDummy"; private const string AbsorbentDummyId = "AbsorbentDummy"; @@ -73,7 +74,7 @@ public sealed class AbsorbentTest [TestCaseSource(nameof(TestCasesToRun))] public async Task AbsorbentOnRefillableTest(TestSolutionCase testCase) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -123,15 +124,12 @@ public sealed class AbsorbentTest Assert.That(VolumeOfPrototypeInComposition(refillableComposition, NonEvaporablePrototypeId), Is.EqualTo(testCase.ExpectedRefillableSolution.VolumeOfNonEvaporable)); }); }); - await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } [TestCaseSource(nameof(TestCasesToRunOnSmallRefillable))] public async Task AbsorbentOnSmallRefillableTest(TestSolutionCase testCase) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -180,9 +178,6 @@ public sealed class AbsorbentTest Assert.That(VolumeOfPrototypeInComposition(refillableComposition, NonEvaporablePrototypeId), Is.EqualTo(testCase.ExpectedRefillableSolution.VolumeOfNonEvaporable)); }); }); - await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } private static FixedPoint2 VolumeOfPrototypeInComposition(Dictionary composition, string prototypeId) diff --git a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs index d6f9bf3598..c073020e9c 100644 --- a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.Fluids.EntitySystems; using Content.Server.Spreader; using Content.Shared.Chemistry.Components; @@ -14,7 +15,7 @@ namespace Content.IntegrationTests.Tests.Fluids; [TestFixture] [TestOf(typeof(SpreaderSystem))] -public sealed class FluidSpill +public sealed class FluidSpill : GameTest { private static PuddleComponent? GetPuddle(IEntityManager entityManager, Entity mapGrid, Vector2i pos) { @@ -36,7 +37,7 @@ public sealed class FluidSpill [Test] public async Task SpillCorner() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -110,7 +111,5 @@ public sealed class FluidSpill } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs index ee2d0cb1f7..71fbefa241 100644 --- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Coordinates; @@ -10,12 +11,12 @@ namespace Content.IntegrationTests.Tests.Fluids { [TestFixture] [TestOf(typeof(PuddleComponent))] - public sealed class PuddleTest + public sealed class PuddleTest : GameTest { [Test] public async Task TilePuddleTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -32,15 +33,12 @@ namespace Content.IntegrationTests.Tests.Fluids Assert.That(spillSystem.TrySpillAt(coordinates, solution, out _), Is.True); }); - await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } [Test] public async Task SpaceNoPuddleTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -69,8 +67,6 @@ namespace Content.IntegrationTests.Tests.Fluids Assert.That(spillSystem.TrySpillAt(coordinates, solution, out _), Is.False); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/FollowerSystemTest.cs b/Content.IntegrationTests/Tests/FollowerSystemTest.cs index f4447426c7..464b3306f2 100644 --- a/Content.IntegrationTests/Tests/FollowerSystemTest.cs +++ b/Content.IntegrationTests/Tests/FollowerSystemTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Shared.Follower; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Map; namespace Content.IntegrationTests.Tests; [TestFixture, TestOf(typeof(FollowerSystem))] -public sealed class FollowerSystemTest +public sealed class FollowerSystemTest : GameTest { /// /// This test ensures that deleting a map while an entity follows another doesn't throw any exceptions. @@ -15,7 +16,7 @@ public sealed class FollowerSystemTest [Test] public async Task FollowerMapDeleteTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -44,6 +45,5 @@ public sealed class FollowerSystemTest entMan.DeleteEntity(mapSys.GetMap(map)); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs index dae3203f9f..97681ad469 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.Cuffs; using Content.Shared.Cuffs.Components; using Content.Shared.Hands.Components; @@ -10,7 +11,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking [TestFixture] [TestOf(typeof(CuffableComponent))] [TestOf(typeof(HandcuffComponent))] - public sealed class HandCuffTest + public sealed class HandCuffTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -40,7 +41,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; EntityUid human; @@ -98,8 +99,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking cuffableSys.TryAddNewCuffs(human, human, secondCuffs, cuffed); Assert.That(cuffed.CuffedHandCount, Is.EqualTo(4), "Player doesn't have correct amount of hands cuffed"); }); - - await pair.CleanReturnAsync(); } private static void AddHand(NetEntity to, IServerConsoleHost host) diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs index ef94cf0f00..3473f1bc9c 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using Content.IntegrationTests.Fixtures; using Robust.Shared.ContentPack; using Robust.Shared.GameObjects; using Robust.Shared.Utility; @@ -11,12 +12,12 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components { [TestFixture] [TestOf(typeof(Server.Entry.IgnoredComponents))] - public sealed class EntityPrototypeComponentsTest + public sealed class EntityPrototypeComponentsTest : GameTest { [Test] public async Task PrototypesHaveKnownComponents() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -100,7 +101,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components if (unknownComponentsClient.Count + unknownComponentsServer.Count + doubleIgnoredComponents.Count == 0) { - await pair.CleanReturnAsync(); Assert.Pass($"Validated {entitiesValidated} entities with {componentsValidated} components in {paths.Length} files."); return; } @@ -131,11 +131,11 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components [Test] public async Task IgnoredComponentsExistInTheCorrectPlaces() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var client = pair.Client; var serverComponents = server.ResolveDependency(); - var ignoredServerNames = Server.Entry.IgnoredComponents.List; + var ignoredServerNames = Content.Server.Entry.IgnoredComponents.List; var clientComponents = client.ResolveDependency(); var failureMessages = ""; @@ -151,7 +151,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components } } Assert.That(failureMessages, Is.Empty); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs index 1e307e9447..3db194eb9e 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Client.UserInterface.Systems.Alerts.Controls; using Content.Client.UserInterface.Systems.Alerts.Widgets; +using Content.IntegrationTests.Fixtures; using Content.Shared.Alert; using Robust.Client.UserInterface; using Robust.Server.Player; @@ -10,16 +11,18 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs { [TestFixture] [TestOf(typeof(AlertsComponent))] - public sealed class AlertsComponentTests + public sealed class AlertsComponentTests : GameTest { + public override PoolSettings PoolSettings => new() + { + Connected = true, + DummyTicker = false + }; + [Test] public async Task AlertsTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -109,8 +112,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs if (entManager.GetComponent(playerUid).EntityPrototype.ID != "MobIpc") // Corvax-IPC Assert.That(alertIDs, Is.SupersetOf(expectedIDs)); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs index 00f028bb6e..1b0677a46c 100644 --- a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs @@ -1,4 +1,7 @@ #nullable enable +using System.Collections.Generic; +using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Antag; using Content.Server.Antag.Components; using Content.Server.Construction.Conditions; @@ -15,17 +18,19 @@ namespace Content.IntegrationTests.Tests.GameRules; // Once upon a time, players in the lobby weren't ever considered eligible for antag roles. // Lets not let that happen again. [TestFixture] -public sealed class AntagPreferenceTest +public sealed class AntagPreferenceTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Connected = true, + InLobby = true + }; + [Test] public async Task TestLobbyPlayersValid() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -115,6 +120,5 @@ public sealed class AntagPreferenceTest // WL-Changes-end await server.WaitPost(() => server.EntMan.DeleteEntity(uid)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs index b9a02339fb..b8efb64b8b 100644 --- a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.GameTicking.Presets; using Content.Shared.CCVar; @@ -9,7 +10,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class FailAndStartPresetTest +public sealed class FailAndStartPresetTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -52,19 +53,21 @@ public sealed class FailAndStartPresetTest - type: TestRule "; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true + }; + /// /// Test that a nuke ops gamemode can start after failing to start once. /// [Test] public async Task FailAndStartTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -115,7 +118,6 @@ public sealed class FailAndStartPresetTest server.CfgMan.SetCVar(CCVars.GameLobbyFallbackEnabled, true); server.CfgMan.SetCVar(CCVars.GameLobbyDefaultPreset, "secret"); server.System().Run = false; - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs index 09538cd30a..4fda5898fa 100644 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Body.Components; using Content.Server.GameTicking; using Content.Server.GameTicking.Presets; @@ -30,24 +31,27 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class NukeOpsTest +public sealed class NukeOpsTest : GameTest { private static readonly ProtoId SyndicateFaction = "Syndicate"; private static readonly ProtoId NanotrasenFaction = "NanoTrasen"; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true + }; + + /// /// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded. /// [Test] public async Task TryStopNukeOpsFromConstantlyFailing() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -261,6 +265,5 @@ public sealed class NukeOpsTest }); ticker.SetGamePreset((GamePresetPrototype?) null); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs index c805d04a71..0bbf2fae4f 100644 --- a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; @@ -9,12 +10,14 @@ namespace Content.IntegrationTests.Tests.GameRules { [TestFixture] [TestOf(typeof(MaxTimeRestartRuleSystem))] - public sealed class RuleMaxTimeRestartTest + public sealed class RuleMaxTimeRestartTest : GameTest { + public override PoolSettings PoolSettings => new() { InLobby = true }; + [Test] public async Task RestartTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { InLobby = true }); + var pair = Pair; var server = pair.Server; Assert.That(server.EntMan.Count(), Is.Zero); @@ -64,8 +67,6 @@ namespace Content.IntegrationTests.Tests.GameRules { Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs b/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs index 5d7ae8efbf..521b043c68 100644 --- a/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs @@ -1,19 +1,22 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class SecretStartsTest +public sealed class SecretStartsTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings { Dirty = true }; + /// /// Tests that when secret is started, all of the game rules it successfully adds are also started. /// [Test] public async Task TestSecretStarts() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Dirty = true }); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -38,7 +41,5 @@ public sealed class SecretStartsTest // End all rules gameTicker.ClearGameRules(); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/StartEndGameRulesTest.cs b/Content.IntegrationTests/Tests/GameRules/StartEndGameRulesTest.cs index bda931397b..1eef1ab0a1 100644 --- a/Content.IntegrationTests/Tests/GameRules/StartEndGameRulesTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/StartEndGameRulesTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Shared.CCVar; using Robust.Shared.Configuration; @@ -7,19 +8,21 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class StartEndGameRulesTest +public sealed class StartEndGameRulesTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + Dirty = true, + DummyTicker = false + }; + /// /// Tests that all game rules can be added/started/ended at the same time without exceptions. /// [Test] public async Task TestAllConcurrent() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); var gameTicker = server.ResolveDependency().GetEntitySystem(); @@ -47,7 +50,5 @@ public sealed class StartEndGameRulesTest gameTicker.ClearGameRules(); Assert.That(!gameTicker.GetAddedGameRules().Any()); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs b/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs index 97fe1c8762..318c4b6b42 100644 --- a/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Antag.Components; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; @@ -17,23 +18,25 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class TraitorRuleTest +public sealed class TraitorRuleTest : GameTest { private const string TraitorGameRuleProtoId = "Traitor"; private const string TraitorAntagRoleName = "Traitor"; private static readonly ProtoId SyndicateFaction = "Syndicate"; private static readonly ProtoId NanotrasenFaction = "NanoTrasen"; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true, + }; + [Test] public async Task TestTraitorObjectives() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings() - { - Dirty = true, - DummyTicker = false, - Connected = true, - InLobby = true, - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; var entMan = server.EntMan; @@ -123,9 +126,6 @@ public sealed class TraitorRuleTest $"MaxDifficulty exceeded! Objectives: {string.Join(", ", mindComp.Objectives.Select(o => FormatObjective(o, entMan)))}"); Assert.That(mindComp.Objectives, Is.Not.Empty, $"No objectives assigned!"); - - - await pair.CleanReturnAsync(); } private static string FormatObjective(Entity entity, IEntityManager entMan) diff --git a/Content.IntegrationTests/Tests/GameTestTests/ContraintTests.cs b/Content.IntegrationTests/Tests/GameTestTests/ContraintTests.cs new file mode 100644 index 0000000000..34a6d6dd9c --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/ContraintTests.cs @@ -0,0 +1,86 @@ +#nullable enable +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.IntegrationTests.NUnit.Constraints; +using Content.IntegrationTests.NUnit.Operators; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +public sealed class ConstraintsTests : GameTest +{ + [Test] + [TestOf(typeof(CompExistsConstraint))] + [Description("Ensures that a freshly spawned entity matches a constraint stating it has MetaData.")] + [RunOnSide(Side.Server)] + public void CompPositive() + { + var ent = SSpawn(null); + + Assert.That(ent, Has.Comp(Server)); + } + + [Test] + [TestOf(typeof(CompOperator))] + [Description("Ensures that NUnit property access works on Comp constraints.")] + [RunOnSide(Side.Server)] + public void CompPropertyAccess() + { + var ent = SSpawn(null); + + Assert.That(ent, + Has + .Comp(Server) + .Property(nameof(MetaDataComponent.EntityDeleted)) + .EqualTo(false) + ); + } + + [Test] + [TestOf(typeof(CompExistsConstraint))] + [Description("Ensures that a freshly spawned entity does not match a constraint stating it has some odd component.")] + [RunOnSide(Side.Server)] + public void CompNegative() + { + var ent = SSpawn(null); + + // Arbitrary pick. + Assert.That(ent, Has.No.Comp(Server)); + } + + [Test] + [TestOf(typeof(LifeStageConstraint))] + [Description("Ensures that a freshly deleted entity is deleted to constraints.")] + [RunOnSide(Side.Server)] + public void DeletedPositive() + { + var ent = SSpawn(null); + + SDeleteNow(ent); + + Assert.That(ent, Is.Deleted(Server)); + } + + [Test] + [TestOf(typeof(LifeStageConstraint))] + [RunOnSide(Side.Server)] + [Description("Entities that never existed are currently considered deleted by constraints.")] + public void DeletedNeverExisted() + { + // We'll never spawn this many ents in tests without it taking all damn day. + var ent = new EntityUid(int.MaxValue / 2); + + Assert.That(ent, Is.Deleted(Server), "Entites that never existed still count as deleted."); + } + + [Test] + [TestOf(typeof(LifeStageConstraint))] + [Description("Entities that are alive do not count as deleted.")] + [RunOnSide(Side.Server)] + public void DeletedNegative() + { + var ent = SSpawn(null); + + Assert.That(ent, Is.Not.Deleted(Server)); + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/DependencyTests.cs b/Content.IntegrationTests/Tests/GameTestTests/DependencyTests.cs new file mode 100644 index 0000000000..5ce2b486fd --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/DependencyTests.cs @@ -0,0 +1,54 @@ +#nullable enable +using System.Collections.Generic; +using Content.Client.GameTicking.Managers; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.IntegrationTests.NUnit.Constraints; +using Content.Server.GameTicking; +using Content.Shared.GameTicking; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(SidedDependencyAttribute))] +public sealed class DependencyTests : GameTest +{ + [SidedDependency(Side.Server)] private readonly SharedGameTicker _sGameTicker = null!; + [SidedDependency(Side.Client)] private readonly SharedGameTicker _cGameTicker = null!; + [SidedDependency(Side.Server)] private readonly EntityQuery _sXformQuery = default!; + + [Test] + [Description("Asserts that sided dependencies actually grab from the right sides.")] + public void DependenciesRespectSides() + { + using (Assert.EnterMultipleScope()) + { + Assert.That(!ReferenceEquals(SEntMan, CEntMan), "Server and client entity managers should be distinct"); + Assert.That(SEntMan, Is.EqualTo(Server.EntMan).Using(ReferenceEqualityComparer.Instance)); + Assert.That(CEntMan, Is.EqualTo(Client.EntMan).Using(ReferenceEqualityComparer.Instance)); + } + } + + [Test] + [Description("Asserts that system dependencies actually grab from the right sides.")] + public void SystemDependenciesRespectSides() + { + using (Assert.EnterMultipleScope()) + { + Assert.That(!ReferenceEquals(_sGameTicker, _cGameTicker), + "Server and client gametickers should be distinct"); + Assert.That(_sGameTicker, Is.TypeOf()); + Assert.That(_cGameTicker, Is.TypeOf()); + } + } + + [Test] + [Description("Asserts that query dependencies function")] + public async Task QueryDependencies() + { + var ent = await Spawn(null); + + Assert.That(_sXformQuery.HasComp(ent), Is.True); + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/DisconnectedDependencyTest.cs b/Content.IntegrationTests/Tests/GameTestTests/DisconnectedDependencyTest.cs new file mode 100644 index 0000000000..d71c56c313 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/DisconnectedDependencyTest.cs @@ -0,0 +1,46 @@ +#nullable enable +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC.Exceptions; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(SidedDependencyAttribute))] +public sealed class DisconnectedDependencyTest : GameTest +{ + [Test] + [PairConfig(nameof(PsDisconnected))] + [Description($""" + Ensures that GameTest can be started up even when the client {nameof(EntitySystemManager)} isn't initialized yet. + No body is necessary, if this fails then the bug is firmly in GameTest itself. + """)] + public void EnsureGameTestSetupWorksDisconnected() + { + // Nothin' + } + + [Test] + [PairConfig(nameof(PsDisconnected))] + [Description(""" + Ensures dependency injection that relies on client side systems fails as expected when the client is detached. + """)] + public void ClientSystemDependencyFails() + { + var creature = new Creature(); + + Assert.Throws(() => + { + InjectDependencies(creature); + }); + } + + private sealed class Creature + { +#pragma warning disable CS0414 // Field is assigned but its value is never used + [SidedDependency(Side.Client)] private readonly MapSystem _mapSys = null!; +#pragma warning restore CS0414 // Field is assigned but its value is never used + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarTest.cs b/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarTest.cs new file mode 100644 index 0000000000..942254807f --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarTest.cs @@ -0,0 +1,53 @@ +#nullable enable +using System.Linq; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Robust.Shared.Configuration; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(EnsureCVarAttribute))] +[Description("Ensures EnsureCVar actually sets CVars as expected.")] +[EnsureCVar(Side.Server, typeof(EnsureCVarsTestCVars), nameof(EnsureCVarsTestCVars.Foo), true)] +public sealed class EnsureCVarTest : GameTest +{ + [SidedDependency(Side.Server)] private readonly IConfigurationManager _sCfg = default!; + [SidedDependency(Side.Client)] private readonly IConfigurationManager _cCfg = default!; + + [Test] + [Description("Ensure Foo is set and Bar is not.")] + public void FooIsSet() + { + using (Assert.EnterMultipleScope()) + { + Assert.That(_sCfg.GetCVar(EnsureCVarsTestCVars.Foo), Is.True); + Assert.That(_cCfg.GetCVar(EnsureCVarsTestCVars.Foo), + Is.EqualTo(EnsureCVarsTestCVars.Foo.DefaultValue), + "Foo is not replicated and should not be set on the client."); + + Assert.That(_sCfg.GetCVar(EnsureCVarsTestCVars.Bar), + Is.EqualTo(EnsureCVarsTestCVars.Bar.DefaultValue)); + } + } + + [Test] + [EnsureCVar(Side.Server, typeof(EnsureCVarsTestCVars), nameof(EnsureCVarsTestCVars.Bar), 42)] + [Description("Ensure Foo and Bar are set.")] + public void BarIsSet() + { + var props = TestContext.CurrentContext.Test.Properties; + using (Assert.EnterMultipleScope()) + { + Assert.That(_sCfg.GetCVar(EnsureCVarsTestCVars.Bar), + Is.EqualTo(42)); + Assert.That(_cCfg.GetCVar(EnsureCVarsTestCVars.Bar), + Is.EqualTo(EnsureCVarsTestCVars.Bar.DefaultValue), + "Bar is not replicated and should not be set on the client."); + + Assert.That(props[EnsureCVarAttribute.ServerEnsuredCVarsProperty].Count(), + Is.EqualTo(1), + "Expected EnsureCVar to appropriately mark its target test."); + } + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarsTestCVars.cs b/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarsTestCVars.cs new file mode 100644 index 0000000000..3d7afccd57 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarsTestCVars.cs @@ -0,0 +1,14 @@ +#nullable enable +using Robust.Shared.Configuration; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[CVarDefs] +public sealed class EnsureCVarsTestCVars +{ + public static readonly CVarDef Foo = + CVarDef.Create("tests.ensure_cvars.foo", false, CVar.SERVER); + + public static readonly CVarDef Bar = + CVarDef.Create("tests.ensure_cvars.bar", 3, CVar.SERVER); +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/IGameTestPairConfigModifierAttributeTests.cs b/Content.IntegrationTests/Tests/GameTestTests/IGameTestPairConfigModifierAttributeTests.cs new file mode 100644 index 0000000000..080c143b8a --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/IGameTestPairConfigModifierAttributeTests.cs @@ -0,0 +1,23 @@ +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(IGameTestPairConfigModifier))] +public sealed class IGameTestPairConfigModifierAttributeTests : GameTest +{ + [Test] + public void Control() + { + Assert.That(Pair.Settings.Connected, Is.True); + } + + [Test] + [PairConfig(nameof(PsDisconnected))] + [Description("Ensures pair settings apply.")] + public void PairConfigWorks() + { + Assert.That(Pair.Settings.Connected, Is.False); + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/RunOnSideTests.cs b/Content.IntegrationTests/Tests/GameTestTests/RunOnSideTests.cs new file mode 100644 index 0000000000..1a3de26514 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/RunOnSideTests.cs @@ -0,0 +1,43 @@ +#nullable enable +using System.Threading; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(RunOnSideAttribute))] +[Description("Asserts that RunOnSide actually does as expected and runs the test on the given side.")] +public sealed class RunOnSideTests : GameTest +{ + [Test] + [Description("Ensures that the default scenario is the test thread.")] + public void Control() + { + Assert.That(Thread.CurrentThread, Is.Not.EqualTo(ServerThread).And.Not.EqualTo(ClientThread)); + } + + [Test] + [RunOnSide(Side.Server)] + public void TestServerSide() + { + Assert.That(Thread.CurrentThread, Is.EqualTo(ServerThread)); + } + + [Test] + [RunOnSide(Side.Client)] + public void TestClientSide() + { + Assert.That(Thread.CurrentThread, Is.EqualTo(ClientThread)); + } + + [Test] + [RunOnSide(Side.Server)] + [Description("Ensures that RunOnSide appropriately adds a property.")] + [Ignore("TestContext on the game threads is broken.")] + [TrackingIssue("https://github.com/space-wizards/RobustToolbox/issues/6449")] + public void TestProperty() + { + Assert.That(TestContext.CurrentContext.Test.Properties.Get(RunOnSideAttribute.RunOnSideProperty), Is.Not.Null); + } +} diff --git a/Content.IntegrationTests/Tests/Gibbing/GibTest.cs b/Content.IntegrationTests/Tests/Gibbing/GibTest.cs index ee0f7a742d..23be8f028a 100644 --- a/Content.IntegrationTests/Tests/Gibbing/GibTest.cs +++ b/Content.IntegrationTests/Tests/Gibbing/GibTest.cs @@ -1,16 +1,17 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Shared.Gibbing; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Body; [TestFixture] -public sealed class GibTest +public sealed class GibTest : GameTest { [Test] public async Task TestGib() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var (server, client) = (pair.Server, pair.Client); var map = await pair.CreateTestMap(); @@ -30,7 +31,5 @@ public sealed class GibTest await pair.RunTicksSync(5); Assert.That(!client.EntMan.EntityExists(nuid)); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs index 0951e7e260..0ef52a12a6 100644 --- a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs +++ b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Gravity; using Content.Shared.Alert; using Content.Shared.Gravity; @@ -8,7 +9,7 @@ namespace Content.IntegrationTests.Tests.Gravity [TestFixture] [TestOf(typeof(GravitySystem))] [TestOf(typeof(GravityGeneratorComponent))] - public sealed class WeightlessStatusTests + public sealed class WeightlessStatusTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -38,7 +39,7 @@ namespace Content.IntegrationTests.Tests.Gravity [Test] public async Task WeightlessStatusTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -86,8 +87,6 @@ namespace Content.IntegrationTests.Tests.Gravity }); await pair.RunTicksSync(10); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/GravityGridTest.cs b/Content.IntegrationTests/Tests/GravityGridTest.cs index 047ec0259a..10804ea201 100644 --- a/Content.IntegrationTests/Tests/GravityGridTest.cs +++ b/Content.IntegrationTests/Tests/GravityGridTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Power.Components; using Content.Shared.Gravity; using Robust.Shared.GameObjects; @@ -11,7 +12,7 @@ namespace Content.IntegrationTests.Tests /// making sure that gravity is applied to the correct grids. [TestFixture] [TestOf(typeof(GravityGeneratorComponent))] - public sealed class GravityGridTest + public sealed class GravityGridTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -31,7 +32,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -96,8 +97,6 @@ namespace Content.IntegrationTests.Tests Assert.That(entityMan.GetComponent(grid2).Enabled, Is.False); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs b/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs index dec2c40c0a..98b7056d00 100644 --- a/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs +++ b/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Content.Client.Guidebook; using Content.Client.Guidebook.Richtext; +using Content.IntegrationTests.Fixtures; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -13,7 +14,7 @@ namespace Content.IntegrationTests.Tests.Guidebook; /// [TestFixture] [TestOf(typeof(DocumentParsingManager))] -public sealed class DocumentParsingTest +public sealed class DocumentParsingTest : GameTest { public string TestDocument = @"multiple @@ -45,7 +46,7 @@ whitespace before newlines are ignored. [Test] public async Task ParseTestDocument() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var client = pair.Client; await client.WaitIdleAsync(); var parser = client.ResolveDependency(); @@ -133,8 +134,6 @@ whitespace before newlines are ignored. subTest2.Params.TryGetValue("k", out val); Assert.That(val, Is.EqualTo(@"<>\>=""=<-_?*3.0//")); - - await pair.CleanReturnAsync(); } public sealed class TestControl : Control, IDocumentTag diff --git a/Content.IntegrationTests/Tests/Guidebook/GuideEntryPrototypeTests.cs b/Content.IntegrationTests/Tests/Guidebook/GuideEntryPrototypeTests.cs index 13d0ad1497..016b06abe8 100644 --- a/Content.IntegrationTests/Tests/Guidebook/GuideEntryPrototypeTests.cs +++ b/Content.IntegrationTests/Tests/Guidebook/GuideEntryPrototypeTests.cs @@ -1,5 +1,6 @@ using Content.Client.Guidebook; using Content.Client.Guidebook.Richtext; +using Content.IntegrationTests.Fixtures; using Robust.Shared.ContentPack; using Robust.Shared.Prototypes; using Content.IntegrationTests.Utility; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Guidebook; [TestOf(typeof(GuidebookSystem))] [TestOf(typeof(GuideEntryPrototype))] [TestOf(typeof(DocumentParsingManager))] -public sealed class GuideEntryPrototypeTests +public sealed class GuideEntryPrototypeTests : GameTest { private static string[] _guideEntries = GameDataScrounger.PrototypesOfKind(); @@ -21,7 +22,7 @@ public sealed class GuideEntryPrototypeTests [Description("Ensures a given guidebook entry is valid, checking the document/etc.")] public async Task Validate(string protoKey) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var client = pair.Client; await client.WaitIdleAsync(); var protoMan = client.ResolveDependency(); @@ -36,7 +37,5 @@ public sealed class GuideEntryPrototypeTests Assert.That(parser.TryAddMarkup(new Document(), text), $"Failed to parse the guide entry's document."); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Hands/HandTests.cs b/Content.IntegrationTests/Tests/Hands/HandTests.cs index d5cf75c463..95f053a1cb 100644 --- a/Content.IntegrationTests/Tests/Hands/HandTests.cs +++ b/Content.IntegrationTests/Tests/Hands/HandTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Storage.EntitySystems; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; @@ -10,7 +11,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Hands; [TestFixture] -public sealed class HandTests +public sealed class HandTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -25,14 +26,16 @@ public sealed class HandTests "; + public override PoolSettings PoolSettings => new() + { + Connected = true, + DummyTicker = false + }; + [Test] public async Task TestPickupDrop() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -69,17 +72,12 @@ public sealed class HandTests Assert.That(sys.GetActiveItem((player, hands)), Is.Null); await server.WaitPost(() => mapSystem.DeleteMap(data.MapId)); - await pair.CleanReturnAsync(); } [Test] public async Task TestPickUpThenDropInContainer() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var map = await pair.CreateTestMap(); await pair.RunTicksSync(5); @@ -134,6 +132,5 @@ public sealed class HandTests Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform))); await server.WaitPost(() => mapSystem.DeleteMap(map.MapId)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs b/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs index 929a231159..accdd4667c 100644 --- a/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs +++ b/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Inventory; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ namespace Content.IntegrationTests.Tests // i.e. the interaction between uniforms and the pocket/ID slots. // and also how big items don't fit in pockets. [TestFixture] - public sealed class HumanInventoryUniformSlotsTest + public sealed class HumanInventoryUniformSlotsTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -55,7 +56,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; @@ -130,8 +131,6 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } private static bool IsDescendant(EntityUid descendant, EntityUid parent, IEntityManager entManager) diff --git a/Content.IntegrationTests/Tests/Humanoid/HideablePrototypeValidation.cs b/Content.IntegrationTests/Tests/Humanoid/HideablePrototypeValidation.cs index d95992bda2..4ee65c9c32 100644 --- a/Content.IntegrationTests/Tests/Humanoid/HideablePrototypeValidation.cs +++ b/Content.IntegrationTests/Tests/Humanoid/HideablePrototypeValidation.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Body; using Content.Shared.Clothing.Components; using Content.Shared.Humanoid; @@ -8,13 +9,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Humanoid; [TestFixture] -public sealed class HideablePrototypeValidation +public sealed class HideablePrototypeValidation : GameTest { [Test] public async Task NoOrgansWithoutClothing() { - await using var pair = await PoolManager.GetServerClient(); - + var pair = Pair; var requirements = new Dictionary>(); foreach (var (proto, component) in pair.GetPrototypesWithComponent()) { @@ -42,14 +42,12 @@ public sealed class HideablePrototypeValidation { Assert.That(provided, Does.Contain(key), $"No clothing will hide {key} that can be hidden on {string.Join(", ", requirement.Select(it => it.Id))}"); } - - await pair.CleanReturnAsync(); } [Test] public async Task NoClothingWithoutOrgans() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var requirements = new Dictionary>(); foreach (var (proto, component) in pair.GetPrototypesWithComponent()) @@ -74,7 +72,5 @@ public sealed class HideablePrototypeValidation { Assert.That(provided, Does.Contain(key), $"No organ will hide {key} that can be hidden by {string.Join(", ", requirement.Select(it => it.Id))}"); } - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Humanoid/HumanoidProfileTests.cs b/Content.IntegrationTests/Tests/Humanoid/HumanoidProfileTests.cs index 1e4a094b79..48eb521c5e 100644 --- a/Content.IntegrationTests/Tests/Humanoid/HumanoidProfileTests.cs +++ b/Content.IntegrationTests/Tests/Humanoid/HumanoidProfileTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences; @@ -10,14 +11,14 @@ namespace Content.IntegrationTests.Tests.Humanoid; [TestFixture] [TestOf(typeof(HumanoidProfileSystem))] -public sealed class HumanoidProfileTests +public sealed class HumanoidProfileTests : GameTest { private static readonly ProtoId Vox = "Vox"; [Test] public async Task EnsureValidLoading() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -43,7 +44,5 @@ public sealed class HumanoidProfileTests Assert.That(voiceComponent.Sounds, Is.Not.Null, message: "the MobHuman spawned by this test needs to have sex-specific sound set"); Assert.That(voiceComponent.Sounds![Sex.Female], Is.EqualTo(voiceComponent.EmoteSounds)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs index 6ac40e92a1..e4bd4615c5 100644 --- a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs @@ -1,5 +1,6 @@ #nullable enable annotations using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Interaction; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; @@ -16,7 +17,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click { [TestFixture] [TestOf(typeof(InteractionSystem))] - public sealed class InteractionSystemTests + public sealed class InteractionSystemTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -40,7 +41,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click [Test] public async Task InteractionTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -101,13 +102,12 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } [Test] public async Task InteractionObstructionTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -168,13 +168,12 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } [Test] public async Task InteractionInRangeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -234,14 +233,13 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } [Test] public async Task InteractionOutOfRangeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -300,13 +298,12 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } [Test] public async Task InsideContainerInteractionBlockTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -388,7 +385,6 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } public sealed class TestInteractionSystem : EntitySystem diff --git a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs index 801433ae72..966f13675d 100644 --- a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs +++ b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Shared.Interaction; using Robust.Server.GameObjects; using Robust.Shared.Containers; @@ -10,7 +11,7 @@ namespace Content.IntegrationTests.Tests.Interaction { [TestFixture] [TestOf(typeof(SharedInteractionSystem))] - public sealed class InRangeUnobstructed + public sealed class InRangeUnobstructed : GameTest { private const string HumanId = "MobHuman"; @@ -27,7 +28,7 @@ namespace Content.IntegrationTests.Tests.Interaction [Test] public async Task EntityEntityTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -109,8 +110,6 @@ namespace Content.IntegrationTests.Tests.Interaction Assert.That(interactionSys.InRangeUnobstructed(mapCoordinates, origin, InteractionRangeDivided15Times3)); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 62a03b0abe..5231055c69 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -175,7 +175,7 @@ public abstract partial class InteractionTest [SetUp] public virtual async Task Setup() { - Pair = await PoolManager.GetServerClient(Settings); + Pair = await PoolManager.GetServerClient(Settings, new NUnitTestContextWrap(TestContext.CurrentContext, TestContext.Out)); // server dependencies SEntMan = Server.ResolveDependency(); diff --git a/Content.IntegrationTests/Tests/Internals/AutoInternalsTests.cs b/Content.IntegrationTests/Tests/Internals/AutoInternalsTests.cs index d153536873..d2f67e9830 100644 --- a/Content.IntegrationTests/Tests/Internals/AutoInternalsTests.cs +++ b/Content.IntegrationTests/Tests/Internals/AutoInternalsTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Systems; using Content.Server.Station.Systems; @@ -7,12 +8,12 @@ namespace Content.IntegrationTests.Tests.Internals; [TestFixture] [TestOf(typeof(InternalsSystem))] -public sealed class AutoInternalsTests +public sealed class AutoInternalsTests : GameTest { [Test] public async Task TestInternalsAutoActivateInSpaceForStationSpawn() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -31,14 +32,12 @@ public sealed class AutoInternalsTests server.EntMan.DeleteEntity(dummy); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestInternalsAutoActivateInSpaceForEntitySpawn() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -55,8 +54,6 @@ public sealed class AutoInternalsTests server.EntMan.DeleteEntity(dummy); }); - - await pair.CleanReturnAsync(); } [TestPrototypes] diff --git a/Content.IntegrationTests/Tests/InventoryHelpersTest.cs b/Content.IntegrationTests/Tests/InventoryHelpersTest.cs index 39761ad089..c841e23365 100644 --- a/Content.IntegrationTests/Tests/InventoryHelpersTest.cs +++ b/Content.IntegrationTests/Tests/InventoryHelpersTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Stunnable; using Content.Shared.Inventory; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Map; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class InventoryHelpersTest + public sealed class InventoryHelpersTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -39,7 +40,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task SpawnItemInSlotTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -87,8 +88,6 @@ namespace Content.IntegrationTests.Tests #pragma warning restore NUnit2045 sEntities.DeleteEntity(human); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Lathe/LatheTest.cs b/Content.IntegrationTests/Tests/Lathe/LatheTest.cs index c335f8d6c8..f0a5c04237 100644 --- a/Content.IntegrationTests/Tests/Lathe/LatheTest.cs +++ b/Content.IntegrationTests/Tests/Lathe/LatheTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Lathe; using Content.Shared.Materials; using Content.Shared.Prototypes; @@ -11,12 +12,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Lathe; [TestFixture] -public sealed class LatheTest +public sealed class LatheTest : GameTest { [Test] public async Task TestLatheRecipeIngredientsFitLathe() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapData = await pair.CreateTestMap(); @@ -111,14 +112,12 @@ public sealed class LatheTest } }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task AllLatheRecipesValidTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var proto = server.ProtoMan; @@ -131,7 +130,5 @@ public sealed class LatheTest Assert.That(recipe.ResultReagents, Is.Not.Null, $"Recipe '{recipe.ID}' has no result or result reagents."); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs index b75b81ab3c..bd35c1f08e 100644 --- a/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs +++ b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Tag; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -11,12 +12,12 @@ namespace Content.IntegrationTests.Tests.Linter; /// Verify that the yaml linter successfully validates static fields /// [TestFixture] -public sealed class StaticFieldValidationTest +public sealed class StaticFieldValidationTest : GameTest { [Test] public async Task TestStaticFieldValidation() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoMan = pair.Server.ProtoMan; var protos = new Dictionary>(); @@ -49,8 +50,6 @@ public sealed class StaticFieldValidationTest Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos), Has.Count.EqualTo(2)); Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetInvalid), protos), Has.Count.EqualTo(2)); Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayInvalid), protos), Has.Count.EqualTo(2)); - - await pair.CleanReturnAsync(); } [TestPrototypes] diff --git a/Content.IntegrationTests/Tests/Lobby/CharacterCreationTest.cs b/Content.IntegrationTests/Tests/Lobby/CharacterCreationTest.cs index 2fa3c9961c..fb48797616 100644 --- a/Content.IntegrationTests/Tests/Lobby/CharacterCreationTest.cs +++ b/Content.IntegrationTests/Tests/Lobby/CharacterCreationTest.cs @@ -1,4 +1,5 @@ using Content.Client.Lobby; +using Content.IntegrationTests.Fixtures; using Content.Server.Preferences.Managers; using Content.Shared.Humanoid; using Content.Shared.Preferences; @@ -9,12 +10,14 @@ namespace Content.IntegrationTests.Tests.Lobby; [TestFixture] [TestOf(typeof(ClientPreferencesManager))] [TestOf(typeof(ServerPreferencesManager))] -public sealed class CharacterCreationTest +public sealed class CharacterCreationTest : GameTest { + public override PoolSettings PoolSettings => new() { InLobby = true }; + [Test] public async Task CreateDeleteCreateTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { InLobby = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; var user = pair.Client.User!.Value; @@ -72,7 +75,6 @@ public sealed class CharacterCreationTest serverCharacters = serverPrefManager.GetPreferences(user).Characters; Assert.That(serverCharacters, Has.Count.EqualTo(2)); AssertEqual(serverCharacters[1], profile); - await pair.CleanReturnAsync(); } private void AssertEqual(HumanoidCharacterProfile a, HumanoidCharacterProfile b) diff --git a/Content.IntegrationTests/Tests/Lobby/ServerReloginTest.cs b/Content.IntegrationTests/Tests/Lobby/ServerReloginTest.cs index 3dc2075887..e59bc269ad 100644 --- a/Content.IntegrationTests/Tests/Lobby/ServerReloginTest.cs +++ b/Content.IntegrationTests/Tests/Lobby/ServerReloginTest.cs @@ -1,20 +1,23 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.CCVar; using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Network; namespace Content.IntegrationTests.Tests.Lobby; -public sealed class ServerReloginTest +public sealed class ServerReloginTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + Connected = true, + DummyTicker = false + }; + [Test] public async Task Relogin() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; var originalMaxPlayers = 0; @@ -62,7 +65,5 @@ public sealed class ServerReloginTest //Put the cvar back, so other tests can still use this server serverConfig.SetCVar(CCVars.SoftMaxPlayers, originalMaxPlayers); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Localization/EntityPrototypeLocalizationTest.cs b/Content.IntegrationTests/Tests/Localization/EntityPrototypeLocalizationTest.cs index 69d44fd08b..1b8fa16543 100644 --- a/Content.IntegrationTests/Tests/Localization/EntityPrototypeLocalizationTest.cs +++ b/Content.IntegrationTests/Tests/Localization/EntityPrototypeLocalizationTest.cs @@ -1,9 +1,10 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.Localization; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Localization; -public sealed class EntityPrototypeLocalizationTest +public sealed class EntityPrototypeLocalizationTest : GameTest { /// /// An explanation of why LocIds should not be used for entity prototype names/descriptions. @@ -18,7 +19,7 @@ public sealed class EntityPrototypeLocalizationTest [Test] public async Task TestNoManualEntityLocStrings() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; var locMan = server.ResolveDependency(); @@ -44,7 +45,5 @@ public sealed class EntityPrototypeLocalizationTest } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs b/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs index 05f98c3d19..14aeaf648c 100644 --- a/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Dataset; using Robust.Shared.Localization; using Robust.Shared.Prototypes; @@ -6,12 +7,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Localization; [TestFixture] -public sealed class LocalizedDatasetPrototypeTest +public sealed class LocalizedDatasetPrototypeTest : GameTest { [Test] public async Task ValidProtoIdsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -36,7 +37,5 @@ public sealed class LocalizedDatasetPrototypeTest Assert.That(localizationMan.HasString(nextId), Is.False, $"LocalizedDataset {proto.ID} with prefix \"{proto.Values.Prefix}\" specifies {proto.Values.Count} entries, but a localized string exists with ID {nextId}! Does count need to be raised?"); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/MachineBoardTest.cs b/Content.IntegrationTests/Tests/MachineBoardTest.cs index e1533bbb8d..9554357ece 100644 --- a/Content.IntegrationTests/Tests/MachineBoardTest.cs +++ b/Content.IntegrationTests/Tests/MachineBoardTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Construction.Components; using Content.Shared.Construction.Components; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; -public sealed class MachineBoardTest +public sealed class MachineBoardTest : GameTest { /// /// A list of machine boards that can be ignored by this test. @@ -32,7 +33,7 @@ public sealed class MachineBoardTest [Test] public async Task TestMachineBoardHasValidMachine() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -60,8 +61,6 @@ public sealed class MachineBoardTest }); } }); - - await pair.CleanReturnAsync(); } /// @@ -71,7 +70,7 @@ public sealed class MachineBoardTest [Test] public async Task TestComputerBoardHasValidComputer() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -100,8 +99,6 @@ public sealed class MachineBoardTest }); } }); - - await pair.CleanReturnAsync(); } /// @@ -111,7 +108,7 @@ public sealed class MachineBoardTest [Test] public async Task TestValidateBoardComponentRequirements() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -136,7 +133,5 @@ public sealed class MachineBoardTest }); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs b/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs index 6d48a668a5..43fea6d6c0 100644 --- a/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs +++ b/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Content.Client.Weapons.Ranged.Components; +using Content.IntegrationTests.Fixtures; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; @@ -9,12 +10,12 @@ namespace Content.IntegrationTests.Tests; /// Tests all entity prototypes with the MagazineVisualsComponent. /// [TestFixture] -public sealed class MagazineVisualsSpriteTest +public sealed class MagazineVisualsSpriteTest : GameTest { [Test] public async Task MagazineVisualsSpritesExist() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var client = pair.Client; var toTest = new List<(int, string)>(); var protos = pair.GetPrototypesWithComponent(); @@ -67,7 +68,5 @@ public sealed class MagazineVisualsSpriteTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs index be8bad229b..0bf637cf19 100644 --- a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs +++ b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -5,15 +6,18 @@ using Robust.Shared.Map; namespace Content.IntegrationTests.Tests.Mapping; [TestFixture] -public sealed class MappingTests +public sealed class MappingTests : GameTest { + public override PoolSettings PoolSettings => + new() { Dirty = true, Connected = true, DummyTicker = false }; + /// /// Checks that the mapping command creates paused & uninitialized maps. /// [Test] public async Task MappingTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Dirty = true, Connected = true, DummyTicker = false }); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -97,6 +101,5 @@ public sealed class MappingTests Assert.That(server.MetaData(ent).EntityPaused, Is.True); await server.WaitPost(() => entMan.DeleteEntity(map)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/MappingEditorTest.cs b/Content.IntegrationTests/Tests/MappingEditorTest.cs index bd930aef9e..987fe7ad73 100644 --- a/Content.IntegrationTests/Tests/MappingEditorTest.cs +++ b/Content.IntegrationTests/Tests/MappingEditorTest.cs @@ -1,19 +1,17 @@ using Content.Client.Gameplay; using Content.Client.Mapping; +using Content.IntegrationTests.Fixtures; using Robust.Client.State; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class MappingEditorTest +public sealed class MappingEditorTest : GameTest { [Test] public async Task StopHardCodingWidgetsJesusChristTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true - }); + var pair = Pair; var client = pair.Client; var state = client.ResolveDependency(); @@ -35,7 +33,5 @@ public sealed class MappingEditorTest state.RequestStateChange(); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs b/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs index c81ff6a698..af5c6c2018 100644 --- a/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs +++ b/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Shared.Body; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Markings; @@ -9,7 +10,7 @@ namespace Content.IntegrationTests.Tests.Markings; [TestFixture] [TestOf(typeof(MarkingManager))] -public sealed class MarkingManagerTests +public sealed class MarkingManagerTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -76,7 +77,7 @@ public sealed class MarkingManagerTests [Test] public async Task HairConvesion() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -96,14 +97,12 @@ public sealed class MarkingManagerTests Assert.That(hairMarkings[0].MarkingId, Is.EqualTo("HumanHairLongBedhead2")); Assert.That(hairMarkings[0].MarkingColors[0], Is.EqualTo(Color.Red)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task LimitsFilling() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -118,14 +117,12 @@ public sealed class MarkingManagerTests Assert.That(dict[HumanoidVisualLayers.Eyes], Has.Count.EqualTo(1)); Assert.That(dict[HumanoidVisualLayers.Eyes][0].MarkingId, Is.EqualTo("EyesMarking")); }); - - await pair.CleanReturnAsync(); } [Test] public async Task LimitsTruncations() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -146,14 +143,12 @@ public sealed class MarkingManagerTests Assert.That(dict[HumanoidVisualLayers.Eyes], Has.Count.EqualTo(1)); Assert.That(dict[HumanoidVisualLayers.Eyes][0].MarkingId, Is.EqualTo("MenOnlyMarking")); }); - - await pair.CleanReturnAsync(); } [Test] public async Task EnsureValidGroupAndSex() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -191,14 +186,12 @@ public sealed class MarkingManagerTests Assert.That(testingMenMarkings[HumanoidVisualLayers.Eyes][1].MarkingId, Is.EqualTo("TestingOnlyMarking")); Assert.That(testingMenMarkings[HumanoidVisualLayers.Eyes][2].MarkingId, Is.EqualTo("TestingMenOnlyMarking")); }); - - await pair.CleanReturnAsync(); } [Test] public async Task EnsureValidColors() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -232,7 +225,5 @@ public sealed class MarkingManagerTests Assert.That(eyeMarkings[1].MarkingColors[0], Is.EqualTo(Color.Red)); Assert.That(eyeMarkings[3].MarkingColors[0], Is.EqualTo(Color.Green)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Markings/MarkingsViewModelTests.cs b/Content.IntegrationTests/Tests/Markings/MarkingsViewModelTests.cs index f69d3356c2..026d2ecf3b 100644 --- a/Content.IntegrationTests/Tests/Markings/MarkingsViewModelTests.cs +++ b/Content.IntegrationTests/Tests/Markings/MarkingsViewModelTests.cs @@ -59,7 +59,7 @@ public sealed class MarkingsViewModelTests [SetUp] public async Task SetUp() { - Pair = await PoolManager.GetServerClient(); + Pair = await PoolManager.GetServerClient(testContext: new NUnitTestContextWrap(TestContext.CurrentContext, TestContext.Out)); await Client.WaitPost(() => { Model = new MarkingsViewModel(); diff --git a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs index 3c6c372b75..460146357c 100644 --- a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs +++ b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.Cargo.Systems; using Content.Server.Construction.Completions; using Content.Server.Construction.Components; @@ -26,7 +27,7 @@ namespace Content.IntegrationTests.Tests; /// create them. /// [TestFixture] -public sealed class MaterialArbitrageTest +public sealed class MaterialArbitrageTest : GameTest { // These sets are for selectively excluding recipes from arbitrage. // You should NOT be adding to these. They exist here for downstreams and potential future issues. @@ -36,7 +37,7 @@ public sealed class MaterialArbitrageTest [Test] public async Task NoMaterialArbitrage() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -439,7 +440,6 @@ public sealed class MaterialArbitrageTest }); await server.WaitPost(() => mapSystem.DeleteMap(testMap.MapId)); - await pair.CleanReturnAsync(); async Task GetSpawnedPrice(Dictionary ents) { diff --git a/Content.IntegrationTests/Tests/Materials/MaterialTests.cs b/Content.IntegrationTests/Tests/Materials/MaterialTests.cs index a177869e7f..43c7dab2b0 100644 --- a/Content.IntegrationTests/Tests/Materials/MaterialTests.cs +++ b/Content.IntegrationTests/Tests/Materials/MaterialTests.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.Stack; using Content.Shared.Stacks; using Content.Shared.Materials; @@ -14,12 +15,12 @@ namespace Content.IntegrationTests.Tests.Materials [TestFixture] [TestOf(typeof(StackSystem))] [TestOf(typeof(MaterialPrototype))] - public sealed class MaterialPrototypeSpawnsStackMaterialTest + public sealed class MaterialPrototypeSpawnsStackMaterialTest : GameTest { [Test] public async Task MaterialPrototypeSpawnsStackMaterial() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -60,8 +61,6 @@ namespace Content.IntegrationTests.Tests.Materials mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs index 32fcf9c1ad..7369fece35 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; using Content.Shared.Ghost; @@ -12,7 +13,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed class GhostRoleTests +public sealed class GhostRoleTests : GameTest { private const string GhostRoleProtoId = "GhostRoleTestEntity"; private const string TestMobProtoId = "GhostRoleTestMob"; @@ -33,6 +34,13 @@ public sealed class GhostRoleTests - type: MobState # MobState is required for correct determination of if the player can return to body or not """; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + DummyTicker = false, + Connected = true + }; + /// /// This is a simple test that just checks if a player can take a ghost role and then regain control of their /// original entity without encountering errors. @@ -43,12 +51,7 @@ public sealed class GhostRoleTests { var ghostCommand = adminGhost ? "aghost" : "ghost"; - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -210,7 +213,6 @@ public sealed class GhostRoleTests if (!adminGhost) { // End of the normal player ghost role test - await pair.CleanReturnAsync(); return; } @@ -242,7 +244,5 @@ public sealed class GhostRoleTests // Check that there is are no lingereing ghosts Assert.That(entMan.Count(), Is.Zero); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/GhostTests.cs b/Content.IntegrationTests/Tests/Minds/GhostTests.cs index 3a860267e5..26991c08ad 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostTests.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Pair; using Content.Shared.Ghost; using Content.Shared.Mind; @@ -12,7 +13,7 @@ using Robust.UnitTesting; namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed class GhostTests +public sealed class GhostTests : GameTest { private struct GhostTestData { @@ -45,17 +46,20 @@ public sealed class GhostTests } } + // Client is needed to create a session for the ghost system. Creating a dummy session was too difficult. + public override PoolSettings PoolSettings => new() + { + DummyTicker = false, + Connected = true, + Dirty = true + }; + private async Task SetupData() { var data = new GhostTestData { - // Client is needed to create a session for the ghost system. Creating a dummy session was too difficult. - Pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }) + // ..Just use the gametest pair, please. + Pair = Pair, }; data.SEntMan = data.Pair.Server.ResolveDependency(); @@ -126,8 +130,6 @@ public sealed class GhostTests // Ensure the position is the same var ghostPosition = data.SEntMan.GetComponent(ghost).Coordinates; Assert.That(ghostPosition, Is.EqualTo(oldPosition)); - - await data.Pair.CleanReturnAsync(); } /// @@ -154,8 +156,6 @@ public sealed class GhostTests // Ensure the position is the same var ghostPosition = data.SEntMan.GetComponent(ghost).Coordinates; Assert.That(ghostPosition, Is.EqualTo(oldPosition)); - - await data.Pair.CleanReturnAsync(); } [Test] @@ -168,10 +168,6 @@ public sealed class GhostTests // Delete the grid await data.Server.WaitPost(() => data.SEntMan.DeleteEntity(data.MapData.Grid.Owner)); }); - - await data.Pair.RunTicksSync(5); - - await data.Pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs b/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs index ad4ddc2612..c207e7b489 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs @@ -11,13 +11,7 @@ public sealed partial class MindTests [Test] public async Task DeleteAllThenGhost() { - var settings = new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true - }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; // Client is connected with a valid entity & mind Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity)); @@ -56,7 +50,5 @@ public sealed partial class MindTests Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind)); var xform = pair.Client.Transform(pair.Client.AttachedEntity!.Value); Assert.That(xform.MapID, Is.EqualTo(mapId)); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs index 513049bcad..cf87b3ea76 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs @@ -23,7 +23,7 @@ public sealed partial class MindTests [Test] public async Task TestDeleteVisiting() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var server = pair.Server; var entMan = server.ResolveDependency(); @@ -67,15 +67,13 @@ public sealed partial class MindTests // This used to throw so make sure it doesn't. await server.WaitPost(() => entMan.DeleteEntity(mind.OwnedEntity!.Value)); await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } // this is a variant of TestGhostOnDelete that just deletes the whole map. [Test] public async Task TestGhostOnDeleteMap() { - await using var pair = await SetupPair(dirty: true); + var pair = await SetupPair(dirty: true); var server = pair.Server; var testMap = await pair.CreateTestMap(); var testMap2 = await pair.CreateTestMap(); @@ -117,8 +115,6 @@ public sealed partial class MindTests Assert.That(transform.MapID, Is.Not.EqualTo(testMap.MapId)); #pragma warning restore NUnit2045 }); - - await pair.CleanReturnAsync(); } /// @@ -130,7 +126,7 @@ public sealed partial class MindTests public async Task TestGhostOnDelete() { // Client is needed to spawn session - await using var pair = await SetupPair(dirty: true); + var pair = await SetupPair(dirty: true); var server = pair.Server; var entMan = server.ResolveDependency(); @@ -145,8 +141,6 @@ public sealed partial class MindTests await pair.RunTicksSync(5); Assert.That(entMan.HasComponent(player.AttachedEntity), "Player did not become a ghost"); - - await pair.CleanReturnAsync(); } /// @@ -162,7 +156,7 @@ public sealed partial class MindTests public async Task TestOriginalDeletedWhileGhostingKeepsGhost() { // Client is needed to spawn session - await using var pair = await SetupPair(); + var pair = await SetupPair(); var server = pair.Server; var entMan = server.ResolveDependency(); @@ -207,8 +201,6 @@ public sealed partial class MindTests Assert.That(mind.Comp.VisitingEntity, Is.Null); Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(ghost)); }); - - await pair.CleanReturnAsync(); } /// @@ -220,7 +212,7 @@ public sealed partial class MindTests [Test] public async Task TestGhostToAghost() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var server = pair.Server; var entMan = server.ResolveDependency(); var playerMan = server.ResolveDependency(); @@ -250,8 +242,6 @@ public sealed partial class MindTests var mind = entMan.GetComponent(mindId!.Value); Assert.That(mind.VisitingEntity, Is.Null); - - await pair.CleanReturnAsync(); } /// @@ -264,7 +254,7 @@ public sealed partial class MindTests public async Task TestGhostDeletedSpawnsNewGhost() { // Client is needed to spawn session - await using var pair = await SetupPair(); + var pair = await SetupPair(); var server = pair.Server; var entMan = server.ResolveDependency(); @@ -308,7 +298,5 @@ public sealed partial class MindTests Assert.That(entMan.HasComponent(player.AttachedEntity!.Value)); #pragma warning restore NUnit2045 }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs index cebbed8ee8..e22f6c1e4d 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs @@ -18,6 +18,14 @@ namespace Content.IntegrationTests.Tests.Minds; // This partial class contains misc helper functions for other tests. public sealed partial class MindTests { + // TODO GAMETEST: Rewrite this test to use improved GameTest pair management when I have an API for that figured out. + public override PoolSettings PoolSettings => new() + { + DummyTicker = false, + Connected = true, + Dirty = true, + }; + /// /// Gets a server-client pair and ensures that the client is attached to a simple mind test entity. /// @@ -26,15 +34,9 @@ public sealed partial class MindTests /// the player's mind's current entity, likely because some previous test directly changed the players attached /// entity. /// - private static async Task SetupPair(bool dirty = false) + private async Task SetupPair(bool dirty = false) { - var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = dirty - }); - + var pair = Pair; var entMan = pair.Server.ResolveDependency(); var playerMan = pair.Server.ResolveDependency(); var mindSys = entMan.System(); diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs b/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs index db87797553..fbe79fe044 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs @@ -17,7 +17,7 @@ public sealed partial class MindTests [Test] public async Task TestGhostsCanReconnect() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var entMan = pair.Server.ResolveDependency(); var mind = GetMind(pair); @@ -32,8 +32,6 @@ public sealed partial class MindTests Assert.That(entMan.HasComponent(mind.Comp.OwnedEntity)); Assert.That(mind.Comp.VisitingEntity, Is.Null); }); - - await pair.CleanReturnAsync(); } // This test will do the following: @@ -44,7 +42,7 @@ public sealed partial class MindTests [Test] public async Task TestDeletedCanReconnect() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var entMan = pair.Server.ResolveDependency(); var mind = GetMind(pair); @@ -82,8 +80,6 @@ public sealed partial class MindTests Assert.That(mind.Comp.OwnedEntity, Is.Not.EqualTo(entity)); Assert.That(entMan.HasComponent(mind.Comp.OwnedEntity)); }); - - await pair.CleanReturnAsync(); } // This test will do the following: @@ -94,7 +90,7 @@ public sealed partial class MindTests [Test] public async Task TestVisitingGhostReconnect() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var entMan = pair.Server.ResolveDependency(); var mind = GetMind(pair); @@ -110,8 +106,6 @@ public sealed partial class MindTests Assert.That(entMan.Deleted(original), Is.False); Assert.That(entMan.Deleted(ghost)); }); - - await pair.CleanReturnAsync(); } // This test will do the following: @@ -122,7 +116,7 @@ public sealed partial class MindTests [Test] public async Task TestVisitingReconnect() { - await using var pair = await SetupPair(true); + var pair = await SetupPair(true); var entMan = pair.Server.ResolveDependency(); var mindSys = entMan.System(); var mind = GetMind(pair); @@ -150,8 +144,6 @@ public sealed partial class MindTests Assert.That(entMan.Deleted(visiting), Is.False); Assert.That(mind.Comp.CurrentEntity, Is.EqualTo(visiting)); }); - - await pair.CleanReturnAsync(); } // This test will do the following @@ -162,7 +154,7 @@ public sealed partial class MindTests [Test] public async Task TestReconnect() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var mind = GetMind(pair); Assert.That(mind.Comp.VisitingEntity, Is.Null); @@ -178,7 +170,5 @@ public sealed partial class MindTests Assert.That(newMind.Comp.VisitingEntity, Is.Null); Assert.That(newMind.Comp.OwnedEntity, Is.EqualTo(entity)); Assert.That(newMind.Id, Is.EqualTo(mind.Id)); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.cs b/Content.IntegrationTests/Tests/Minds/MindTests.cs index 35069339ba..08cfbaed5e 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; using Content.Server.Mind; @@ -23,7 +24,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed partial class MindTests +public sealed partial class MindTests : GameTest { private static readonly ProtoId BluntDamageType = "Blunt"; @@ -56,7 +57,7 @@ public sealed partial class MindTests [Test] public async Task TestCreateAndTransferMindToNewEntity() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -75,14 +76,12 @@ public sealed partial class MindTests mindSystem.TransferTo(mind, entity, mind: mind); Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind.Owner)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestReplaceMind() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -107,14 +106,12 @@ public sealed partial class MindTests Assert.That(mind.OwnedEntity, Is.Not.EqualTo(entity)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestEntityDeadWhenGibbed() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -160,14 +157,12 @@ public sealed partial class MindTests var mind = entMan.GetComponent(mindId); Assert.That(mindSystem.IsCharacterDeadPhysically(mind)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestMindTransfersToOtherEntity() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -194,18 +189,12 @@ public sealed partial class MindTests Assert.That(mindSystem.GetMind(targetEntity), Is.EqualTo(mind)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestOwningPlayerCanBeChanged() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -253,14 +242,12 @@ public sealed partial class MindTests }); await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } [Test] public async Task TestAddRemoveHasRoles() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -323,15 +310,13 @@ public sealed partial class MindTests Assert.That(roleSystem.MindHasRole(mindId), Is.False); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestPlayerCanGhost() { // Client is needed to spawn session - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false }); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -399,19 +384,12 @@ public sealed partial class MindTests Assert.That(mId, Is.Not.EqualTo(mindId)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestGhostDoesNotInfiniteLoop() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -482,7 +460,5 @@ public sealed partial class MindTests Assert.That(player.AttachedEntity, Is.Not.Null); Assert.That(player.AttachedEntity!.Value, Is.EqualTo(ghost)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/RoleTests.cs b/Content.IntegrationTests/Tests/Minds/RoleTests.cs index f0a7268a3d..3204dc1c2f 100644 --- a/Content.IntegrationTests/Tests/Minds/RoleTests.cs +++ b/Content.IntegrationTests/Tests/Minds/RoleTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Roles.Components; using Robust.Shared.GameObjects; using Robust.Shared.Reflection; @@ -6,7 +7,7 @@ using Robust.Shared.Reflection; namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed class RoleTests +public sealed class RoleTests : GameTest { /// /// Check that any prototype with a is properly configured @@ -14,7 +15,7 @@ public sealed class RoleTests [Test] public async Task ValidateRolePrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var jobComp = pair.Server.ResolveDependency().GetComponentName(); @@ -35,7 +36,6 @@ public sealed class RoleTests } }); - await pair.CleanReturnAsync(); } /// @@ -45,7 +45,7 @@ public sealed class RoleTests [Test] public async Task ValidateJobPrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var mindCompId = pair.Server.ResolveDependency().GetComponentName(); @@ -57,8 +57,6 @@ public sealed class RoleTests Assert.That(((MindRoleComponent)mindComp).JobPrototype, Is.Not.Null); } }); - - await pair.CleanReturnAsync(); } /// @@ -68,7 +66,7 @@ public sealed class RoleTests [Test] public async Task ValidateRolesHaveMindRoleComp() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var refMan = pair.Server.ResolveDependency(); var mindCompId = pair.Server.ResolveDependency().GetComponentName(); @@ -87,7 +85,5 @@ public sealed class RoleTests } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/NPC/NPCTest.cs b/Content.IntegrationTests/Tests/NPC/NPCTest.cs index 064fd6c5bf..1568f360e7 100644 --- a/Content.IntegrationTests/Tests/NPC/NPCTest.cs +++ b/Content.IntegrationTests/Tests/NPC/NPCTest.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.NPC.HTN; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -7,12 +8,12 @@ using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests.NPC; [TestFixture] -public sealed class NPCTest +public sealed class NPCTest : GameTest { [Test] public async Task CompoundRecursion() { - var pool = await PoolManager.GetServerClient(); + var pool = Pair; var server = pool.Server; await server.WaitIdleAsync(); @@ -30,8 +31,6 @@ public sealed class NPCTest counts.Clear(); } }); - - await pool.CleanReturnAsync(); } private static void Count(HTNCompoundPrototype compound, Dictionary counts, HTNSystem htnSystem, IPrototypeManager protoManager) diff --git a/Content.IntegrationTests/Tests/Networking/NetworkIdsMatchTest.cs b/Content.IntegrationTests/Tests/Networking/NetworkIdsMatchTest.cs index c72be944fd..7b4f048939 100644 --- a/Content.IntegrationTests/Tests/Networking/NetworkIdsMatchTest.cs +++ b/Content.IntegrationTests/Tests/Networking/NetworkIdsMatchTest.cs @@ -1,14 +1,15 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Networking { [TestFixture] - public sealed class NetworkIdsMatchTest + public sealed class NetworkIdsMatchTest : GameTest { [Test] public async Task TestConnect() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -38,7 +39,6 @@ namespace Content.IntegrationTests.Tests.Networking Assert.That(clientNetComps[netId].Name, Is.EqualTo(serverNetComps[netId].Name)); } }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs b/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs index b395569848..786e14ae42 100644 --- a/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs +++ b/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; @@ -5,16 +6,17 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Networking; [TestFixture] -public sealed class PvsCommandTest +public sealed class PvsCommandTest : GameTest { private static readonly EntProtoId TestEnt = "MobHuman"; + public override PoolSettings PoolSettings => new() { Connected = true, DummyTicker = false }; + [Test] public async Task TestPvsCommands() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false }); + var pair = Pair; var (server, client) = pair; - await pair.RunTicksSync(5); // Spawn a complex entity. EntityUid entity = default; @@ -46,6 +48,5 @@ public sealed class PvsCommandTest Assert.That(meta.LastStateApplied, Is.GreaterThan(lastApplied)); await server.WaitPost(() => server.EntMan.DeleteEntity(entity)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Networking/ReconnectTest.cs b/Content.IntegrationTests/Tests/Networking/ReconnectTest.cs index 4539ca81a5..26e76376ce 100644 --- a/Content.IntegrationTests/Tests/Networking/ReconnectTest.cs +++ b/Content.IntegrationTests/Tests/Networking/ReconnectTest.cs @@ -1,15 +1,16 @@ +using Content.IntegrationTests.Fixtures; using Robust.Client.Console; using Robust.Shared.Network; namespace Content.IntegrationTests.Tests.Networking { [TestFixture] - public sealed class ReconnectTest + public sealed class ReconnectTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -32,7 +33,6 @@ namespace Content.IntegrationTests.Tests.Networking await pair.RunTicksSync(10); await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs index 3e49499845..15c7373b63 100644 --- a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs +++ b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.Generic; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Robust.Client.GameStates; using Robust.Client.Timing; using Robust.Shared; @@ -27,12 +28,12 @@ namespace Content.IntegrationTests.Tests.Networking // the tick where the server *should* have, but did not, acknowledge the state change. // Finally, we run two events inside the prediction area to ensure reconciling does for incremental stuff. [TestFixture] - public sealed class SimplePredictReconcileTest + public sealed class SimplePredictReconcileTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -386,7 +387,6 @@ namespace Content.IntegrationTests.Tests.Networking } cfg.SetCVar(CVars.NetLogging, log); - await pair.CleanReturnAsync(); } public sealed class PredictionTestEntitySystem : EntitySystem diff --git a/Content.IntegrationTests/Tests/Physics/AnchorPrototypeTest.cs b/Content.IntegrationTests/Tests/Physics/AnchorPrototypeTest.cs index a65e7d1fd6..f65bd324a0 100644 --- a/Content.IntegrationTests/Tests/Physics/AnchorPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Physics/AnchorPrototypeTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.GameObjects; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; @@ -6,7 +7,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Physics; [TestFixture] -public sealed class AnchorPrototypeTest +public sealed class AnchorPrototypeTest : GameTest { /// /// Asserts that entityprototypes marked as anchored are also static physics bodies. @@ -14,7 +15,7 @@ public sealed class AnchorPrototypeTest [Test] public async Task TestStaticAnchorPrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoManager = pair.Server.ResolveDependency(); @@ -37,7 +38,5 @@ public sealed class AnchorPrototypeTest Assert.That(physics.BodyType, Is.EqualTo(BodyType.Static), $"Found entity prototype {ent} marked as anchored but not static for physics."); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs index e0588f789c..5473595963 100644 --- a/Content.IntegrationTests/Tests/PostMapInitTest.cs +++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Content.IntegrationTests.Utility; using YamlDotNet.RepresentationModel; using Content.Server.Administration.Systems; @@ -28,8 +30,14 @@ using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class PostMapInitTest + public sealed class PostMapInitTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings() + { + Connected = true, + Dirty = true, + }; + private const bool SkipTestMaps = true; private const string TestMapsPath = "/Maps/Test/"; @@ -95,16 +103,16 @@ namespace Content.IntegrationTests.Tests /// Asserts that specific files have been saved as grids and not maps. /// [Test, TestCaseSource(nameof(Grids))] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task GridsLoadableTest(string mapFile) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); var mapSystem = entManager.System(); var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); var path = new ResPath(mapFile); await server.WaitPost(() => @@ -121,9 +129,6 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(mapId); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -131,16 +136,16 @@ namespace Content.IntegrationTests.Tests /// [Test] [TestCaseSource(nameof(ShuttleMapFiles))] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task ShuttlesLoadableTest(ResPath path) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); var mapSystem = entManager.System(); var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { @@ -160,17 +165,13 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(mapId); }); }); - - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } [Test] [TestCaseSource(nameof(AllMapFiles))] public async Task NoSavedPostMapInitTest(ResPath map) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var resourceManager = server.ResolveDependency(); @@ -184,7 +185,6 @@ namespace Content.IntegrationTests.Tests // ReSharper disable once RedundantLogicalConditionalExpressionOperand if (SkipTestMaps && rootedPath.ToString().StartsWith(TestMapsPath, StringComparison.Ordinal)) { - await pair.CleanReturnAsync(); return; // We just pass immediately. } @@ -241,8 +241,6 @@ namespace Content.IntegrationTests.Tests await server.WaitPost(() => mapSys.InitializeMap(id)); Assert.That(loader.TrySaveMap(id, path)); Assert.That(IsPreInit(path, loader, deps, ev.RenamedPrototypes, ev.DeletedPrototypes), Is.False); - - await pair.CleanReturnAsync(); } private bool IsWhitelistedForMap(EntProtoId protoId, ResPath map) @@ -327,12 +325,10 @@ namespace Content.IntegrationTests.Tests } [Test, TestCaseSource(nameof(GameMaps))] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task GameMapsLoadableTest(string mapProto) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true // Stations spawn a bunch of nullspace entities and maps like centcomm. - }); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -343,7 +339,6 @@ namespace Content.IntegrationTests.Tests var ticker = entManager.EntitySysManager.GetEntitySystem(); var shuttleSystem = entManager.EntitySysManager.GetEntitySystem(); var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { @@ -442,9 +437,6 @@ namespace Content.IntegrationTests.Tests throw new Exception($"Failed to delete map {mapProto}", ex); } }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } @@ -473,37 +465,18 @@ namespace Content.IntegrationTests.Tests return resultCount; } - [Test] - public async Task AllMapsTested() - { - await using var pair = await PoolManager.GetServerClient(); - var server = pair.Server; - var protoMan = server.ResolveDependency(); - - var gameMaps = protoMan.EnumeratePrototypes() - .Where(x => !pair.IsTestPrototype(x)) - .Select(x => x.ID) - .ToHashSet(); - - Assert.That(gameMaps.Remove(PoolManager.TestMap)); - - Assert.That(gameMaps, Is.EquivalentTo(GameMaps.ToHashSet()), "Game map prototype missing from test cases."); - - await pair.CleanReturnAsync(); - } - [Test] [TestCaseSource(nameof(AllMapFiles))] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task NonGameMapsLoadableTest(ResPath mapPath) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapLoader = server.ResolveDependency().GetEntitySystem(); var resourceManager = server.ResolveDependency(); var protoManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); var gameMaps = protoManager.EnumeratePrototypes().Select(o => o.MapPath).ToHashSet(); @@ -512,7 +485,6 @@ namespace Content.IntegrationTests.Tests { // TODO: You might be able to save like, 1-2 seconds of test time if you eliminate these before // actually needing a pair. - await pair.CleanReturnAsync(); return; } @@ -520,7 +492,6 @@ namespace Content.IntegrationTests.Tests if (SkipTestMaps && rootedPath.ToString().StartsWith(TestMapsPath, StringComparison.Ordinal)) { - await pair.CleanReturnAsync(); return; } @@ -563,9 +534,6 @@ namespace Content.IntegrationTests.Tests } }); }); - - await server.WaitRunTicks(1); - await pair.CleanReturnAsync(); } /// diff --git a/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs b/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs index 288e976e9b..e991801330 100644 --- a/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Power.Components; using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; @@ -8,7 +9,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Power; [TestFixture, TestOf(typeof(SharedPowerStateSystem))] -public sealed class PowerStatePrototypeTest +public sealed class PowerStatePrototypeTest : GameTest { /// /// Asserts that the 's load is the same @@ -18,7 +19,7 @@ public sealed class PowerStatePrototypeTest [Test] public async Task AssertApcPowerMatchesPowerState() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -53,7 +54,5 @@ public sealed class PowerStatePrototypeTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Power/PowerStateTest.cs b/Content.IntegrationTests/Tests/Power/PowerStateTest.cs index edec6f3d21..a265de532f 100644 --- a/Content.IntegrationTests/Tests/Power/PowerStateTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerStateTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Coordinates; using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; @@ -8,7 +9,7 @@ using Robust.Shared.Maths; namespace Content.IntegrationTests.Tests.Power; [TestFixture] -public sealed class PowerStateTest +public sealed class PowerStateTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -31,7 +32,7 @@ public sealed class PowerStateTest [Test] public async Task SetWorkingState_IdleToWorking_UpdatesLoad() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -65,8 +66,6 @@ public sealed class PowerStateTest Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -75,7 +74,7 @@ public sealed class PowerStateTest [Test] public async Task SetWorkingState_WorkingToIdle_UpdatesLoad() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -118,8 +117,6 @@ public sealed class PowerStateTest Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -128,7 +125,7 @@ public sealed class PowerStateTest [Test] public async Task SetWorkingState_AlreadyInState_NoChange() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -179,8 +176,6 @@ public sealed class PowerStateTest Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Power/PowerTest.cs b/Content.IntegrationTests/Tests/Power/PowerTest.cs index a28f646ef8..db6c0b7c89 100644 --- a/Content.IntegrationTests/Tests/Power/PowerTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.NodeContainer.EntitySystems; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -14,7 +15,7 @@ using Robust.Shared.Timing; namespace Content.IntegrationTests.Tests.Power { [TestFixture] - public sealed class PowerTest + public sealed class PowerTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -167,7 +168,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestSimpleSurplus() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -218,8 +219,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(loadPower * 2).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } @@ -229,7 +228,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestSimpleDeficit() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -280,14 +279,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestSupplyRamp() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -368,14 +365,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(consumer.ReceivedPower, Is.EqualTo(400).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestBatteryRamp() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -472,8 +467,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(currentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] @@ -481,7 +474,7 @@ namespace Content.IntegrationTests.Tests.Power { // checks that batteries and supplies properly ramp down if the load is disconnected/disabled. - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -571,14 +564,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(consumer.ReceivedPower, Is.EqualTo(0).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestSimpleBatteryChargeDeficit() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var gameTiming = server.ResolveDependency(); @@ -631,14 +622,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(500).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestFullBattery() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -713,14 +702,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(currentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestFullBatteryEfficiencyPassThrough() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -795,14 +782,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(currentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestFullBatteryEfficiencyDemandPassThrough() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -888,8 +873,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -899,7 +882,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestSupplyPrioritized() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -988,8 +971,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(netBattery2.SupplyRampPosition, Is.EqualTo(500).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -998,7 +979,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestBatteriesProportional() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1079,14 +1060,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestBatteryEngineCut() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1161,8 +1140,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(netBattery.CurrentSupply, Is.GreaterThan(0)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -1171,7 +1148,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestTerminalNodeGroups() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1230,14 +1207,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(leftNode.NodeGroup, Is.Not.EqualTo(rightNode.NodeGroup)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task ApcChargingTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1290,14 +1265,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(currentCharge, Is.GreaterThan(0)); //apc battery should have gained charge }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task ApcNetTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1355,8 +1328,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(apcNetBattery.CurrentSupply, Is.EqualTo(1).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Power/StationPowerTests.cs b/Content.IntegrationTests/Tests/Power/StationPowerTests.cs index ab65fc89f6..0ef5a7e861 100644 --- a/Content.IntegrationTests/Tests/Power/StationPowerTests.cs +++ b/Content.IntegrationTests/Tests/Power/StationPowerTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -13,8 +14,7 @@ using Robust.Shared.EntitySerialization; namespace Content.IntegrationTests.Tests.Power; - -public sealed class StationPowerTests +public sealed class StationPowerTests : GameTest { /// /// How long the station should be able to survive on stored power if nothing is changed from round start. @@ -37,15 +37,17 @@ public sealed class StationPowerTests "Exo", ]; + public override PoolSettings PoolSettings => new () + { + Dirty = true, + }; + [Explicit] [Test, TestCaseSource(nameof(GameMaps)), Ignore("WL-Changed: Official maps are breaking the test due to upstream")] public async Task TestStationStartingPowerWindow(string mapProtoId) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - }); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -98,17 +100,12 @@ public sealed class StationPowerTests Assert.That(totalStartingCharge, Is.GreaterThanOrEqualTo(requiredStoredPower), $"Needs at least {requiredStoredPower - totalStartingCharge} more stored power!"); }); - - await pair.CleanReturnAsync(); } [Test, TestCaseSource(nameof(GameMaps))] public async Task TestApcLoad(string mapProtoId) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - }); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -147,7 +144,5 @@ public sealed class StationPowerTests } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs b/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs index 267b3637e0..62c9f1a460 100644 --- a/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs +++ b/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.Station.Systems; using Content.Shared.Inventory; using Content.Shared.Preferences; @@ -9,7 +10,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Preferences; [TestFixture] -public sealed class LoadoutTests +public sealed class LoadoutTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -42,16 +43,18 @@ public sealed class LoadoutTests ["jumpsuit"] = "ClothingUniformJumpsuitColorGrey" }; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + }; + /// /// Checks that an empty loadout still spawns with default gear and not naked. /// [Test] public async Task TestEmptyLoadout() { - var pair = await PoolManager.GetServerClient(new PoolSettings() - { - Dirty = true, - }); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -87,7 +90,5 @@ public sealed class LoadoutTests entManager.DeleteEntity(tester); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs index d937e7dbd1..dc588bdea8 100644 --- a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs +++ b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading; +using Content.IntegrationTests.Fixtures; using Content.Server.Database; using Content.Server.Preferences.Managers; using Content.Shared.Body; @@ -20,7 +21,7 @@ using Robust.UnitTesting; namespace Content.IntegrationTests.Tests.Preferences { [TestFixture] - public sealed class ServerDbSqliteTests + public sealed class ServerDbSqliteTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -75,12 +76,10 @@ namespace Content.IntegrationTests.Tests.Preferences [Test] public async Task TestUserDoesNotExist() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var db = GetDb(pair.Server); // Database should be empty so a new GUID should do it. Assert.That(await db.GetPlayerPreferencesAsync(NewUserId()), Is.Null); - - await pair.CleanReturnAsync(); } [Test] @@ -119,7 +118,7 @@ namespace Content.IntegrationTests.Tests.Preferences [Test] public async Task TestInitPrefs() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var db = GetDb(pair.Server); var preferences = (ServerPreferencesManager)pair.Server.ResolveDependency(); var username = new NetUserId(new Guid("640bd619-fc8d-4fe2-bf3c-4a5fb17d6ddd")); @@ -129,13 +128,12 @@ namespace Content.IntegrationTests.Tests.Preferences var prefs = await db.GetPlayerPreferencesAsync(username); var profile = preferences.ConvertProfiles(prefs!.Profiles.Find(p => p.Slot == slot)); Assert.That(profile.MemberwiseEquals(originalProfile)); - await pair.CleanReturnAsync(); } [Test] public async Task TestDeleteCharacter() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var db = GetDb(server); var username = new NetUserId(new Guid("640bd619-fc8d-4fe2-bf3c-4a5fb17d6ddd")); @@ -145,18 +143,16 @@ namespace Content.IntegrationTests.Tests.Preferences await db.SaveCharacterSlotAsync(username, null, 1); var prefs = await db.GetPlayerPreferencesAsync(username); Assert.That(prefs!.Profiles, Has.Count.EqualTo(1)); - await pair.CleanReturnAsync(); } [Test] public async Task TestNoPendingDatabaseChanges() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var db = GetDb(server); Assert.That(async () => await db.HasPendingModelChanges(), Is.False, "The database has pending model changes. Add a new migration to apply them. See https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations"); - await pair.CleanReturnAsync(); } private static NetUserId NewUserId() @@ -172,7 +168,7 @@ namespace Content.IntegrationTests.Tests.Preferences [TestCaseSource(nameof(_trueFalse))] public async Task InvalidSpeciesConversion(bool legacy) { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var db = GetDb(pair.Server); var preferences = (ServerPreferencesManager)pair.Server.ResolveDependency(); @@ -204,8 +200,6 @@ namespace Content.IntegrationTests.Tests.Preferences Assert.That(converted.Characters[0].Species, Is.Not.EqualTo(InvalidSpecies)); Assert.That(converted.Characters[0].Species, Is.EqualTo(HumanoidCharacterProfile.DefaultSpecies)); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Procedural/DungeonTests.cs b/Content.IntegrationTests/Tests/Procedural/DungeonTests.cs index 6bede9660a..d3afcc7253 100644 --- a/Content.IntegrationTests/Tests/Procedural/DungeonTests.cs +++ b/Content.IntegrationTests/Tests/Procedural/DungeonTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.Procedural; using Content.Shared.Procedural; using Robust.Shared.Maths; @@ -7,12 +8,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Procedural; [TestOf(typeof(DungeonSystem))] -public sealed class DungeonTests +public sealed class DungeonTests : GameTest { [Test] public async Task TestDungeonRoomPackBounds() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoManager = pair.Server.ResolveDependency(); await pair.Server.WaitAssertion(() => @@ -55,14 +56,12 @@ public sealed class DungeonTests } } }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestDungeonPresets() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoManager = pair.Server.ResolveDependency(); await pair.Server.WaitAssertion(() => @@ -92,7 +91,5 @@ public sealed class DungeonTests } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index 9ff8ca2900..5977e7deee 100644 --- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Coordinates; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -27,12 +28,12 @@ namespace Content.IntegrationTests.Tests; /// spawn it into a new empty map and seeing what the map yml looks like. /// [TestFixture] -public sealed class PrototypeSaveTest +public sealed class PrototypeSaveTest : GameTest { [Test] public async Task UninitializedSaveTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityMan = server.ResolveDependency(); @@ -156,7 +157,6 @@ public sealed class PrototypeSaveTest } }); }); - await pair.CleanReturnAsync(); } public sealed class TestEntityUidContext : ISerializationContext, diff --git a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs index 440d9e636e..3b4bed882d 100644 --- a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs +++ b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Markdown; @@ -8,7 +9,7 @@ using Robust.UnitTesting; namespace Content.IntegrationTests.Tests.PrototypeTests; -public sealed class PrototypeTests +public sealed class PrototypeTests : GameTest { /// /// This test writes all known server prototypes as yaml files, then validates that the result is valid yaml. @@ -17,10 +18,9 @@ public sealed class PrototypeTests [Test] public async Task TestAllServerPrototypesAreSerializable() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var context = new PrototypeSaveTest.TestEntityUidContext(); await SaveThenValidatePrototype(pair.Server, "server", context); - await pair.CleanReturnAsync(); } /// @@ -30,10 +30,9 @@ public sealed class PrototypeTests [Test] public async Task TestAllClientPrototypesAreSerializable() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var context = new PrototypeSaveTest.TestEntityUidContext(); await SaveThenValidatePrototype(pair.Client, "client", context); - await pair.CleanReturnAsync(); } public async Task SaveThenValidatePrototype(RobustIntegrationTest.IntegrationInstance instance, string instanceId, @@ -69,10 +68,9 @@ public sealed class PrototypeTests [Test] public async Task ServerPrototypeSaveLoadSaveTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var context = new PrototypeSaveTest.TestEntityUidContext(); await SaveLoadSavePrototype(pair.Server, context); - await pair.CleanReturnAsync(); } /// @@ -81,10 +79,9 @@ public sealed class PrototypeTests [Test] public async Task ClientPrototypeSaveLoadSaveTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var context = new PrototypeSaveTest.TestEntityUidContext(); await SaveLoadSavePrototype(pair.Client, context); - await pair.CleanReturnAsync(); } private async Task SaveLoadSavePrototype( diff --git a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs index c641cd9bd1..ac7bdcd24d 100644 --- a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Tag; using Robust.Client.Upload.Commands; using Robust.Shared.GameObjects; @@ -6,7 +7,7 @@ using Robust.Shared.Upload; namespace Content.IntegrationTests.Tests.PrototypeTests; -public sealed class PrototypeUploadTest +public sealed class PrototypeUploadTest : GameTest { public const string IdA = "UploadTestPrototype"; public const string IdB = $"{IdA}NoParent"; @@ -36,7 +37,7 @@ public sealed class PrototypeUploadTest [TestOf(typeof(LoadPrototypeCommand))] public async Task TestFileUpload() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings {Connected = true}); + var pair = Pair; var sCompFact = pair.Server.ResolveDependency(); var cCompFact = pair.Client.ResolveDependency(); @@ -79,7 +80,5 @@ public sealed class PrototypeUploadTest Assert.That(cProtoB!.TryGetComponent(out _, cCompFact), Is.False); Assert.That(cProtoD!.TryGetComponent(out _, cCompFact), Is.True); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Puller/PullerTest.cs b/Content.IntegrationTests/Tests/Puller/PullerTest.cs index a4fde86dbf..541417354a 100644 --- a/Content.IntegrationTests/Tests/Puller/PullerTest.cs +++ b/Content.IntegrationTests/Tests/Puller/PullerTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Hands.Components; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Prototypes; @@ -9,7 +10,7 @@ namespace Content.IntegrationTests.Tests.Puller; #nullable enable [TestFixture] -public sealed class PullerTest +public sealed class PullerTest : GameTest { /// /// Checks that needsHands on PullerComponent is not set on mobs that don't even have hands. @@ -17,7 +18,7 @@ public sealed class PullerTest [Test] public async Task PullerSanityTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var compFactory = server.ResolveDependency(); @@ -39,7 +40,5 @@ public sealed class PullerTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Replays/ReplayTests.cs b/Content.IntegrationTests/Tests/Replays/ReplayTests.cs index dcff6c3b45..4bd5f62e78 100644 --- a/Content.IntegrationTests/Tests/Replays/ReplayTests.cs +++ b/Content.IntegrationTests/Tests/Replays/ReplayTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Shared.CCVar; using Robust.Shared; @@ -6,20 +7,21 @@ using Robust.Shared.Replays; namespace Content.IntegrationTests.Tests.Replays; [TestFixture] -public sealed class ReplayTests +public sealed class ReplayTests : GameTest { + public override PoolSettings PoolSettings => new() + { + DummyTicker = false, + Dirty = true + }; + /// /// Simple test that just makes sure that automatic replay recording on round restarts works without any issues. /// [Test] public async Task AutoRecordReplayTest() { - var settings = new PoolSettings - { - DummyTicker = false, - Dirty = true - }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var server = pair.Server; Assert.That(server.CfgMan.GetCVar(CVars.ReplayServerRecordingEnabled), Is.False); @@ -54,7 +56,5 @@ public sealed class ReplayTests await server.WaitPost(() => ticker.RestartRound()); await pair.RunTicksSync(25); Assert.That(recordMan.IsRecording, Is.False); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ResearchTest.cs b/Content.IntegrationTests/Tests/ResearchTest.cs index 4661a1ea9e..c95d68d9e2 100644 --- a/Content.IntegrationTests/Tests/ResearchTest.cs +++ b/Content.IntegrationTests/Tests/ResearchTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Lathe; using Content.Shared.Research.Prototypes; using Robust.Shared.GameObjects; @@ -8,12 +9,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class ResearchTest +public sealed class ResearchTest : GameTest { [Test] public async Task DisciplineValidTierPrerequesitesTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoManager = server.ResolveDependency(); @@ -42,14 +43,12 @@ public sealed class ResearchTest } }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task AllTechPrintableTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -99,7 +98,5 @@ public sealed class ResearchTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs b/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs index 40457f5488..13bb3cfd74 100644 --- a/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs +++ b/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs @@ -1,4 +1,5 @@ -using Content.Server.GameTicking; +using Content.IntegrationTests.Fixtures; +using Content.Server.GameTicking; using Content.Shared.GameTicking; using Robust.Shared.GameObjects; using Robust.Shared.Reflection; @@ -7,7 +8,7 @@ namespace Content.IntegrationTests.Tests { [TestFixture] [TestOf(typeof(RoundRestartCleanupEvent))] - public sealed class ResettingEntitySystemTests + public sealed class ResettingEntitySystemTests : GameTest { public sealed class TestRoundRestartCleanupEvent : EntitySystem { @@ -26,15 +27,17 @@ namespace Content.IntegrationTests.Tests } } + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }; + [Test] public async Task ResettingEntitySystemResetTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }); + var pair = Pair; var server = pair.Server; var entitySystemManager = server.ResolveDependency(); @@ -52,7 +55,6 @@ namespace Content.IntegrationTests.Tests Assert.That(system.HasBeenReset); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Respirator/LungTest.cs b/Content.IntegrationTests/Tests/Respirator/LungTest.cs index ae6b50ff0f..097fe5e3a0 100644 --- a/Content.IntegrationTests/Tests/Respirator/LungTest.cs +++ b/Content.IntegrationTests/Tests/Respirator/LungTest.cs @@ -6,6 +6,7 @@ using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Map; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Shared.Atmos.Components; using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Utility; @@ -14,7 +15,7 @@ namespace Content.IntegrationTests.Tests.Respirator; [TestFixture] [TestOf(typeof(LungSystem))] -public sealed class LungTest +public sealed class LungTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -54,7 +55,7 @@ public sealed class LungTest public async Task AirConsistencyTest() { // --- Setup - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -123,14 +124,12 @@ public sealed class LungTest "Did not exhale as much gas as was inhaled" ); } - - await pair.CleanReturnAsync(); } [Test] public async Task NoSuffocationTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -183,7 +182,5 @@ public sealed class LungTest $"Entity {entityManager.GetComponent(human).EntityName} is suffocating on tick {tick}"); }); } - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/RestartRoundTest.cs b/Content.IntegrationTests/Tests/RestartRoundTest.cs index 69c9a7dedf..1ed43b0951 100644 --- a/Content.IntegrationTests/Tests/RestartRoundTest.cs +++ b/Content.IntegrationTests/Tests/RestartRoundTest.cs @@ -1,20 +1,23 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class RestartRoundTest + public sealed class RestartRoundTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }; + [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }); + var pair = Pair; var server = pair.Server; var sysManager = server.ResolveDependency(); @@ -23,8 +26,7 @@ namespace Content.IntegrationTests.Tests sysManager.GetEntitySystem().RestartRound(); }); - await pair.RunTicksSync(10); - await pair.CleanReturnAsync(); + await pair.RunUntilSynced(); } } } diff --git a/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs b/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs index de89f16be7..e4f80eebb7 100644 --- a/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs +++ b/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Roles; using Content.Server.Storage.EntitySystems; using Robust.Shared.GameObjects; @@ -7,16 +8,17 @@ using Robust.Shared.Collections; namespace Content.IntegrationTests.Tests.Roles; [TestFixture] -public sealed class StartingGearPrototypeStorageTest +public sealed class StartingGearPrototypeStorageTest : GameTest { + public override PoolSettings PoolSettings => new() { Connected = true, Dirty = true }; + /// /// Checks that a storage fill on a StartingGearPrototype will properly fill /// [Test] public async Task TestStartingGearStorage() { - var settings = new PoolSettings { Connected = true, Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var server = pair.Server; var mapSystem = server.System(); var storageSystem = server.System(); @@ -65,7 +67,5 @@ public sealed class StartingGearPrototypeStorageTest mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Round/JobTest.cs b/Content.IntegrationTests/Tests/Round/JobTest.cs index 92d7a4fb08..a3c3fe4c4d 100644 --- a/Content.IntegrationTests/Tests/Round/JobTest.cs +++ b/Content.IntegrationTests/Tests/Round/JobTest.cs @@ -1,4 +1,7 @@ #nullable enable +using System.Collections.Generic; +using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Pair; using Content.Server.GameTicking; using Content.Server.Mind; @@ -19,7 +22,7 @@ using System.Linq; namespace Content.IntegrationTests.Tests.Round; [TestFixture] -public sealed class JobTest +public sealed class JobTest : GameTest { private static readonly ProtoId Passenger = "Passenger"; private static readonly ProtoId Engineer = "StationEngineer"; @@ -47,7 +50,14 @@ public sealed class JobTest {Captain}: [ 1, 1 ] "; - private async Task AssertJob(TestPair pair, ProtoId job, NetUserId? user = null, bool isAntag = false) + public override PoolSettings PoolSettings => new() + { + DummyTicker = false, + Connected = true, + InLobby = true + }; + + private void AssertJob(TestPair pair, ProtoId job, NetUserId? user = null, bool isAntag = false) { var jobSys = pair.Server.System(); var mindSys = pair.Server.System(); @@ -89,12 +99,7 @@ public sealed class JobTest [Test] public async Task StartRoundTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); var ticker = pair.Server.System(); @@ -113,7 +118,6 @@ public sealed class JobTest await AssertJob(pair, Passenger); // WL-Changes await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -122,12 +126,7 @@ public sealed class JobTest [Test] public async Task JobPreferenceTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); var ticker = pair.Server.System(); @@ -151,7 +150,6 @@ public sealed class JobTest await AssertJob(pair, Passenger); // WL-Changes await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -161,12 +159,7 @@ public sealed class JobTest [Test] public async Task JobWeightTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); var ticker = pair.Server.System(); @@ -187,7 +180,6 @@ public sealed class JobTest await AssertJob(pair, Captain); // WL-Changes await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -196,12 +188,7 @@ public sealed class JobTest [Test] public async Task JobPriorityTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; pair.Server.CfgMan.SetCVar(WLCVars.RoleRestrictionChecksEnabled, false); // WL-Changes @@ -237,6 +224,5 @@ public sealed class JobTest }); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/RoundEndTest.cs b/Content.IntegrationTests/Tests/RoundEndTest.cs index 5de6de381d..16c460c3da 100644 --- a/Content.IntegrationTests/Tests/RoundEndTest.cs +++ b/Content.IntegrationTests/Tests/RoundEndTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.RoundEnd; using Content.Shared.CCVar; @@ -7,7 +8,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class RoundEndTest + public sealed class RoundEndTest : GameTest { private sealed class RoundEndTestSystem : EntitySystem { @@ -25,15 +26,18 @@ namespace Content.IntegrationTests.Tests } } + + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }; + [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }); + var pair = Pair; var server = pair.Server; @@ -151,7 +155,6 @@ namespace Content.IntegrationTests.Tests roundEndSystem.DefaultCountdownDuration = TimeSpan.FromMinutes(4); ticker.RestartRound(); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/SalvageTest.cs b/Content.IntegrationTests/Tests/SalvageTest.cs index 0059db6292..68bdf4726c 100644 --- a/Content.IntegrationTests/Tests/SalvageTest.cs +++ b/Content.IntegrationTests/Tests/SalvageTest.cs @@ -1,4 +1,6 @@ -using Content.Shared.CCVar; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.Shared.CCVar; using Content.Shared.Salvage; using Robust.Shared.Configuration; using Robust.Shared.EntitySerialization.Systems; @@ -8,15 +10,16 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class SalvageTest +public sealed class SalvageTest : GameTest { /// /// Asserts that all salvage maps have been saved as grids and are loadable. /// [Test] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task AllSalvageMapsLoadableTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -24,7 +27,6 @@ public sealed class SalvageTest var prototypeManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); var mapSystem = entManager.System(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { @@ -50,8 +52,6 @@ public sealed class SalvageTest } } }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); + await RunUntilSynced(); } } diff --git a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs index eb3dc14720..914a1e21c2 100644 --- a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs @@ -1,4 +1,6 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Content.Shared.CCVar; using Robust.Server.GameObjects; using Robust.Shared.Configuration; @@ -12,14 +14,15 @@ using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class SaveLoadMapTest + public sealed class SaveLoadMapTest : GameTest { [Test] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task SaveLoadMultiGridMap() { var mapPath = new ResPath("/Maps/Test/TestMap.yml"); - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var sEntities = server.ResolveDependency(); @@ -27,8 +30,6 @@ namespace Content.IntegrationTests.Tests var mapSystem = sEntities.System(); var xformSystem = sEntities.EntitySysManager.GetEntitySystem(); var resManager = server.ResolveDependency(); - var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitAssertion(() => { @@ -94,8 +95,6 @@ namespace Content.IntegrationTests.Tests }); } }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs index b41aa0bf2f..9339d61270 100644 --- a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs @@ -1,5 +1,6 @@ using System.IO; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.CCVar; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; @@ -16,12 +17,12 @@ namespace Content.IntegrationTests.Tests /// Tests that a grid's yaml does not change when saved consecutively. /// [TestFixture] - public sealed class SaveLoadSaveTest + public sealed class SaveLoadSaveTest : GameTest { [Test] public async Task CreateSaveLoadSaveGrid() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); @@ -85,10 +86,9 @@ namespace Content.IntegrationTests.Tests } }); testSystem.Enabled = false; - await pair.CleanReturnAsync(); } - private const string TestMap = "Maps/bagel.yml"; + private new const string TestMap = "Maps/bagel.yml"; /// /// Loads the default map, runs it for 5 ticks, then assert that it did not change. @@ -96,7 +96,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task LoadSaveTicksSaveBagel() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapLoader = server.ResolveDependency().GetEntitySystem(); var mapSys = server.System(); @@ -167,7 +167,6 @@ namespace Content.IntegrationTests.Tests testSystem.Enabled = false; await server.WaitPost(() => mapSys.DeleteMap(mapId)); - await pair.CleanReturnAsync(); } /// @@ -183,7 +182,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task LoadTickLoadBagel() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapLoader = server.System(); @@ -241,7 +240,6 @@ namespace Content.IntegrationTests.Tests testSystem.Enabled = false; await server.WaitPost(() => mapSys.DeleteMap(mapId1)); await server.WaitPost(() => mapSys.DeleteMap(mapId2)); - await pair.CleanReturnAsync(); } /// diff --git a/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs b/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs index 339420362c..daf14e42a0 100644 --- a/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs +++ b/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Robust.Shared.Reflection; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Manager.Attributes; @@ -8,7 +9,7 @@ using Robust.Shared.Serialization.Markdown.Value; namespace Content.IntegrationTests.Tests.Serialization; [TestFixture] -public sealed partial class SerializationTest +public sealed partial class SerializationTest : GameTest { /// /// Check that serializing generic enums works as intended. This should really be in engine, but engine @@ -17,7 +18,7 @@ public sealed partial class SerializationTest [Test] public async Task SerializeGenericEnums() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var seriMan = server.ResolveDependency(); var refMan = server.ResolveDependency(); @@ -67,8 +68,6 @@ public sealed partial class SerializationTest Assert.That(seriMan.ValidateNode(genericNode).GetErrors().Any(), Is.True); Assert.That(seriMan.ValidateNode(typedNode).GetErrors().Any(), Is.True); Assert.That(seriMan.ValidateNode(typedNode).GetErrors().Any(), Is.False); - - await pair.CleanReturnAsync(); } private enum TestEnum : byte { Aa, Bb, Cc, Dd } diff --git a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs index ab82a3d2f9..927f973472 100644 --- a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs +++ b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Shuttles.Systems; using Content.Tests; using Robust.Server.GameObjects; @@ -13,7 +14,7 @@ using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests.Shuttle; -public sealed class DockTest : ContentUnitTest +public sealed class DockTest : GameTest { private static IEnumerable TestSource() { @@ -26,7 +27,7 @@ public sealed class DockTest : ContentUnitTest [TestCaseSource(nameof(TestSource))] public async Task TestDockingConfig(Vector2 dock1Pos, Vector2 dock2Pos, Angle dock1Angle, Angle dock2Angle, bool result) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var map = await pair.CreateTestMap(); @@ -83,14 +84,12 @@ public sealed class DockTest : ContentUnitTest Assert.That(result, Is.EqualTo(config != null)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestPlanetDock() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var map = await pair.CreateTestMap(); @@ -126,7 +125,5 @@ public sealed class DockTest : ContentUnitTest var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid); Assert.That(dockingConfig, Is.Not.EqualTo(null)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ShuttleTest.cs b/Content.IntegrationTests/Tests/ShuttleTest.cs index da5b82d91e..1c447db871 100644 --- a/Content.IntegrationTests/Tests/ShuttleTest.cs +++ b/Content.IntegrationTests/Tests/ShuttleTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Shuttles.Components; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -9,12 +10,12 @@ using Robust.Shared.Physics.Systems; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class ShuttleTest + public sealed class ShuttleTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -50,7 +51,6 @@ namespace Content.IntegrationTests.Tests { Assert.That(entManager.GetComponent(map.Grid).LocalPosition, Is.Not.EqualTo(Vector2.Zero)); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs b/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs index da7e1e8e9b..ca667f2e7a 100644 --- a/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs +++ b/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Shared.Item; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; @@ -22,7 +23,7 @@ namespace Content.IntegrationTests.Tests.Sprite; /// /// [TestFixture] -public sealed class PrototypeSaveTest +public sealed class PrototypeSaveTest : GameTest { private static readonly HashSet Ignored = new() { @@ -34,8 +35,7 @@ public sealed class PrototypeSaveTest [Test] public async Task AllItemsHaveSpritesTest() { - var settings = new PoolSettings() { Connected = true }; // client needs to be in-game - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; List badPrototypes = []; await pair.Client.WaitPost(() => @@ -58,7 +58,5 @@ public sealed class PrototypeSaveTest Assert.Fail($"Item prototype has no sprite: {proto.ID}. It should probably either be marked as abstract, not be an item, or have a valid sprite"); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/StartTest.cs b/Content.IntegrationTests/Tests/StartTest.cs index e2bf5e8ff1..223f4676b0 100644 --- a/Content.IntegrationTests/Tests/StartTest.cs +++ b/Content.IntegrationTests/Tests/StartTest.cs @@ -1,9 +1,10 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.Exceptions; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class StartTest + public sealed class StartTest : GameTest { /// /// Test that the server, and client start, and stop. @@ -11,7 +12,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task TestClientStart() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var client = pair.Client; Assert.That(client.IsAlive); await client.WaitRunTicks(5); @@ -27,8 +28,6 @@ namespace Content.IntegrationTests.Tests Assert.That(sRuntimeLog.ExceptionCount, Is.EqualTo(0), "No exceptions must be logged on server."); await server.WaitIdleAsync(); Assert.That(server.IsAlive); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs b/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs index 02552669f7..8da97cbab7 100644 --- a/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs +++ b/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Systems; @@ -12,15 +13,21 @@ namespace Content.IntegrationTests.Tests.Station; [TestFixture] [TestOf(typeof(EmergencyShuttleSystem))] -public sealed class EvacShuttleTest +public sealed class EvacShuttleTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings() + { + DummyTicker = true, + Dirty = true, + }; + /// /// Ensure that the emergency shuttle can be called, and that it will travel to centcomm /// [Test] public async Task EmergencyEvacTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { DummyTicker = true, Dirty = true }); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; var ticker = server.System(); @@ -122,6 +129,5 @@ public sealed class EvacShuttleTest server.CfgMan.SetCVar(CCVars.EmergencyShuttleDockTime, dockTime); pair.Server.CfgMan.SetCVar(CCVars.EmergencyShuttleEnabled, false); pair.Server.CfgMan.SetCVar(CCVars.GameMap, gameMap); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Station/JobTests.cs b/Content.IntegrationTests/Tests/Station/JobTests.cs index 5172049a99..0961db1c67 100644 --- a/Content.IntegrationTests/Tests/Station/JobTests.cs +++ b/Content.IntegrationTests/Tests/Station/JobTests.cs @@ -2,12 +2,13 @@ using Content.Shared.Roles; using Content.Shared.Roles.Jobs; using Robust.Shared.Prototypes; using System.Linq; +using Content.IntegrationTests.Fixtures; namespace Content.IntegrationTests.Tests.Station; [TestFixture] [TestOf(typeof(SharedJobSystem))] -public sealed class JobTest +public sealed class JobTest : GameTest { /// /// Ensures that every job belongs to at most 1 primary department. @@ -16,7 +17,7 @@ public sealed class JobTest [Test] public async Task PrimaryDepartmentsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -43,6 +44,5 @@ public sealed class JobTest } } }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs index 4abd32bda0..c0f0b19b19 100644 --- a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs +++ b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Station.Components; using Content.Server.Station.Systems; using Content.Shared.Maps; @@ -15,7 +16,7 @@ namespace Content.IntegrationTests.Tests.Station; [TestFixture] [TestOf(typeof(StationJobsSystem))] -public sealed class StationJobsTest +public sealed class StationJobsTest : GameTest { private const string StationMapId = "FooStation"; @@ -85,7 +86,7 @@ public sealed class StationJobsTest [Test] public async Task AssignJobsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -153,13 +154,12 @@ public sealed class StationJobsTest Assert.That(assigned.Values.Select(x => x.Item1).ToList(), Does.Contain("TCaptain")); }); }); - await pair.CleanReturnAsync(); } [Test] public async Task AdjustJobsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -203,13 +203,12 @@ public sealed class StationJobsTest Assert.That(stationJobs.IsJobUnlimited(station, "TChaplain"), "Could not make TChaplain unlimited."); }); }); - await pair.CleanReturnAsync(); } [Test] public async Task InvalidRoundstartJobsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -247,7 +246,6 @@ public sealed class StationJobsTest } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs b/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs index f80cc089de..6b100f89fd 100644 --- a/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs +++ b/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Storage.EntitySystems; using Content.Shared.Damage; using Content.Shared.Damage.Systems; @@ -7,7 +8,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Storage; [TestFixture] -public sealed class EntityStorageTests +public sealed class EntityStorageTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -31,7 +32,7 @@ public sealed class EntityStorageTests [Test] public async Task TestContainerDestruction() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var map = await pair.CreateTestMap(); @@ -76,7 +77,5 @@ public sealed class EntityStorageTests await server.WaitRunTicks(5); Assert.That(server.EntMan.Deleted(box)); Assert.That(server.EntMan.Deleted(crowbar), Is.False); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Storage/StorageTest.cs b/Content.IntegrationTests/Tests/Storage/StorageTest.cs index 95d94906bb..c0ef6a3691 100644 --- a/Content.IntegrationTests/Tests/Storage/StorageTest.cs +++ b/Content.IntegrationTests/Tests/Storage/StorageTest.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Containers; using Content.Shared.Item; using Content.Shared.Prototypes; @@ -12,7 +13,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Storage; -public sealed class StorageTest +public sealed class StorageTest : GameTest { /// /// Can an item store more than itself weighs. @@ -21,7 +22,7 @@ public sealed class StorageTest [Test] public async Task StorageSizeArbitrageTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoManager = server.ResolveDependency(); @@ -44,13 +45,12 @@ public sealed class StorageTest $"Found storage arbitrage on {proto.ID}"); } }); - await pair.CleanReturnAsync(); } [Test] public async Task TestStorageFillPrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoManager = server.ResolveDependency(); @@ -72,13 +72,12 @@ public sealed class StorageTest } }); }); - await pair.CleanReturnAsync(); } [Test] public async Task TestSufficientSpaceForFill() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -159,14 +158,12 @@ public sealed class StorageTest } } }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestSufficientSpaceForEntityStorageFill() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -194,7 +191,6 @@ public sealed class StorageTest $"{proto.ID} storage fill is too large."); }); } - await pair.CleanReturnAsync(); } private int GetEntrySize(EntitySpawnEntry entry, bool getCount, IPrototypeManager protoMan, SharedItemSystem itemSystem) @@ -242,7 +238,7 @@ public sealed class StorageTest [Test] public async Task NoMultipleContainerFillsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var compFact = pair.Server.ResolveDependency(); Assert.Multiple(() => @@ -258,6 +254,5 @@ public sealed class StorageTest Assert.That(!proto.HasComponent(compFact), $"Prototype {proto.ID} has both {nameof(ContainerFillComponent)} and {nameof(StorageFillComponent)}."); } }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/StoreTests.cs b/Content.IntegrationTests/Tests/StoreTests.cs index 39df0fc8cc..5a63dfa563 100644 --- a/Content.IntegrationTests/Tests/StoreTests.cs +++ b/Content.IntegrationTests/Tests/StoreTests.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; using System.Linq; -using System.Threading; -using Content.Server.Store.Systems; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.Server.PDA.Ringer; using Content.Server.Traitor.Uplink; using Content.Shared.FixedPoint; using Content.Shared.Inventory; @@ -10,13 +10,12 @@ using Content.Shared.Store; using Content.Shared.Store.Components; using Content.Shared.StoreDiscount.Components; using Robust.Shared.GameObjects; -using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class StoreTests +public sealed class StoreTests : GameTest { [TestPrototypes] @@ -32,10 +31,23 @@ public sealed class StoreTests - idcard - type: Pda "; + [Test] + [Ignore(""" + This currently causes the client to crash, failing the test. + When this is fixed, this test should be removed and StoreDiscountAndRefund + should just use the default pair config. + """)] + public async Task StoreDiscountAndRefundWithClient() + { + await StoreDiscountAndRefund(); + } + + [Test] + [PairConfig(nameof(PsDisconnected))] public async Task StoreDiscountAndRefund() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -57,6 +69,7 @@ public sealed class StoreTests EntityUid pda = default; var uplinkSystem = entManager.System(); + var ringerSystem = entManager.System(); var listingPrototypes = prototypeManager.EnumeratePrototypes() .ToArray(); @@ -78,10 +91,13 @@ public sealed class StoreTests mindSystem.TransferTo(mind, human, mind: mind); FixedPoint2 originalBalance = 20; - uplinkSystem.AddUplink(human, originalBalance, null, true); + uplinkSystem.AddUplink(human, originalBalance, out var notes, pda, true); - var storeComponent = entManager.GetComponent(pda); - var discountComponent = entManager.GetComponent(pda); + Assert.That(notes != null); + ringerSystem.TryMatchRingtoneToStore(notes, out var storeEnt); + Assert.That(storeEnt.HasValue); + var storeComponent = entManager.GetComponent(storeEnt.Value); + var discountComponent = entManager.GetComponent(storeEnt.Value); Assert.That( discountComponent.Discounts, Has.Exactly(6).Items, @@ -127,8 +143,8 @@ public sealed class StoreTests Assert.That(plainDiscountedCost.Value, Is.LessThan(prototypeCost.Value), "Expected discounted cost to be lower then prototype cost."); - var buyMsg = new StoreBuyListingMessage(discountedListingItem.ID){Actor = human}; - server.EntMan.EventBus.RaiseLocalEvent(pda, buyMsg); + var buyMsg = new StoreBuyListingMessage(discountedListingItem.ID, null){Actor = human}; + server.EntMan.EventBus.RaiseLocalEvent(storeEnt.Value, buyMsg); var newBalance = storeComponent.Balance[UplinkSystem.TelecrystalCurrencyPrototype]; Assert.That(newBalance.Value, Is.EqualTo((originalBalance - plainDiscountedCost).Value), "Expected to have balance reduced by discounted cost"); @@ -141,7 +157,7 @@ public sealed class StoreTests Assert.That(costAfterBuy.Value, Is.EqualTo(prototypeCost.Value), "Expected cost after discount refund to be equal to prototype cost."); var refundMsg = new StoreRequestRefundMessage { Actor = human }; - server.EntMan.EventBus.RaiseLocalEvent(pda, refundMsg); + server.EntMan.EventBus.RaiseLocalEvent(storeEnt.Value, refundMsg); // get refreshed item after refund re-generated items discountedListingItem = storeComponent.FullListingsCatalog.First(x => x.ID == itemId); @@ -168,7 +184,5 @@ public sealed class StoreTests } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Tag/TagTest.cs b/Content.IntegrationTests/Tests/Tag/TagTest.cs index e6cd2accaf..4632e3ea2e 100644 --- a/Content.IntegrationTests/Tests/Tag/TagTest.cs +++ b/Content.IntegrationTests/Tests/Tag/TagTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Shared.Tag; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -10,7 +11,7 @@ namespace Content.IntegrationTests.Tests.Tag { [TestFixture] [TestOf(typeof(TagComponent))] - public sealed class TagTest + public sealed class TagTest : GameTest { private const string TagEntityId = "TagTestDummy"; @@ -44,7 +45,7 @@ namespace Content.IntegrationTests.Tests.Tag [Test] public async Task TagComponentTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntityManager = server.ResolveDependency(); @@ -295,7 +296,6 @@ namespace Content.IntegrationTests.Tests.Tag Assert.Throws(() => { tagSystem.AddTags(sTagEntity, new HashSet> { UnregisteredTag }); }); #endif }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Tiles/TileStackRecursionTest.cs b/Content.IntegrationTests/Tests/Tiles/TileStackRecursionTest.cs index 52c5b03265..d1c26e9bec 100644 --- a/Content.IntegrationTests/Tests/Tiles/TileStackRecursionTest.cs +++ b/Content.IntegrationTests/Tests/Tiles/TileStackRecursionTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.CCVar; using Content.Shared.Maps; using Robust.Shared.Configuration; @@ -7,12 +8,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Tiles; -public sealed class TileStackRecursionTest +public sealed class TileStackRecursionTest : GameTest { [Test] public async Task TestBaseTurfRecursion() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoMan = pair.Server.ResolveDependency(); var cfg = pair.Server.ResolveDependency(); var maxTileHistoryLength = cfg.GetCVar(CCVars.TileStackLimit); @@ -40,7 +41,6 @@ public sealed class TileStackRecursionTest (possibleTurf, new ProtoId(ctdef.ID)))); } Bfs(nodes, edges, maxTileHistoryLength); - await pair.CleanReturnAsync(); } private void Bfs(List<(ProtoId, int)> nodes, List<(ProtoId, ProtoId)> edges, int depthLimit) diff --git a/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs b/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs index 5efa009ca7..2aea2986ec 100644 --- a/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs +++ b/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Client.LateJoin; +using Content.IntegrationTests.Fixtures; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.ContentPack; using Robust.Shared.IoC; @@ -8,7 +9,7 @@ using Robust.Shared.Reflection; namespace Content.IntegrationTests.Tests.UserInterface; [TestFixture] -public sealed class UiControlTest +public sealed class UiControlTest : GameTest { // You should not be adding to this. private Type[] _ignored = new Type[] @@ -22,10 +23,7 @@ public sealed class UiControlTest [Test] public async Task TestWindows() { - var pair = await PoolManager.GetServerClient(new PoolSettings() - { - Connected = true, - }); + var pair = Pair; var activator = pair.Client.ResolveDependency(); var refManager = pair.Client.ResolveDependency(); var loader = pair.Client.ResolveDependency(); @@ -50,7 +48,5 @@ public sealed class UiControlTest activator.CreateInstance(type, oneOff: true, inject: false); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs b/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs index d460fd354f..6ab5ca1691 100644 --- a/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs +++ b/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Shared.Physics; using Content.Shared.Spawning; using Robust.Shared.GameObjects; @@ -8,7 +9,7 @@ namespace Content.IntegrationTests.Tests.Utility { [TestFixture] [TestOf(typeof(EntitySystemExtensions))] - public sealed class EntitySystemExtensionsTest + public sealed class EntitySystemExtensionsTest : GameTest { private const string BlockerDummyId = "BlockerDummy"; @@ -32,7 +33,7 @@ namespace Content.IntegrationTests.Tests.Utility [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -95,7 +96,6 @@ namespace Content.IntegrationTests.Tests.Utility Assert.That(entity, Is.Not.Null); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs b/Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs index 19b25816fa..d7e1239603 100644 --- a/Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs +++ b/Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Containers.ItemSlots; using Content.Shared.Whitelist; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ namespace Content.IntegrationTests.Tests.Utility { [TestFixture] [TestOf(typeof(EntityWhitelist))] - public sealed class EntityWhitelistTest + public sealed class EntityWhitelistTest : GameTest { private const string InvalidComponent = "Sprite"; private const string ValidComponent = "Physics"; @@ -58,7 +59,7 @@ namespace Content.IntegrationTests.Tests.Utility [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -111,7 +112,6 @@ namespace Content.IntegrationTests.Tests.Utility Assert.That(sys.IsValid(whitelistSer, WhitelistTestInvalidTag), Is.False); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs index 7058cfab6a..dfaaae05ad 100644 --- a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs +++ b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.VendingMachines; using Content.Server.Wires; using Content.Shared.Cargo.Prototypes; @@ -21,7 +22,7 @@ namespace Content.IntegrationTests.Tests [TestFixture] [TestOf(typeof(VendingMachineRestockComponent))] [TestOf(typeof(VendingMachineSystem))] - public sealed class VendingMachineRestockTest : EntitySystem + public sealed class VendingMachineRestockTest : GameTest { private static readonly ProtoId TestDamageType = "Blunt"; @@ -111,7 +112,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task TestAllRestocksAreAvailableToBuy() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -194,14 +195,12 @@ namespace Content.IntegrationTests.Tests $"Some entities with {restockCompName} are unavailable for purchase: \n - {string.Join("\n - ", restockEntities)}"); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestCompleteRestockProcess() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -280,14 +279,12 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestRestockBreaksOpen() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -342,14 +339,12 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestRestockInventoryBounds() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -388,10 +383,6 @@ namespace Content.IntegrationTests.Tests Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(3), "Machine's available inventory did not stay the same after a third restock."); }); - - await pair.CleanReturnAsync(); } } } - -#nullable disable diff --git a/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs b/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs index 920dc08818..d9801275b9 100644 --- a/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs +++ b/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs @@ -1,4 +1,5 @@ -using Content.Server.Doors; +using Content.IntegrationTests.Fixtures; +using Content.Server.Doors; using Content.Server.Power; using Content.Server.Wires; using Robust.Shared.GameObjects; @@ -10,7 +11,7 @@ namespace Content.IntegrationTests.Tests.Wires; [TestFixture] [Parallelizable(ParallelScope.All)] [TestOf(typeof(WiresSystem))] -public sealed class WireLayoutTest +public sealed class WireLayoutTest : GameTest { [TestPrototypes] public const string Prototypes = """ @@ -53,7 +54,7 @@ public sealed class WireLayoutTest [Test] public async Task TestLayoutInheritance() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -89,8 +90,6 @@ public sealed class WireLayoutTest Assert.That(ent3.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 door bolt wire"); }); }); - - await pair.CleanReturnAsync(); } private static Entity SpawnWithComp(IEntityManager entityManager, string prototype, MapCoordinates coords) diff --git a/Content.IntegrationTests/Tests/WizdenContentFreeze/WizdenContentFreeze.cs b/Content.IntegrationTests/Tests/WizdenContentFreeze/WizdenContentFreeze.cs index 6faf730ed8..79bbe56cf5 100644 --- a/Content.IntegrationTests/Tests/WizdenContentFreeze/WizdenContentFreeze.cs +++ b/Content.IntegrationTests/Tests/WizdenContentFreeze/WizdenContentFreeze.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Kitchen; namespace Content.IntegrationTests.Tests.WizdenContentFreeze; @@ -5,7 +6,7 @@ namespace Content.IntegrationTests.Tests.WizdenContentFreeze; /// /// These tests are limited to adding a specific type of content, essentially freezing it. If you are a fork developer, you may want to disable these tests. /// -public sealed class WizdenContentFreeze +public sealed class WizdenContentFreeze : GameTest { /// /// This freeze prohibits the addition of new microwave recipes. @@ -18,7 +19,7 @@ public sealed class WizdenContentFreeze [Test] public async Task MicrowaveRecipesFreezeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; @@ -35,7 +36,5 @@ public sealed class WizdenContentFreeze { Assert.Fail($"Oh, you deleted the microwave recipes? YOU ARE SO COOL! Please lower the number of recipes in MicrowaveRecipesFreezeTest from {recipesLimit} to {recipesCount} so that future contributors cannot add new recipes back."); } - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/XenoArtifactTest.cs b/Content.IntegrationTests/Tests/XenoArtifactTest.cs index ac4c58c52c..35b03470e9 100644 --- a/Content.IntegrationTests/Tests/XenoArtifactTest.cs +++ b/Content.IntegrationTests/Tests/XenoArtifactTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.Components; using Robust.Shared.GameObjects; @@ -6,7 +7,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class XenoArtifactTest +public sealed class XenoArtifactTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -90,7 +91,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactAddNodeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -133,9 +134,6 @@ public sealed class XenoArtifactTest Assert.That(artifactSystem.GetDirectPredecessorNodes(artifactEnt, node3!.Value), Has.Count.EqualTo(1)); Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node3!.Value), Has.Count.EqualTo(2)); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -144,7 +142,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactRemoveNodeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -182,9 +180,6 @@ public sealed class XenoArtifactTest Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node2!.Value), Is.Empty); Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -193,7 +188,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactResizeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -245,9 +240,6 @@ public sealed class XenoArtifactTest Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node4!.Value), Is.Empty); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -256,7 +248,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactReplaceTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -304,9 +296,6 @@ public sealed class XenoArtifactTest Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -315,7 +304,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactBuildActiveNodesTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -367,15 +356,12 @@ public sealed class XenoArtifactTest Assert.That(artifactEnt.Comp.CachedActiveNodes, Has.Count.EqualTo(expectedActiveNodes.Length)); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } [Test] public async Task XenoArtifactGenerateSegmentsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -412,8 +398,5 @@ public sealed class XenoArtifactTest Assert.That(grouped[2].Count(), Is.LessThanOrEqualTo(2)); // maintain same width or, if we used 3 nodes on previous layer - we only have 1 left! }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Utility/TestProperties.cs b/Content.IntegrationTests/Utility/TestProperties.cs new file mode 100644 index 0000000000..c8ed338a82 --- /dev/null +++ b/Content.IntegrationTests/Utility/TestProperties.cs @@ -0,0 +1,9 @@ +namespace Content.IntegrationTests.Utility; + +public static class TestProperties +{ + /// + /// Name of the property describing what kind of test frame a test is running under. + /// + public const string TestFrameKind = "TestFrameKind"; +} diff --git a/Content.MapRenderer/Program.cs b/Content.MapRenderer/Program.cs index 90f97a5786..668e04f63d 100644 --- a/Content.MapRenderer/Program.cs +++ b/Content.MapRenderer/Program.cs @@ -145,7 +145,7 @@ namespace Content.MapRenderer { Console.Write($"Following map files did not exist on disk directly, searching through prototypes: {string.Join(", ", lookupPrototypeFiles)}"); - await using var pair = await PoolManager.GetServerClient(); + await using var pair = await PoolManager.GetServerClient(testContext: testContext); var mapPrototypes = pair.Server .ResolveDependency() .EnumeratePrototypes() diff --git a/Content.Server/Access/Systems/AgentIDCardSystem.cs b/Content.Server/Access/Systems/AgentIDCardSystem.cs index 0586c33ada..6e7cd6a406 100644 --- a/Content.Server/Access/Systems/AgentIDCardSystem.cs +++ b/Content.Server/Access/Systems/AgentIDCardSystem.cs @@ -1,22 +1,22 @@ -using Content.Server.Access.Components; -using Content.Server.Popups; -using Content.Shared.UserInterface; -using Content.Shared.Access.Components; -using Content.Shared.Access.Systems; -using Content.Shared.Interaction; -using Content.Shared.StatusIcon; -using Robust.Server.GameObjects; -using Robust.Shared.Prototypes; -using Content.Shared.Roles; using System.Diagnostics.CodeAnalysis; +using Content.Server.Access.Components; using Content.Server.Clothing.Systems; using Content.Server.Implants; -using Content.Server.VoiceMask; +using Content.Server.Popups; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.Clothing.Components; using Content.Shared.Implants; +using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Lock; using Content.Shared.PDA; +using Content.Shared.Roles; +using Content.Shared.StatusIcon; +using Content.Shared.UserInterface; using Content.Shared.VoiceMask; +using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; namespace Content.Server.Access.Systems { @@ -80,7 +80,8 @@ namespace Content.Server.Access.Systems if (!proto.TryGetComponent(out var comp, EntityManager.ComponentFactory)) return; - _chameleon.SetSelectedPrototype(ent, comp.IdCard); + if (TryComp(ent, out var chameleonComp) && chameleonComp.CanBeSetByController) + _chameleon.SetSelectedPrototype(ent, comp.IdCard, component: chameleonComp); } private void OnVoiceMaskNameChanged(Entity ent, ref InventoryRelayedEvent args) diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index 0ea55eee91..1e4a044901 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -135,7 +135,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem string newFullName, string newJobTitle, List> newAccessList, - ProtoId newJobProto, + ProtoId? newJobProto, EntityUid player, IdCardConsoleComponent? component = null) { @@ -158,7 +158,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem _idCard.TryChangeFullName(targetId, newFullName, player: player); _idCard.TryChangeJobTitle(targetId, newJobTitle, player: player); - if (_prototype.Resolve(newJobProto, out var job) + if (_prototype.TryIndex(newJobProto, out var job) && _prototype.Resolve(job.Icon, out var jobIcon)) { _idCard.TryChangeJobIcon(targetId, jobIcon, player: player); diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 1ba069b93a..60dc4b8e35 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -579,7 +579,7 @@ public sealed partial class ServerApi : IPostInjectInit var bans = await _db.GetBansAsync(userId: located.UserId, address: null, hwId: null, - modernHWIds: located.LastModernHWIds, + modernHWIds: null, includeUnbanned: false); if (bans.Count > 0) { @@ -609,6 +609,11 @@ public sealed partial class ServerApi : IPostInjectInit { info.AddAddress(player.Channel.RemoteEndPoint.Address); } + else + { + // We fallback into using the located player ip. + info.AddAddress(located.LastAddress); + } _bans.CreateServerBan(info); await RespondOk(context); diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index f65a3eceb8..b6b8702fc6 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -279,7 +279,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Consumable/Food/Baked/pie.rsi"), "plain-slice"), Act = () => { - _creamPieSystem.SetCreamPied(args.Target, creamPied, true); + _creamPieSystem.SetCreamPied((args.Target, creamPied), true); }, Impact = LogImpact.Extreme, Message = string.Join(": ", creamPieName, Loc.GetString("admin-smite-creampie-description")) diff --git a/Content.Server/Anomaly/Effects/GravityAnomalySystem.cs b/Content.Server/Anomaly/Effects/GravityAnomalySystem.cs index 02cd0aafc6..e053377d57 100644 --- a/Content.Server/Anomaly/Effects/GravityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/GravityAnomalySystem.cs @@ -1,9 +1,9 @@ using Content.Server.Physics.Components; +using Content.Server.Radiation.Systems; using Content.Server.Singularity.Components; using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Effects; using Content.Shared.Anomaly.Effects.Components; -using Content.Shared.Radiation.Components; namespace Content.Server.Anomaly.Effects; @@ -12,6 +12,8 @@ namespace Content.Server.Anomaly.Effects; /// public sealed class GravityAnomalySystem : SharedGravityAnomalySystem { + [Dependency] private readonly RadiationSystem _radiation = default!; + /// public override void Initialize() { @@ -22,8 +24,7 @@ public sealed class GravityAnomalySystem : SharedGravityAnomalySystem private void OnSeverityChanged(Entity anomaly, ref AnomalySeverityChangedEvent args) { - if (TryComp(anomaly, out var radSource)) - radSource.Intensity = anomaly.Comp.MaxRadiationIntensity * args.Severity; + _radiation.SetIntensity(anomaly.Owner, anomaly.Comp.MaxRadiationIntensity * args.Severity); if (TryComp(anomaly, out var gravityWell)) { diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index e68bf7b2ec..7bb2c0c89c 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -1,3 +1,4 @@ +using System.Buffers; using System.Diagnostics; using Content.Server.NodeContainer.NodeGroups; using Content.Shared.Atmos; @@ -6,6 +7,7 @@ using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.Reactions; using JetBrains.Annotations; using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Atmos.EntitySystems; @@ -289,6 +291,95 @@ public partial class AtmosphereSystem GetTileMixture(entity, excite)?.AdjustMoles(gas, mols); } + /// + /// Retrieves the pressures of all gas mixtures + /// in the given array of s, and stores the results in the + /// provided span. + /// + /// The tiles span to find the pressures of. + /// The span to store the pressures to - this should be the same length + /// as the tile array. + /// Thrown when the length of the provided spans do not match. + /// Note that for or s that are null, + /// this method will return a value close to zero but not exactly zero. + [PublicAPI] + public static void GetBulkTileAtmospherePressures(Span tiles, Span pressures) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(tiles.Length, pressures.Length); + + var len = tiles.Length; + var arr1 = ArrayPool.Shared.Rent(len); + + try + { + var mixtSpan = arr1.AsSpan(0, len); + for (var i = 0; i < tiles.Length; i++) + { + mixtSpan[i] = tiles[i]?.Air; + } + + GetBulkGasMixturePressures(mixtSpan, pressures); + } + finally + { + ArrayPool.Shared.Return(arr1); + } + } + + /// + /// Gets the pressures of a of s. + /// + /// The to get the pressures of. + /// The to store the pressures to - this should be the same length + /// as the mixtures array. + /// Thrown when the length of the provided spans do not match. + /// Note that for GasMixtures that are null, this method will return a value close to zero but not exactly zero. + [PublicAPI] + public static void GetBulkGasMixturePressures(Span mixtures, Span pressures) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(mixtures.Length, pressures.Length); + + var len = mixtures.Length; + + var arr1 = ArrayPool.Shared.Rent(len); + var arr2 = ArrayPool.Shared.Rent(len); + var arr3 = ArrayPool.Shared.Rent(len); + try + { + var mixtVol = arr1.AsSpan(0, len); + var mixtTemp = arr2.AsSpan(0, len); + var mixtMoles = arr3.AsSpan(0, len); + + for (var i = 0; i < len; i++) + { + if (mixtures[i] is not { } mixture) + { + // To prevent any NaN/Div/0 errors, we just bite the bullet + // and set everything to the lowest possible value. + mixtVol[i] = 1; + mixtTemp[i] = 1; + mixtMoles[i] = float.Epsilon; + continue; + } + + mixtVol[i] = mixture.Volume; + mixtTemp[i] = mixture.Temperature; + mixtMoles[i] = mixture.TotalMoles; + } + + // TODO NumericsHelpers need a method that substitutes NaNs with zeros. AVX-512 has one iirc but for 256/128 we need to do some masking bs + NumericsHelpers.Multiply(mixtMoles, Atmospherics.R); + NumericsHelpers.Multiply(mixtMoles, mixtTemp); + NumericsHelpers.Divide(mixtMoles, mixtVol, pressures); + } + finally + { + ArrayPool.Shared.Return(arr1); + ArrayPool.Shared.Return(arr2); + ArrayPool.Shared.Return(arr3); + } + } + /// /// Triggers a tile's to react. /// @@ -715,6 +806,26 @@ public partial class AtmosphereSystem return contains; } + /// + /// Applies an exponential moving average to a value, given a new value, an old value, and a time delta. + /// + /// The new value to factor into the average. + /// The old value to factor into the average. + /// The time delta to factor into the average. + /// The time constant to use for the average. + /// Higher values will make the average change more slowly, + /// while lower values will make it change more quickly. + /// The result of the exponential moving average. + [PublicAPI] + public static float ExponentialMovingAverage(float newValue, + float oldValue, + float deltaTime, + float tau = 0.5f) + { + var a = deltaTime / tau; + return a * newValue + (1 - a) * oldValue; + } + [ByRefEvent] private record struct SetSimulatedGridMethodEvent( EntityUid Grid, diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs index 9457c5689c..59ccbbb8c8 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs @@ -200,71 +200,6 @@ public sealed partial class AtmosphereSystem } } - /// - /// A DeltaPressure helper method that retrieves the pressures of all gas mixtures - /// in the given array of s, and stores the results in the - /// provided span. - /// - /// The tiles span to find the pressures of. - /// The span to store the pressures to - this should be the same length - /// as the tile array. - /// Thrown when the length of the provided spans do not match. - private static void GetBulkTileAtmospherePressures(Span tiles, Span pressures) - { - // this shit is internal because I don't even trust myself - if (tiles.Length != pressures.Length) - throw new ArgumentException("Length of Tiles and Pressures span must be the same!"); - - var len = pressures.Length; - - // Once again, ArrayPool might return arrays that are longer than the length. - // We really need them to be all the same length, so slice them here. - var arr1 = ArrayPool.Shared.Rent(len); - var arr2 = ArrayPool.Shared.Rent(len); - var arr3 = ArrayPool.Shared.Rent(len); - - var mixtVol = arr1.AsSpan(0, len); - var mixtTemp = arr2.AsSpan(0, len); - var mixtMoles = arr3.AsSpan(0, len); - - try - { - for (var i = 0; i < len; i++) - { - if (tiles[i] is not { Air: { } mixture }) - { - // To prevent any NaN/Div/0 errors, we just bite the bullet - // and set everything to the lowest possible value. - mixtVol[i] = 1; - mixtTemp[i] = 1; - mixtMoles[i] = float.Epsilon; - continue; - } - - mixtVol[i] = mixture.Volume; - mixtTemp[i] = mixture.Temperature; - mixtMoles[i] = mixture.TotalMoles; - } - - /* - Retrieval of single tile pressures requires calling a get method for each tile, - which does a bunch of scalar operations. - - So we go ahead and batch-retrieve the pressures of all tiles - and process them in bulk. - */ - NumericsHelpers.Multiply(mixtMoles, Atmospherics.R); - NumericsHelpers.Multiply(mixtMoles, mixtTemp); - NumericsHelpers.Divide(mixtMoles, mixtVol, pressures); - } - finally - { - ArrayPool.Shared.Return(arr1); - ArrayPool.Shared.Return(arr2); - ArrayPool.Shared.Return(arr3); - } - } - /// /// Packs data into a data struct and enqueues it /// into the queue for diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 639a71ec60..db89d53fea 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -28,6 +28,20 @@ namespace Content.Server.Atmos.EntitySystems Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority)); } + public override float GetMass(GasMixture mix) + { + return GetMass(mix.Moles); + } + + public override float GetMass(float[] moles) + { + Span tmp = stackalloc float[moles.Length]; + NumericsHelpers.Multiply(moles, GasMolarMasses, tmp); + + // Conversion of grams to kilograms. + return NumericsHelpers.HorizontalAdd(tmp) * Atmospherics.gToKg; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override float GetHeatCapacityCalculation(float[] moles, bool space) { @@ -38,7 +52,7 @@ namespace Content.Server.Atmos.EntitySystems } Span tmp = stackalloc float[moles.Length]; - NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp); + NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); // Adjust heat capacity by speedup, because this is primarily what // determines how quickly gases heat up/cool. return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); @@ -196,211 +210,6 @@ namespace Content.Server.Atmos.EntitySystems Merge(destination, buffer); } - /// - /// Calculates the dimensionless fraction of gas required to equalize pressure between two gas mixtures. - /// - /// The first gas mixture involved in the pressure equalization. - /// This mixture should be the one you always expect to be the highest pressure. - /// The second gas mixture involved in the pressure equalization. - /// A float (from 0 to 1) representing the dimensionless fraction of gas that needs to be transferred from the - /// mixture of higher pressure to the mixture of lower pressure. - /// - /// - /// This properly takes into account the effect - /// of gas merging from inlet to outlet affecting the temperature - /// (and possibly increasing the pressure) in the outlet. - /// - /// - /// The gas is assumed to expand freely, - /// so the temperature of the gas with the greater pressure is not changing. - /// - /// - /// - /// If you want to calculate the moles required to equalize pressure between an inlet and an outlet, - /// multiply the fraction returned by the source moles. - /// - public float FractionToEqualizePressure(GasMixture gasMixture1, GasMixture gasMixture2) - { - /* - Problem: the gas being merged from the inlet to the outlet could affect the - temp. of the gas and cause a pressure rise. - We want the pressure to be equalized, so we have to account for this. - - For clarity, let's assume that gasMixture1 is the inlet and gasMixture2 is the outlet. - - We require mechanical equilibrium, so \( P_1' = P_2' \) - - Before the transfer, we have: - \( P_1 = \frac{n_1 R T_1}{V_1} \) - \( P_2 = \frac{n_2 R T_2}{V_2} \) - - After removing fraction \( x \) moles from the inlet, we have: - \( P_1' = \frac{(1 - x) n_1 R T_1}{V_1} \) - - The outlet will gain the same \( x n_1 \) moles of gas. - So \( n_2' = n_2 + x n_1 \) - - After mixing, the outlet temperature will be changed. - Denote the new mixture temperature as \( T_2' \). - Volume is constant. - So we have: - \( P_2' = \frac{(n_2 + x n_1) R T_2}{V_2} \) - - The total energy of the incoming inlet to outlet gas at \( T_1 \) plus the existing energy of the outlet gas at \( T_2 \) - will be equal to the energy of the new outlet gas at \( T_2' \). - This leads to the following derivation: - \( x n_1 C_1 T_1 + n_2 C_2 T_2 = (x n_1 C_1 + n_2 C_2) T_2' \) - - Where \( C_1 \) and \( C_2 \) are the heat capacities of the inlet and outlet gases, respectively. - - Solving for \( T_2' \) gives us: - \( T_2' = \frac{x n_1 C_1 T_1 + n_2 C_2 T_2}{x n_1 C_1 + n_2 C_2} \) - - Once again, we require mechanical equilibrium (\( P_1' = P_2' \)), - so we can substitute \( T_2' \) into the pressure equation: - - \( \frac{(1 - x) n_1 R T_1}{V_1} = - \frac{(n_2 + x n_1) R}{V_2} \cdot - \frac{x n_1 C_1 T_1 + n_2 C_2 T_2} - {x n_1 C_1 + n_2 C_2} \) - - Now it's a matter of solving for \( x \). - Not going to show the full derivation here, just steps. - 1. Cancel common factor \( R \). - 2. Multiply both sides by \( x n_1 C_1 + n_2 C_2 \), so that everything - becomes a polynomial in terms of \( x \). - 3. Expand both sides. - 4. Collect like powers of \( x \). - 5. After collecting, you should end up with a polynomial of the form: - - \( (-n_1 C_1 T_1 (1 + \frac{V_2}{V_1})) x^2 + - (n_1 T_1 \frac{V_2}{V_1} (C_1 - C_2) - n_2 C_1 T_1 - n_1 C_2 T_2) x + - (n_1 T_1 \frac{V_2}{V_1} C_2 - n_2 C_2 T_2) = 0 \) - - Divide through by \( n_1 C_1 T_1 \) and replace each ratio with a symbol for clarity: - \( k_V = \frac{V_2}{V_1} \) - \( k_n = \frac{n_2}{n_1} \) - \( k_T = \frac{T_2}{T_1} \) - \( k_C = \frac{C_2}{C_1} \) - */ - - // Ensure that P_1 > P_2 so the quadratic works out. - if (gasMixture1.Pressure < gasMixture2.Pressure) - { - (gasMixture1, gasMixture2) = (gasMixture2, gasMixture1); - } - - // Establish the dimensionless ratios. - var volumeRatio = gasMixture2.Volume / gasMixture1.Volume; - var molesRatio = gasMixture2.TotalMoles / gasMixture1.TotalMoles; - var temperatureRatio = gasMixture2.Temperature / gasMixture1.Temperature; - var heatCapacityRatio = GetHeatCapacity(gasMixture2) / GetHeatCapacity(gasMixture1); - - // The quadratic equation is solved for the transfer fraction. - var quadraticA = 1 + volumeRatio; - var quadraticB = molesRatio - volumeRatio + heatCapacityRatio * (temperatureRatio + volumeRatio); - var quadraticC = heatCapacityRatio * (molesRatio * temperatureRatio - volumeRatio); - - return (-quadraticB + MathF.Sqrt(quadraticB * quadraticB - 4 * quadraticA * quadraticC)) / (2 * quadraticA); - } - - /// - /// Determines the fraction of gas to be removed and transferred from a source - /// to a target to reach a target pressure - /// in the target . - /// - /// The source that gas will be removed from. - /// This should always be of higher pressure than the second . - /// The target that will increase in pressure - /// to the target pressure. - /// The target mixture's desired pressure to target. - /// A float representing the dimensionless fraction of gas to transfer from the source - /// to the target. This may return negative if you have your mixtures swapped. - /// Note that this method doesn't take into account the heat capacity of the - /// transferred volume causing a pressure rise in the target . - [PublicAPI] - public static float FractionToMaxPressure(GasMixture mix1, GasMixture mix2, float targetPressure) - { - var molesToTransfer = MolesToMaxPressure(mix1, mix2, targetPressure); - return molesToTransfer / mix1.TotalMoles; - } - - /// - /// Determines the number of moles to be removed and transferred from a source - /// to a target to reach a target pressure - /// in the target . - /// - /// The source that gas will be removed from. - /// This should always be of higher pressure than the second . - /// The target that will increase in pressure - /// to the target pressure. - /// The target mixture's desired pressure to target. - /// The difference in moles required to reach the target pressure. - /// Note that this method doesn't take into account the heat capacity of the - /// transferred volume causing a pressure rise in the target . - [PublicAPI] - public static float MolesToMaxPressure(GasMixture mix1, GasMixture mix2, float targetPressure) - { - /* - Calculate the moles required to reach the target pressure. - The formula is derived from the ideal gas law and the - general Richman's law, under the simplification that all the specific heat capacities are equal. - Derivation can also be seen at - https://github.com/space-wizards/space-station-14/pull/35211/files/a0ae787fe07a4e792570f55b49d9dd8038eb6e4d#r1961183456 - TODO ATMOS Make this properly obey the heat capacity change on the target mixture. - - Derivation is as follows. - Assume A is mix1, B is mix2, C is the combined mixture after transfer. - We can express the number of moles in C: - n_C = n_A + n_B - - We can then determine the temperature of C: - T_C = \frac{T_A n_A c_A + T_B n_B c_B}{n_A c_A + n_B c_B} - - Where c_A and c_B are the specific heats of mixtures A and B, respectively. - We can then express the pressure of C: - P_C = \frac{n_C R T_C}{V_C} - - Using the above equations, we can express P_C as follows: - P_C = \frac{(n_A + n_B) R (\frac{T_a n_A + T_B n_B}{n_A + n_B}}{V_C} - - Which can be reduced to: - P_C = \frac{R (T_A n_A + T_B n_B)}{V_C} - - Solving for n_A gives: - n_A = \frac{P_C V_C - R T_B n_B}{R T_A} - - Using the ideal gas law to substitute: - n_A = \frac{P_C V_C - P_B V_B}{R T_A} - - The output volume doesn't change: - V_B = V_C - - So: - n_A = \frac{(P_C - P_B) V_B}{R T_A} - */ - - var delta = targetPressure - mix2.Pressure; - var requiredMoles = (delta * mix2.Volume) / (mix1.Temperature * Atmospherics.R); - - // Return the fraction of moles to transfer. - return requiredMoles; - } - - /// - /// Determines the number of moles that need to be removed from a to reach a target pressure threshold. - /// - /// The gas mixture whose moles and properties will be used in the calculation. - /// The target pressure threshold to calculate against. - /// The difference in moles required to reach the target pressure threshold. - /// The temperature of the gas is assumed to be not changing due to a free expansion. - public static float MolesToPressureThreshold(GasMixture gasMixture, float targetPressure) - { - // Kid named PV = nRT. - return gasMixture.TotalMoles - - targetPressure * gasMixture.Volume / (Atmospherics.R * gasMixture.Temperature); - } - /// /// Checks whether a gas mixture is probably safe. /// This only checks temperature and pressure, not gas composition. diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs index ad19770bfe..d4a70dfbc0 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -117,8 +117,11 @@ namespace Content.Server.Atmos.EntitySystems private void Archive(TileAtmosphere tile, int fireCount) { if (tile.Air != null) + { + // TODO ATMOS: This is an extremely large hotspot in LINDA, accounting for 1/5th of its time. + // Please make GasMixture a struct or use a FauxGasMixture with an InlineArray to handle copying this sanely. tile.AirArchived = new GasMixture(tile.Air); - + } tile.ArchivedCycle = fireCount; } @@ -197,12 +200,25 @@ namespace Content.Server.Atmos.EntitySystems } /// - /// Shares gas between two tiles. Part of LINDA. + /// Performs a share operation between two tiles, sharing both physical gas and temperature. /// + /// The receiving the share. + /// The sharing its air. + /// The number of s next to the receiver that air can flow to. + /// The pressure difference between the two tiles after sharing. + /// LINDA is an FEA-like solver and this method is basically the core of it. + /// In FEA we divide the problem into infinitesimal parts and try to step towards the desired end state: + /// a steady state where all air is equalized between tiles. + /// To do this we share the tiles air between other tiles over time (as well as the temperature). + /// Note that the timestep is actually a cyclestep, so running the cycles faster leads to a faster equalization. + /// Hilarious, I know. public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int atmosAdjacentTurfs) { - if (tileReceiver.Air is not {} receiver || tileSharer.Air is not {} sharer || - tileReceiver.AirArchived == null || tileSharer.AirArchived == null) + // TODO ATMOS: Method needs to timestep over deltaTime instead of per cycle + // TODO ATMOS: Method needs to account for adjacent turfs in the situation where air is moving from receiver to sharer. + // See https://github.com/tgstation/tgstation/pull/63785 + if (tileReceiver.Air is not { } receiver || tileSharer.Air is not { } sharer || + tileReceiver.AirArchived == null || tileSharer.AirArchived == null) return 0f; var temperatureDelta = tileReceiver.AirArchived.Temperature - tileSharer.AirArchived.Temperature; @@ -221,15 +237,16 @@ namespace Content.Server.Atmos.EntitySystems var movedMoles = 0f; var absMovedMoles = 0f; - for(var i = 0; i < Atmospherics.TotalNumberOfGases; i++) + for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) { var thisValue = receiver.Moles[i]; var sharerValue = sharer.Moles[i]; var delta = (thisValue - sharerValue) / (atmosAdjacentTurfs + 1); - if (!(MathF.Abs(delta) >= Atmospherics.GasMinMoles)) continue; + if (!(MathF.Abs(delta) >= Atmospherics.GasMinMoles)) + continue; if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider) { - var gasHeatCapacity = delta * GasSpecificHeats[i]; + var gasHeatCapacity = delta * GasMolarHeatCapacities[i]; if (delta > 0) { heatCapacityToSharer += gasHeatCapacity; @@ -240,8 +257,10 @@ namespace Content.Server.Atmos.EntitySystems } } - if (!receiver.Immutable) receiver.Moles[i] -= delta; - if (!sharer.Immutable) sharer.Moles[i] += delta; + if (!receiver.Immutable) + receiver.Moles[i] -= delta; + if (!sharer.Immutable) + sharer.Moles[i] += delta; movedMoles += delta; absMovedMoles += MathF.Abs(delta); } @@ -256,16 +275,21 @@ namespace Content.Server.Atmos.EntitySystems // Transfer of thermal energy (via changed heat capacity) between self and sharer. if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity) { - receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * tileReceiver.AirArchived.Temperature) + (heatCapacitySharerToThis * tileSharer.AirArchived.Temperature)) / newHeatCapacity; + receiver.Temperature = + (oldHeatCapacity * receiver.Temperature - + heatCapacityToSharer * tileReceiver.AirArchived.Temperature + + heatCapacitySharerToThis * tileSharer.AirArchived.Temperature) / newHeatCapacity; } if (!sharer.Immutable && newSharerHeatCapacity > Atmospherics.MinimumHeatCapacity) { - sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * tileSharer.AirArchived.Temperature) + (heatCapacityToSharer * tileReceiver.AirArchived.Temperature)) / newSharerHeatCapacity; + sharer.Temperature = + (oldSharerHeatCapacity * sharer.Temperature - + heatCapacitySharerToThis * tileSharer.AirArchived.Temperature + + heatCapacityToSharer * tileReceiver.AirArchived.Temperature) / newSharerHeatCapacity; } // Thermal energy of the system (self and sharer) is unchanged. - if (MathF.Abs(oldSharerHeatCapacity) > Atmospherics.MinimumHeatCapacity) { if (MathF.Abs(newSharerHeatCapacity / oldSharerHeatCapacity - 1) < 0.1) @@ -275,12 +299,25 @@ namespace Content.Server.Atmos.EntitySystems } } - if (!(temperatureDelta > Atmospherics.MinimumTemperatureToMove) && - !(MathF.Abs(movedMoles) > Atmospherics.MinimumMolesDeltaToMove)) return 0f; + // If we didn't move enough air or if the temperature difference is too small, + // we don't consider there to be a pressure difference. + // TODO ATMOS: This is a very weird early return, please figure out why this exists because this logic seems to be double checked + // in a lot of other places (ex. HighPressureDelta). + if (!(absTemperatureDelta > Atmospherics.MinimumTemperatureToMove) && + !(MathF.Abs(movedMoles) > Atmospherics.MinimumMolesDeltaToMove)) + return 0f; + var moles = receiver.TotalMoles; var theirMoles = sharer.TotalMoles; - return (tileReceiver.AirArchived.Temperature * (moles + movedMoles)) - (tileSharer.AirArchived.Temperature * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume; + /* + To get the pressure delta: + PV = nRT + P = nRT / V + \Delta P = ((n_1 * T_1) - (n_2 * T_2)) * R / V + */ + return (tileReceiver.AirArchived.Temperature * (moles + movedMoles) - + tileSharer.AirArchived.Temperature * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume; } /// diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 6eb46b1946..6f881a32e7 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -64,17 +64,17 @@ namespace Content.Server.Atmos.EntitySystems private void OnPressureProtectionEquipped(EntityUid uid, PressureProtectionComponent pressureProtection, GotEquippedEvent args) { - if (TryComp(args.Equipee, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot)) + if (TryComp(args.EquipTarget, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot)) { - UpdateCachedResistances(args.Equipee, barotrauma); + UpdateCachedResistances(args.EquipTarget, barotrauma); } } private void OnPressureProtectionUnequipped(EntityUid uid, PressureProtectionComponent pressureProtection, GotUnequippedEvent args) { - if (TryComp(args.Equipee, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot)) + if (TryComp(args.EquipTarget, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot)) { - UpdateCachedResistances(args.Equipee, barotrauma); + UpdateCachedResistances(args.EquipTarget, barotrauma); } } diff --git a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs index a3fe2400d0..9dcb4d13a2 100644 --- a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs @@ -8,7 +8,6 @@ using Content.Shared.Interaction; using Content.Shared.NodeContainer; using JetBrains.Annotations; using Robust.Server.GameObjects; -using static Content.Shared.Atmos.Components.GasAnalyzerComponent; namespace Content.Server.Atmos.EntitySystems; @@ -101,6 +100,7 @@ public sealed class GasAnalyzerSystem : EntitySystem _popup.PopupEntity(Loc.GetString("gas-analyzer-shutoff"), user.Value, user.Value); entity.Comp.Enabled = false; + entity.Comp.User = null; Dirty(entity); _appearance.SetData(entity.Owner, GasAnalyzerVisuals.Enabled, entity.Comp.Enabled); RemCompDeferred(entity.Owner); @@ -139,15 +139,15 @@ public sealed class GasAnalyzerSystem : EntitySystem return false; // check if the user has walked away from what they scanned - if (component.Target.HasValue) + if (component.Target.HasValue && component.User.HasValue) { // Listen! Even if you don't want the Gas Analyzer to work on moving targets, you should use // this code to determine if the object is still generally in range so that the check is consistent with the code // in OnAfterInteract() and also consistent with interaction code in general. - if (!_interactionSystem.InRangeUnobstructed((component.User, null), (component.Target.Value, null))) + if (!_interactionSystem.InRangeUnobstructed((component.User.Value, null), (component.Target.Value, null))) { - if (component.User is { } userId && component.Enabled) - _popup.PopupEntity(Loc.GetString("gas-analyzer-object-out-of-range"), userId, userId); + if (component.Enabled) + _popup.PopupEntity(Loc.GetString("gas-analyzer-object-out-of-range"), component.User.Value, component.User.Value); component.Target = null; } @@ -256,18 +256,17 @@ public sealed class GasAnalyzerSystem : EntitySystem { var gases = new List(); + if (mixture == null) + return []; + for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) { - var gas = _atmo.GetGas(i); + var gas = (Gas)i; - if (mixture?[i] <= UIMinMoles) + if (mixture[i] <= UIMinMoles) continue; - if (mixture != null) - { - var gasName = Loc.GetString(gas.Name); - gases.Add(new GasEntry(gasName, mixture[i], gas.Color)); - } + gases.Add(new GasEntry(gas, mixture[i])); } var gasesOrdered = gases.OrderByDescending(gas => gas.Amount); diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index 420db3f732..fba6c145ae 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -1,238 +1,169 @@ -using Content.Server.Explosion.EntitySystems; +using System.Numerics; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Atmos.EntitySystems; using Content.Shared.Cargo; +using Content.Shared.Popups; using Content.Shared.Throwing; using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; +using Robust.Shared.Physics.Systems; using Robust.Shared.Random; -using Robust.Shared.Configuration; -using Content.Shared.CCVar; -namespace Content.Server.Atmos.EntitySystems +namespace Content.Server.Atmos.EntitySystems; + +[UsedImplicitly] +public sealed class GasTankSystem : SharedGasTankSystem { - [UsedImplicitly] - public sealed class GasTankSystem : SharedGasTankSystem + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + + private const float MinimumSoundValvePressure = 21.3f; // Arbitrary number + + private const float ReleaseArea = 0.0005f; // About 5cm^2 + + // A vector bias for throwing our gas tanks in radians. Averages about -43 degrees since the sprite is at a 45-degree angle. + private static readonly Vector2 ThrowVector = new (-1.0f, -0.5f); + + public override void Initialize() { - [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; - [Dependency] private readonly ExplosionSystem _explosions = default!; - [Dependency] private readonly SharedAudioSystem _audioSys = default!; - [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; + base.Initialize(); + SubscribeLocalEvent(OnParentChange); + SubscribeLocalEvent(OnAnalyzed); + SubscribeLocalEvent(OnGasTankPrice); + } - private const float TimerDelay = 0.5f; - private float _timer = 0f; - private const float MinimumSoundValvePressure = 10.0f; - private float _maxExplosionRange; - - public override void Initialize() + protected override void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + // Release gas if valve is open + // Disconnect from internals if valve is open + if (entity.Comp.ReleaseValveOpen) { - base.Initialize(); - SubscribeLocalEvent(OnParentChange); - SubscribeLocalEvent(OnAnalyzed); - SubscribeLocalEvent(OnGasTankPrice); - Subs.CVar(_cfg, CCVars.AtmosTankFragment, UpdateMaxRange, true); + DisconnectFromInternals(entity); + ReleaseGas(entity, args.dt); } - - private void UpdateMaxRange(float value) + else if (entity.Comp.CheckUser) { - _maxExplosionRange = value; - } - - public override void UpdateUserInterface(Entity ent) - { - var (owner, component) = ent; - _ui.SetUiState(owner, SharedGasTankUiKey.Key, - new GasTankBoundUserInterfaceState - { - TankPressure = component.Air?.Pressure ?? 0, - }); - } - - private void OnParentChange(EntityUid uid, GasTankComponent component, ref EntParentChangedMessage args) - { - // When an item is moved from hands -> pockets, the container removal briefly dumps the item on the floor. - // So this is a shitty fix, where the parent check is just delayed. But this really needs to get fixed - // properly at some point. - component.CheckUser = true; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - _timer += frameTime; - - if (_timer < TimerDelay) - return; - - _timer -= TimerDelay; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) + entity.Comp.CheckUser = false; + if (Transform(entity).ParentUid != entity.Comp.User) { - var gasTank = (uid, comp); - if (comp.IsValveOpen && !comp.IsLowPressure && comp.OutputPressure > 0) - { - ReleaseGas(gasTank); - } - - if (comp.CheckUser) - { - comp.CheckUser = false; - if (Transform(uid).ParentUid != comp.User) - { - DisconnectFromInternals(gasTank); - continue; - } - } - - if (comp.Air != null) - { - _atmosphereSystem.React(comp.Air, comp); - } - - CheckStatus(gasTank); - - if ((comp.IsConnected || comp.IsValveOpen) && _ui.IsUiOpen(uid, SharedGasTankUiKey.Key)) - { - UpdateUserInterface(gasTank); - } + DisconnectFromInternals(entity); } } - private void ReleaseGas(Entity gasTank) - { - var removed = RemoveAirVolume(gasTank, gasTank.Comp.ValveOutputRate * TimerDelay); - var environment = _atmosphereSystem.GetContainingMixture(gasTank.Owner, false, true); - if (environment != null) + Atmos.React(entity.Comp.Air, entity.Comp); + + if ((entity.Comp.IsConnected || entity.Comp.ReleaseValveOpen) && UI.IsUiOpen(entity.Owner, SharedGasTankUiKey.Key)) + UpdateUserInterface(entity); + } + + public override void UpdateUserInterface(Entity ent) + { + var (owner, component) = ent; + UI.SetUiState(owner, + SharedGasTankUiKey.Key, + new GasTankBoundUserInterfaceState { - _atmosphereSystem.Merge(environment, removed); - } - var strength = removed.TotalMoles * MathF.Sqrt(removed.Temperature); - var dir = _random.NextAngle().ToWorldVec(); - _throwing.TryThrow(gasTank, dir * strength, strength); - if (gasTank.Comp.OutputPressure >= MinimumSoundValvePressure) - _audioSys.PlayPvs(gasTank.Comp.RuptureSound, gasTank); - } + TankPressure = component.Air.Pressure + }); + } - public GasMixture? RemoveAir(Entity gasTank, float amount) - { - var gas = gasTank.Comp.Air?.Remove(amount); - CheckStatus(gasTank); - return gas; - } + private void OnParentChange(EntityUid uid, GasTankComponent component, ref EntParentChangedMessage args) + { + // When an item is moved from hands -> pockets, the container removal briefly dumps the item on the floor. + // So this is a shitty fix, where the parent check is just delayed. But this really needs to get fixed + // properly at some point. + component.CheckUser = true; + } - public GasMixture RemoveAirVolume(Entity gasTank, float volume) - { - var component = gasTank.Comp; - if (component.Air == null) - return new GasMixture(volume); + /// + /// Tries to release gas through the pressure release valve. + /// + /// The gas tank entity releasing gas + /// The amount of time since the last update + /// + private void ReleaseGas(Entity entity, float dt) + { + var environment = _atmosphereSystem.GetContainingMixture(entity.Owner, false, true); - var molesNeeded = component.OutputPressure * volume / (Atmospherics.R * component.Air.Temperature); + var deltaP = environment == null + ? entity.Comp.Air.Pressure + : entity.Comp.Air.Pressure - environment.Pressure; - var air = RemoveAir(gasTank, molesNeeded); + // Cap deltaP by the maximum output pressure of the tank. + if (deltaP < entity.Comp.SafetyPressure) + deltaP = Math.Min(entity.Comp.ReleasePressure, deltaP); - if (air != null) - air.Volume = volume; - else - return new GasMixture(volume); + var removed = _atmosphereSystem.FlowGas(entity.Comp.Air, deltaP, dt, ReleaseArea); - return air; - } + if (removed == null) + return; - public void AssumeAir(Entity ent, GasMixture giver) - { - _atmosphereSystem.Merge(ent.Comp.Air, giver); - CheckStatus(ent); - } + if (environment != null) + _atmosphereSystem.Merge(environment, removed); - public void CheckStatus(Entity ent) - { - var (owner, component) = ent; - if (component.Air == null) - return; + // If we wouldn't produce a sound, don't throw or play a sound. + if (removed.Pressure < MinimumSoundValvePressure) + return; - var pressure = component.Air.Pressure; + Audio.PlayPvs(entity.Comp.ReleaseSound, entity); - if (pressure > component.TankFragmentPressure && _maxExplosionRange > 0) - { - // Give the gas a chance to build up more pressure. - for (var i = 0; i < 3; i++) - { - _atmosphereSystem.React(component.Air, component); - } + var strength = Atmos.GetOverPressure(removed) * Atmospherics.kPaToKg_m2; - pressure = component.Air.Pressure; - var range = MathF.Sqrt((pressure - component.TankFragmentPressure) / component.TankFragmentScale); + if (strength <= 0) + return; - // Let's cap the explosion, yeah? - // !1984 - range = Math.Min(Math.Min(range, GasTankComponent.MaxExplosionRange), _maxExplosionRange); + // TODO: I hate throwing system. I shouldn't need to do this boilerplate to get a nice looking throw + var rot = _xform.GetWorldRotation(entity); + var ang = _random.NextAngle(rot + ThrowVector.X, rot + ThrowVector.Y); - _explosions.TriggerExplosive(owner, radius: range); + // We bias by angle to make sure it doesn't rotate too much and flies relatively straight. + _physics.ApplyAngularImpulse(entity, (float)(strength * ang)); - return; - } + // TODO ATMOS: If we can predict ReleaseGas at some point, we should have this apply an impulse to a person holding this gas tank. + _throwing.TryThrow(entity, ang.ToWorldVec() * strength, strength, doSpin: false); + } - if (pressure > component.TankRupturePressure) - { - if (component.Integrity <= 0) - { - var environment = _atmosphereSystem.GetContainingMixture(owner, false, true); - if (environment != null) - _atmosphereSystem.Merge(environment, component.Air); + public GasMixture RemoveAirOutput(Entity gasTank, float volume) + { + var mixture = _atmosphereSystem.RemoveVolumeAtPressure(gasTank.Comp.Air, volume, gasTank.Comp.ReleasePressure); + // We resize the volume because lungs breathe in volume rather than being pressure based atm. + // If we don't do this, they won't consume all of the outputted gas or will consume way too much. + mixture.Volume = volume; + return mixture; + } - _audioSys.PlayPvs(component.RuptureSound, Transform(owner).Coordinates, AudioParams.Default.WithVariation(0.125f)); + public GasMixture RemoveAir(Entity gasTank, float amount) + { + return gasTank.Comp.Air.Remove(amount); + } - QueueDel(owner); - return; - } + protected override void SafetyMeasures(Entity entity) + { + if (entity.Comp.ReleaseValveOpen) + return; - component.Integrity--; - return; - } + ToggleValve(entity); + if (entity.Comp.SafetyAlert != null) + _popup.PopupEntity(Loc.GetString(entity.Comp.SafetyAlert), entity, PopupType.LargeCaution); - if (pressure > component.TankLeakPressure) - { - if (component.Integrity <= 0) - { - var environment = _atmosphereSystem.GetContainingMixture(owner, false, true); - if (environment == null) - return; + Dirty(entity); + } - var leakedGas = component.Air.RemoveRatio(0.25f); - _atmosphereSystem.Merge(environment, leakedGas); - } - else - { - component.Integrity--; - } + /// + /// Returns the gas mixture for the gas analyzer + /// + private void OnAnalyzed(EntityUid uid, GasTankComponent component, GasAnalyzerScanEvent args) + { + args.GasMixtures ??= new List<(string, GasMixture?)>(); + args.GasMixtures.Add((Name(uid), component.Air)); + } - return; - } - - if (component.Integrity < 3) - component.Integrity++; - } - - /// - /// Returns the gas mixture for the gas analyzer - /// - private void OnAnalyzed(EntityUid uid, GasTankComponent component, GasAnalyzerScanEvent args) - { - args.GasMixtures ??= new List<(string, GasMixture?)>(); - args.GasMixtures.Add((Name(uid), component.Air)); - } - - private void OnGasTankPrice(EntityUid uid, GasTankComponent component, ref PriceCalculationEvent args) - { - args.Price += _atmosphereSystem.GetPrice(component.Air); - } + private void OnGasTankPrice(EntityUid uid, GasTankComponent component, ref PriceCalculationEvent args) + { + args.Price += _atmosphereSystem.GetPrice(component.Air); } } diff --git a/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs b/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs index 57d48b64e9..1e3ffc869e 100644 --- a/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs +++ b/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs @@ -1,20 +1,19 @@ -using Content.Shared.Atmos; +namespace Content.Server.Atmos.Piping.Binary.Components; -namespace Content.Server.Atmos.Piping.Binary.Components +/// +/// Defines a passive gate, which equalizes gas from +/// inlet to outlet, but does not allow gas to flow from outlet to inlet. +/// +[RegisterComponent] +public sealed partial class GasPassiveGateComponent : Component { - [RegisterComponent] - public sealed partial class GasPassiveGateComponent : Component - { - [ViewVariables(VVAccess.ReadWrite)] - [DataField("inlet")] - public string InletName { get; set; } = "inlet"; + [DataField("inlet")] + public string InletName = "inlet"; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("outlet")] - public string OutletName { get; set; } = "outlet"; + [DataField("outlet")] + public string OutletName = "outlet"; - [ViewVariables(VVAccess.ReadOnly)] - [DataField("flowRate")] - public float FlowRate { get; set; } = 0; - } + [ViewVariables(VVAccess.ReadOnly)] + [DataField] + public float FlowRate; } diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs index b3da10513e..abbddd72fe 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs @@ -2,87 +2,57 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Binary.Components; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.Nodes; -using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Examine; using JetBrains.Annotations; -namespace Content.Server.Atmos.Piping.Binary.EntitySystems +namespace Content.Server.Atmos.Piping.Binary.EntitySystems; + +[UsedImplicitly] +public sealed class GasPassiveGateSystem : EntitySystem { - [UsedImplicitly] - public sealed class GasPassiveGateSystem : EntitySystem + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + + public override void Initialize() { - [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; - [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + base.Initialize(); - public override void Initialize() + SubscribeLocalEvent(OnPassiveGateUpdated); + SubscribeLocalEvent(OnExamined); + } + + private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, ref AtmosDeviceUpdateEvent args) + { + if (!_nodeContainer.TryGetNodes(uid, gate.InletName, gate.OutletName, out PipeNode? inlet, out PipeNode? outlet)) + return; + + // ReSharper disable thrice InconsistentNaming + var P1 = inlet.Air.Pressure; + var P2 = outlet.Air.Pressure; + var V1 = inlet.Air.Volume; + var pressureDelta = P1 - P2; + + var dt = args.dt; + float dV = 0; + if (pressureDelta > 0 && P1 > 0) { - base.Initialize(); + var transferFrac = _atmosphereSystem.FractionToEqualizePressure(inlet.Air, outlet.Air); + dV = transferFrac * V1; - SubscribeLocalEvent(OnPassiveGateUpdated); - SubscribeLocalEvent(OnExamined); + // Actually transfer the gas. + _atmosphereSystem.Merge(outlet.Air, inlet.Air.RemoveRatio(transferFrac)); } - private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, ref AtmosDeviceUpdateEvent args) - { - if (!_nodeContainer.TryGetNodes(uid, gate.InletName, gate.OutletName, out PipeNode? inlet, out PipeNode? outlet)) - return; + gate.FlowRate = AtmosphereSystem.ExponentialMovingAverage(dV, gate.FlowRate, dt); + } - var n1 = inlet.Air.TotalMoles; - var n2 = outlet.Air.TotalMoles; - var P1 = inlet.Air.Pressure; - var P2 = outlet.Air.Pressure; - var V1 = inlet.Air.Volume; - var V2 = outlet.Air.Volume; - var T1 = inlet.Air.Temperature; - var T2 = outlet.Air.Temperature; - var pressureDelta = P1 - P2; + private void OnExamined(Entity gate, ref ExaminedEvent args) + { + if (!Transform(gate).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. + return; - float dt = args.dt; - float dV = 0; - var denom = (T1*V2 + T2*V1); - - if (pressureDelta > 0 && P1 > 0 && denom > 0) - { - // Calculate the number of moles to transfer to equalize the final pressure of - // both sides of the valve. You can derive this equation yourself by solving - // the equations: - // - // P_inlet,final = P_outlet,final (pressure equilibrium) - // n_inlet,initial + n_outlet,initial = n_inlet,final + n_outlet,final (mass conservation) - // - // These simplifying assumptions allow an easy closed-form solution: - // - // T_inlet,initial = T_inlet,final - // T_outlet,initial = T_outlet,final - // - // If you don't want to push through the math, just know that this behaves like a - // pump that can equalize pressure instantly, i.e. much faster than pressure or - // volume pumps. - var transferMoles = n1 - (n1+n2)*T2*V1 / denom; - - // Get the volume transfered to update our flow meter. - // When you remove x from one side and add x to the other the total difference is 2x. - // Also account for atmos speedup so that measured flow rate matches the setting on the volume pump. - dV = 2*transferMoles*Atmospherics.R*T1/P1 / _atmosphereSystem.Speedup; - - // Actually transfer the gas. - _atmosphereSystem.Merge(outlet.Air, inlet.Air.Remove(transferMoles)); - } - - // Update transfer rate with an exponential moving average. - var tau = 1; // Time constant (averaging time) in seconds - var a = dt/tau; - gate.FlowRate = a*dV/tau + (1-a)*gate.FlowRate; // in L/sec - } - - private void OnExamined(Entity gate, ref ExaminedEvent args) - { - if (!Comp(gate).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. - return; - - var str = Loc.GetString("gas-passive-gate-examined", ("flowRate", $"{gate.Comp.FlowRate:0.#}")); - args.PushMarkup(str); - } + var str = Loc.GetString("gas-passive-gate-examined", ("flowRate", $"{gate.Comp.FlowRate:0.#}")); + args.PushMarkup(str); } } diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs index 38f3ca8a47..d9128f68ae 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs @@ -8,86 +8,90 @@ using Content.Shared.Atmos.Piping.Components; using Content.Shared.Audio; using JetBrains.Annotations; -namespace Content.Server.Atmos.Piping.Trinary.EntitySystems +namespace Content.Server.Atmos.Piping.Trinary.EntitySystems; + +[UsedImplicitly] +public sealed class PressureControlledValveSystem : EntitySystem { - [UsedImplicitly] - public sealed class PressureControlledValveSystem : EntitySystem + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + + public override void Initialize() { - [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; - [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnUpdate); + SubscribeLocalEvent(OnFilterLeaveAtmosphere); + } - public override void Initialize() + private void OnInit(EntityUid uid, PressureControlledValveComponent comp, ComponentInit args) + { + UpdateAppearance(uid, comp); + } + + private void OnUpdate(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceUpdateEvent args) + { + if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.ControlName, comp.OutletName, out PipeNode? inletNode, out PipeNode? controlNode, out PipeNode? outletNode)) { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnUpdate); - SubscribeLocalEvent(OnFilterLeaveAtmosphere); + _ambientSoundSystem.SetAmbience(uid, false); + comp.Enabled = false; + return; } - private void OnInit(EntityUid uid, PressureControlledValveComponent comp, ComponentInit args) + // If output is higher than input, flip input/output to enable bidirectional flow. + if (outletNode.Air.Pressure > inletNode.Air.Pressure) { - UpdateAppearance(uid, comp); + PipeNode temp = outletNode; + outletNode = inletNode; + inletNode = temp; } - private void OnUpdate(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceUpdateEvent args) - { - if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.ControlName, comp.OutletName, out PipeNode? inletNode, out PipeNode? controlNode, out PipeNode? outletNode)) - { - _ambientSoundSystem.SetAmbience(uid, false); - comp.Enabled = false; - return; - } - - // If output is higher than input, flip input/output to enable bidirectional flow. - if (outletNode.Air.Pressure > inletNode.Air.Pressure) - { - PipeNode temp = outletNode; - outletNode = inletNode; - inletNode = temp; - } - - float control = (controlNode.Air.Pressure - outletNode.Air.Pressure) - comp.Threshold; - float transferRate; - if (control < 0) - { - comp.Enabled = false; - transferRate = 0; - } - else - { - comp.Enabled = true; - transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate * _atmosphereSystem.PumpSpeedup()); - } - UpdateAppearance(uid, comp); - - // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. - var transferVolume = transferRate * args.dt; - if (transferVolume <= 0) - { - _ambientSoundSystem.SetAmbience(uid, false); - return; - } - - _ambientSoundSystem.SetAmbience(uid, true); - var removed = inletNode.Air.RemoveVolume(transferVolume); - _atmosphereSystem.Merge(outletNode.Air, removed); - } - - private void OnFilterLeaveAtmosphere(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceDisabledEvent args) + float control = (controlNode.Air.Pressure - outletNode.Air.Pressure) - comp.Threshold; + float transferRate; + if (control < 0) { comp.Enabled = false; - UpdateAppearance(uid, comp); - _ambientSoundSystem.SetAmbience(uid, false); + transferRate = 0; } - - private void UpdateAppearance(EntityUid uid, PressureControlledValveComponent? comp = null, AppearanceComponent? appearance = null) + else { - if (!Resolve(uid, ref comp, ref appearance, false)) - return; - - _appearance.SetData(uid, FilterVisuals.Enabled, comp.Enabled, appearance); + comp.Enabled = true; + transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate * _atmosphereSystem.PumpSpeedup()); } + UpdateAppearance(uid, comp); + + // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. + var transferVolume = transferRate * args.dt; + if (transferVolume <= 0) + { + _ambientSoundSystem.SetAmbience(uid, false); + return; + } + + // clamp to equalization so we don't overshoot (happens with silly euler) + var maxFrac = _atmosphereSystem.FractionToEqualizePressure(inletNode.Air, outletNode.Air); + var maxVol = inletNode.Air.Volume * maxFrac; + var clampedVolume = Math.Min(transferVolume, maxVol); + + _ambientSoundSystem.SetAmbience(uid, true); + var removed = inletNode.Air.RemoveVolume(clampedVolume); + _atmosphereSystem.Merge(outletNode.Air, removed); + } + + private void OnFilterLeaveAtmosphere(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceDisabledEvent args) + { + comp.Enabled = false; + UpdateAppearance(uid, comp); + _ambientSoundSystem.SetAmbience(uid, false); + } + + private void UpdateAppearance(EntityUid uid, PressureControlledValveComponent? comp = null, AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref comp, ref appearance, false)) + return; + + _appearance.SetData(uid, FilterVisuals.Enabled, comp.Enabled, appearance); } } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 06807640a8..60e6f5a20d 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -5,11 +5,11 @@ using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Piping.Binary.Components; -using Content.Shared.Atmos.Piping.Components; using Content.Shared.Atmos.Piping.Unary.Systems; using Content.Shared.Cargo; using Content.Shared.Database; using Content.Shared.NodeContainer; +using Content.Shared.Popups; using GasCanisterComponent = Content.Shared.Atmos.Piping.Unary.Components.GasCanisterComponent; namespace Content.Server.Atmos.Piping.Unary.EntitySystems; @@ -17,14 +17,16 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems; public sealed class GasCanisterSystem : SharedGasCanisterSystem { [Dependency] private readonly AtmosphereSystem _atmos = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + private const float ReleaseArea = 0.05f; // 500cm^2 Number chosen for balance reasons. It's quite large, but so are gas canisters (holding 1.5 cubic meters of gas!) public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnCanisterUpdated); SubscribeLocalEvent(CalculateCanisterPrice); SubscribeLocalEvent(OnAnalyzed); } @@ -64,64 +66,84 @@ public sealed class GasCanisterSystem : SharedGasCanisterSystem tankPressure = tankComponent.Air.Pressure; } - UI.SetUiState(uid, GasCanisterUiKey.Key, + UI.SetUiState(uid, + GasCanisterUiKey.Key, new GasCanisterBoundUserInterfaceState(canister.Air.Pressure, portStatus, tankPressure)); } - private void OnCanisterUpdated(EntityUid uid, GasCanisterComponent canister, ref AtmosDeviceUpdateEvent args) + protected override void SafetyMeasures(Entity entity) { - _atmos.React(canister.Air, canister); - - if (!TryComp(uid, out var nodeContainer) - || !TryComp(uid, out var appearance)) + if (entity.Comp.SafetyValveOpen) return; - if (!_nodeContainer.TryGetNode(nodeContainer, canister.PortName, out PortablePipeNode? portNode)) + ToggleSafetyValve(entity, open: true); + if (entity.Comp.SafetyAlert != null) + _popup.PopupEntity(Loc.GetString(entity.Comp.SafetyAlert), entity, PopupType.LargeCaution); + } + + private void ToggleSafetyValve(Entity entity, bool open) + { + entity.Comp.SafetyValveOpen = open; + Audio.PlayPvs(entity.Comp.ValveSound, entity); + } + + protected override void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + _atmos.React(entity.Comp.Air, entity.Comp); + + if (!TryComp(entity, out var nodeContainer) + || !TryComp(entity, out var appearance)) + return; + + if (!_nodeContainer.TryGetNode(nodeContainer, entity.Comp.PortName, out PortablePipeNode? portNode)) return; if (portNode.NodeGroup is PipeNet {NodeCount: > 1} net) { - MixContainerWithPipeNet(canister.Air, net.Air); + MixContainerWithPipeNet(entity.Comp.Air, net.Air); } - // Release valve is open, release gas. - if (canister.ReleaseValve) + // If safety valve is open, we release gas through there ignoring other outputs. + if (entity.Comp.SafetyValveOpen) { - if (canister.GasTankSlot.Item != null) - { - var gasTank = Comp(canister.GasTankSlot.Item.Value); - _atmos.ReleaseGasTo(canister.Air, gasTank.Air, canister.ReleasePressure); - } - else - { - var environment = _atmos.GetContainingMixture(uid, args.Grid, args.Map, false, true); - _atmos.ReleaseGasTo(canister.Air, environment, canister.ReleasePressure); - } + var environment = _atmos.GetContainingMixture(entity.Owner, args.Grid, args.Map, false, true); + _atmos.FlowGas(entity.Comp.Air, environment, args.dt, ReleaseArea); + if (entity.Comp.Air.Pressure < entity.Comp.SafetyPressure) + ToggleSafetyValve(entity, false); + } + else if (entity.Comp.ReleaseValveOpen) // Release valve is open, release gas. + { + var output = entity.Comp.GasTankSlot.Item == null + ? _atmos.GetContainingMixture(entity.Owner, args.Grid, args.Map, false, true) + : CompOrNull(entity.Comp.GasTankSlot.Item.Value)?.Air; + + // Only let gas flow one way! + _atmos.ReleaseGasTo(entity.Comp.Air, output, entity.Comp.ReleasePressure); } // If last pressure is very close to the current pressure, do nothing. - if (MathHelper.CloseToPercent(canister.Air.Pressure, canister.LastPressure)) + if (MathHelper.CloseToPercent(entity.Comp.Air.Pressure, entity.Comp.LastPressure)) return; - DirtyUI(uid, canister, nodeContainer); + DirtyUI(entity, entity.Comp, nodeContainer); - canister.LastPressure = canister.Air.Pressure; + entity.Comp.LastPressure = entity.Comp.Air.Pressure; - if (canister.Air.Pressure < 10) + if (entity.Comp.Air.Pressure < 10) { - _appearance.SetData(uid, GasCanisterVisuals.PressureState, 0, appearance); + _appearance.SetData(entity, GasCanisterVisuals.PressureState, 0, appearance); } - else if (canister.Air.Pressure < Atmospherics.OneAtmosphere) + else if (entity.Comp.Air.Pressure < Atmospherics.OneAtmosphere) { - _appearance.SetData(uid, GasCanisterVisuals.PressureState, 1, appearance); + _appearance.SetData(entity, GasCanisterVisuals.PressureState, 1, appearance); } - else if (canister.Air.Pressure < (15 * Atmospherics.OneAtmosphere)) + else if (entity.Comp.Air.Pressure < (15 * Atmospherics.OneAtmosphere)) { - _appearance.SetData(uid, GasCanisterVisuals.PressureState, 2, appearance); + _appearance.SetData(entity, GasCanisterVisuals.PressureState, 2, appearance); } else { - _appearance.SetData(uid, GasCanisterVisuals.PressureState, 3, appearance); + _appearance.SetData(entity, GasCanisterVisuals.PressureState, 3, appearance); } } diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index c470ae3f0d..da7ac33479 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -1,14 +1,9 @@ using Content.Server.Atmos.EntitySystems; -using Content.Server.Body.Components; -using Content.Server.Popups; using Content.Shared.Alert; -using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Systems; -using Content.Shared.DoAfter; using Content.Shared.Internals; -using Content.Shared.Inventory; using Content.Shared.Roles; namespace Content.Server.Body.Systems; @@ -59,8 +54,9 @@ public sealed class InternalsSystem : SharedInternalsSystem if (AreInternalsWorking(ent)) { var gasTank = Comp(ent.Comp.GasTankEntity!.Value); - args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), args.Respirator.BreathVolume); - // TODO: Should listen to gas tank updates instead I guess? + // TODO ATMOS: TODO BODY: This is an incredibly stupid workaround to stop Urist McHands from horfing all 5 litres of the gas tank in 10 seconds. + args.Gas = _gasTank.RemoveAirOutput((ent.Comp.GasTankEntity.Value, gasTank), args.Respirator.BreathVolume); + // TODO ATMOS: Should listen to gas tank updates instead I guess? _alerts.ShowAlert(ent.Owner, ent.Comp.InternalsAlert, GetSeverity(ent)); } } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 5fb2461133..2a1fb1a3ea 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -429,6 +429,7 @@ public sealed class PlantHolderSystem : EntitySystem && component.WeedLevel >= component.Seed.WeedHighLevelThreshold) { Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); + EnsureUniqueSeed(uid, component); component.Seed.TurnIntoKudzu = false; component.Health = 0; } diff --git a/Content.Server/Cargo/Components/RandomPriceComponent.cs b/Content.Server/Cargo/Components/RandomPriceComponent.cs new file mode 100644 index 0000000000..3ece8334e6 --- /dev/null +++ b/Content.Server/Cargo/Components/RandomPriceComponent.cs @@ -0,0 +1,38 @@ +using Content.Server.Cargo.Systems; + +namespace Content.Server.Cargo.Components; + +/// +/// Adds a random value between 0 and X to an entity's sell value. +/// +[RegisterComponent, Access(typeof(PricingSystem))] +public sealed partial class RandomPriceComponent : Component +{ + /// + /// The max random price the entity may be priced at. Non-inclusive. + /// + [DataField(required: true)] + public double MaxRandomPrice; + + /// + /// How the random pricing modifier (0.0 - 1.0) should be distributed. + /// + [DataField] + public RandomPricingCurve PricingCurve = RandomPricingCurve.Cubed; + + /// + /// The generated price for the specific entity. + /// + [DataField] + public double? RandomPrice = null; +} + +/// +/// The random distribution used when generating a random price. +/// +public enum RandomPricingCurve +{ + Linear, + Squared, + Cubed, +} diff --git a/Content.Server/Cargo/Components/TradeStationComponent.cs b/Content.Server/Cargo/Components/TradeStationComponent.cs deleted file mode 100644 index 0422470cc9..0000000000 --- a/Content.Server/Cargo/Components/TradeStationComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Content.Server.Cargo.Components; - -/// -/// Target for approved orders to spawn at. -/// -[RegisterComponent] -public sealed partial class TradeStationComponent : Component -{ - -} diff --git a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs index 351451123c..635eadea1d 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs @@ -6,6 +6,7 @@ using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Events; using Content.Shared.Cargo.Prototypes; using Content.Shared.CCVar; +using Content.Shared.HijackBeacon; using Robust.Shared.Audio; using Robust.Shared.Prototypes; @@ -14,7 +15,7 @@ namespace Content.Server.Cargo.Systems; public sealed partial class CargoSystem { /* - * Handles cargo shuttle / trade mechanics. + * Handles automated trade station / trade mechanics. */ private static readonly SoundPathSpecifier ApproveSound = new("/Audio/Effects/Cargo/ping.ogg"); diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index 9f3a4d5bf3..a35f1e7ea3 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Access.Systems; using Content.Shared.Administration.Logs; using Content.Server.Radio.EntitySystems; using Content.Shared.Cargo; +using Content.Shared.Cargo.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Mobs.Components; using Content.Shared.Paper; diff --git a/Content.Server/Cargo/Systems/PricingSystem.cs b/Content.Server/Cargo/Systems/PricingSystem.cs index 0101b913d6..bbf8176035 100644 --- a/Content.Server/Cargo/Systems/PricingSystem.cs +++ b/Content.Server/Cargo/Systems/PricingSystem.cs @@ -8,13 +8,14 @@ using Content.Shared.Chemistry.Reagent; using Content.Shared.Materials; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Research.Prototypes; using Content.Shared.Stacks; using Robust.Shared.Console; using Robust.Shared.Containers; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; +using Robust.Shared.Random; using Robust.Shared.Utility; -using Content.Shared.Research.Prototypes; namespace Content.Server.Cargo.Systems; @@ -24,6 +25,7 @@ namespace Content.Server.Cargo.Systems; public sealed class PricingSystem : EntitySystem { [Dependency] private readonly IConsoleHost _consoleHost = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; @@ -32,6 +34,8 @@ public sealed class PricingSystem : EntitySystem public override void Initialize() { SubscribeLocalEvent(CalculateMobPrice); + SubscribeLocalEvent(SetRandomPrice); + SubscribeLocalEvent(CalculateRandomPrice); _consoleHost.RegisterCommand("appraisegrid", "Calculates the total value of the given grids.", @@ -95,6 +99,37 @@ public sealed class PricingSystem : EntitySystem args.Price += component.Price * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty); } + private void SetRandomPrice(Entity entity, ref MapInitEvent args) + { + if (entity.Comp.RandomPrice == null) + { + var modifier = _random.NextDouble(); + switch (entity.Comp.PricingCurve) + { + default: + case RandomPricingCurve.Linear: + break; + case RandomPricingCurve.Squared: + modifier = modifier * modifier; + break; + case RandomPricingCurve.Cubed: + modifier = modifier * modifier * modifier; + break; + } + + entity.Comp.RandomPrice = modifier * entity.Comp.MaxRandomPrice; + } + } + + private void CalculateRandomPrice(Entity entity, ref PriceCalculationEvent args) + { + // TODO: Estimated pricing. + if (args.Handled) + return; + + args.Price += entity.Comp.RandomPrice ?? 0; + } + private double GetSolutionPrice(Entity entity) { if (Comp(entity).EntityLifeStage < EntityLifeStage.MapInitialized) diff --git a/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs index b1b23b2732..e7687ec3df 100644 --- a/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs +++ b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Station.Systems; using Content.Shared.CartridgeLoader; using Content.Shared.CartridgeLoader.Cartridges; using Content.Shared.CCVar; +using Content.Shared.CrewManifest; using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.Prototypes; @@ -60,7 +61,13 @@ public sealed class CrewManifestCartridgeSystem : EntitySystem var owningStation = _stationSystem.GetOwningStation(uid); if (owningStation is null) + { + // Display "loading failed" message + var failureMessage = Loc.GetString("crew-manifest-cartridge-loading-failed"); + var failureState = new CrewManifestUiState(failureMessage, new CrewManifestEntries()); + _cartridgeLoader.UpdateCartridgeUiState(loaderUid, failureState); return; + } var (stationName, entries) = _crewManifest.GetCrewManifest(owningStation.Value); diff --git a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs index 7252a59f60..e4dc4fb8f9 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; +using Content.Shared.DragDrop; using Content.Shared.FixedPoint; using Content.Shared.Labels.EntitySystems; using Content.Shared.Storage; @@ -49,6 +50,9 @@ namespace Content.Server.Chemistry.EntitySystems SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(SubscribeUpdateUiState); + // Subscribing to DragDropTargetEvent is a quick fix to ensure the UI updates when fluids are dragged and dropped into the ChemMaster, since Shared.Fluids.EntitySystems.SolutionDumpingSystem.cs bypasses UpdateChemicals(). + // TODO: Remove when proper support for infinite volume solutions is added. + SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(OnSetModeMessage); diff --git a/Content.Server/Cloning/CloningSystem.Subscriptions.cs b/Content.Server/Cloning/CloningSystem.Subscriptions.cs index a05c7069f0..995f3e32eb 100644 --- a/Content.Server/Cloning/CloningSystem.Subscriptions.cs +++ b/Content.Server/Cloning/CloningSystem.Subscriptions.cs @@ -1,15 +1,19 @@ using Content.Server.Forensics; using Content.Server.Speech.EntitySystems; using Content.Shared.Cloning.Events; +using Content.Shared.Clothing.Components; +using Content.Shared.Clothing.EntitySystems; using Content.Shared.FixedPoint; using Content.Shared.Inventory; using Content.Shared.Labels.Components; using Content.Shared.Labels.EntitySystems; using Content.Shared.Movement.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; using Content.Shared.Movement.Systems; using Content.Shared.Paper; -using Content.Shared.Stacks; using Content.Shared.Speech.Components; +using Content.Shared.Stacks; using Content.Shared.Storage; using Content.Shared.Store; using Content.Shared.Store.Components; @@ -32,6 +36,8 @@ public sealed partial class CloningSystem [Dependency] private readonly PaperSystem _paper = default!; [Dependency] private readonly VocalSystem _vocal = default!; [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; + [Dependency] private readonly SharedChameleonClothingSystem _chameleonClothing = default!; + [Dependency] private readonly PullingSystem _pulling = default!; public override void Initialize() { @@ -47,13 +53,15 @@ public sealed partial class CloningSystem SubscribeLocalEvent(OnCloneItemPaper); SubscribeLocalEvent(OnCloneItemForensics); SubscribeLocalEvent(OnCloneItemStore); + SubscribeLocalEvent(OnCloneItemChameleon); // These are for cloning components that cannot be cloned using CopyComp. // Put them into CloningSettingsPrototype.EventComponents to have them be applied to the clone. SubscribeLocalEvent(OnCloneVocal); SubscribeLocalEvent(OnCloneStorage); SubscribeLocalEvent(OnCloneInventory); - SubscribeLocalEvent(OnCloneInventory); + SubscribeLocalEvent(OnCloneMovementSpeedModifier); + SubscribeLocalEvent(OnClonePuller); } private void OnCloneItemStack(Entity ent, ref CloningItemEvent args) @@ -96,6 +104,12 @@ public sealed partial class CloningSystem } } + private void OnCloneItemChameleon(Entity ent, ref CloningItemEvent args) + { + // copy the prototype the original is mimicing + _chameleonClothing.SetSelectedPrototype(args.CloneUid, ent.Comp.Default); + } + private void OnCloneVocal(Entity ent, ref CloningEvent args) { if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) @@ -120,11 +134,19 @@ public sealed partial class CloningSystem _inventory.CopyComponent(ent.AsNullable(), args.CloneUid); } - private void OnCloneInventory(Entity ent, ref CloningEvent args) + private void OnCloneMovementSpeedModifier(Entity ent, ref CloningEvent args) { if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; _movementSpeedModifier.CopyComponent(ent.AsNullable(), args.CloneUid); } + + private void OnClonePuller(Entity ent, ref CloningEvent args) + { + if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) + return; + + _pulling.CopyPullerComponent(ent.AsNullable(), args.CloneUid); + } } diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index 3dc2224d00..9f07612e2b 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -1,16 +1,15 @@ -using Content.Server.Humanoid; using Content.Shared.Administration.Logs; using Content.Shared.Body; using Content.Shared.Cloning; using Content.Shared.Cloning.Events; using Content.Shared.Database; using Content.Shared.Humanoid; +using Content.Shared.IdentityManagement; using Content.Shared.Inventory; using Content.Shared.Implants; using Content.Shared.Implants.Components; using Content.Shared.NameModifier.EntitySystems; using Content.Shared.StatusEffect; -using Content.Shared.StatusEffectNew.Components; using Content.Shared.Storage; using Content.Shared.Storage.EntitySystems; using Content.Shared.Whitelist; @@ -40,13 +39,14 @@ public sealed partial class CloningSystem : SharedCloningSystem [Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!; [Dependency] private readonly SharedVisualBodySystem _visualBody = default!; [Dependency] private readonly NameModifierSystem _nameMod = default!; + [Dependency] private readonly IdentitySystem _identity = default!; [Dependency] private readonly SharedSkillsSystem _skills = default!; // WL-Skills - [Dependency] private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!; //TODO: This system has to support both the old and new status effect systems, until the old is able to be fully removed. - /// - /// Spawns a clone of the given humanoid mob at the specified location or in nullspace. - /// - public bool TryCloning(EntityUid original, MapCoordinates? coords, ProtoId settingsId, [NotNullWhen(true)] out EntityUid? clone) + public override bool TryCloning( + EntityUid original, + MapCoordinates? coords, + ProtoId settingsId, + [NotNullWhen(true)] out EntityUid? clone) { clone = null; if (!_prototype.Resolve(settingsId, out var settings)) @@ -58,10 +58,13 @@ public sealed partial class CloningSystem : SharedCloningSystem if (!_prototype.Resolve(humanoid.Species, out var speciesPrototype)) return false; // invalid species - var attemptEv = new CloningAttemptEvent(settings); - RaiseLocalEvent(original, ref attemptEv); - if (attemptEv.Cancelled && !settings.ForceCloning) - return false; // cannot clone, for example due to the unrevivable trait + if (!settings.ForceCloning) + { + var attemptEv = new CloningAttemptEvent(settings); + RaiseLocalEvent(original, ref attemptEv); + if (attemptEv.Cancelled) + return false; // cannot clone, for example due to the unrevivable trait + } clone = coords == null ? Spawn(speciesPrototype.Prototype) : Spawn(speciesPrototype.Prototype, coords.Value); _visualBody.CopyAppearanceFrom(original, clone.Value); @@ -90,13 +93,17 @@ public sealed partial class CloningSystem : SharedCloningSystem var originalName = _nameMod.GetBaseName(original); // Set the clone's name. The raised events will also adjust their PDA and ID card names. - _metaData.SetEntityName(clone.Value, originalName); + _metaData.SetEntityName(clone.Value, originalName, raiseEvents: settings.RaiseEntityRenamedEvent); + _identity.QueueIdentityUpdate(clone.Value); // We have to manually refresh the identity in case we did not raise events. _adminLogger.Add(LogType.Chat, LogImpact.Medium, $"The body of {original:player} was cloned as {clone.Value:player}"); return true; } - public override void CloneComponents(EntityUid original, EntityUid clone, ProtoId settings) + public override void CloneComponents( + EntityUid original, + EntityUid clone, + ProtoId settings) { if (!_prototype.Resolve(settings, out var proto)) return; @@ -104,7 +111,10 @@ public sealed partial class CloningSystem : SharedCloningSystem CloneComponents(original, clone, proto); } - public override void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings) + public override void CloneComponents( + EntityUid original, + EntityUid clone, + CloningSettingsPrototype settings) { var componentsToCopy = settings.Components; var componentsToEvent = settings.EventComponents; @@ -164,7 +174,12 @@ public sealed partial class CloningSystem : SharedCloningSystem /// Copies the equipment the original has to the clone. /// This uses the original prototype of the items, so any changes to components that are done after spawning are lost! /// - public void CopyEquipment(Entity original, Entity clone, SlotFlags slotFlags, EntityWhitelist? whitelist = null, EntityWhitelist? blacklist = null) + public override void CopyEquipment( + Entity original, + Entity clone, + SlotFlags slotFlags, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) { if (!Resolve(original, ref original.Comp) || !Resolve(clone, ref clone.Comp)) return; @@ -182,15 +197,11 @@ public sealed partial class CloningSystem : SharedCloningSystem } } - /// - /// Copies an item and its storage recursively, placing all items at the same position in grid storage. - /// This uses the original prototype of the items, so any changes to components that are done after spawning are lost! - /// - /// - /// This is not perfect and only considers item in storage containers. - /// Some components have their own additional spawn logic on map init, so we cannot just copy all containers. - /// - public EntityUid? CopyItem(EntityUid original, EntityCoordinates coords, EntityWhitelist? whitelist = null, EntityWhitelist? blacklist = null) + public override EntityUid? CopyItem( + EntityUid original, + EntityCoordinates coords, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) { // we use a whitelist and blacklist to be sure to exclude any problematic entities if (!_whitelist.CheckBoth(original, blacklist, whitelist)) @@ -226,12 +237,11 @@ public sealed partial class CloningSystem : SharedCloningSystem return spawned; } - /// - /// Copies an item's storage recursively to another storage. - /// The storage grids should have the same shape or it will drop on the floor. - /// Basically the same as CopyItem, but we don't copy the outermost container. - /// - public void CopyStorage(Entity original, Entity target, EntityWhitelist? whitelist = null, EntityWhitelist? blacklist = null) + public override void CopyStorage( + Entity original, + Entity target, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) { if (!Resolve(original, ref original.Comp, false) || !Resolve(target, ref target.Comp, false)) return; @@ -250,17 +260,12 @@ public sealed partial class CloningSystem : SharedCloningSystem } } - /// - /// Copies all implants from one mob to another. - /// Might result in duplicates if the target already has them. - /// Can copy the storage inside a storage implant according to a whitelist and blacklist. - /// - /// Entity to copy implants from. - /// Entity to copy implants to. - /// If true will copy storage of the implants (E.g storage implant) - /// Whitelist for the storage copy (If copyStorage is true) - /// Blacklist for the storage copy (If copyStorage is true) - public void CopyImplants(Entity original, EntityUid target, bool copyStorage = false, EntityWhitelist? whitelist = null, EntityWhitelist? blacklist = null) + public override void CopyImplants( + Entity original, + EntityUid target, + bool copyStorage = false, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) { if (!Resolve(original, ref original.Comp, false)) return; // they don't have any implants to copy! @@ -287,35 +292,5 @@ public sealed partial class CloningSystem : SharedCloningSystem if (copyStorage) CopyStorage(originalImplant, targetImplant.Value, whitelist, blacklist); // only needed for storage implants } - - } - - /// - /// Scans all permanent status effects applied to the original entity and transfers them to the clone. - /// - public void CopyStatusEffects(Entity original, Entity target) - { - if (!Resolve(original, ref original.Comp, false)) - return; - - if (original.Comp.ActiveStatusEffects is null) - return; - - foreach (var effect in original.Comp.ActiveStatusEffects.ContainedEntities) - { - if (!TryComp(effect, out var effectComp)) - continue; - - //We are not interested in temporary effects, only permanent ones. - if (effectComp.EndEffectTime is not null) - continue; - - var effectProto = Prototype(effect); - - if (effectProto is null) - continue; - - _statusEffects.TrySetStatusEffectDuration(target, effectProto); - } } } diff --git a/Content.Server/Cloning/RandomCloneSpawnerSystem.cs b/Content.Server/Cloning/RandomCloneSpawnerSystem.cs index a645a10890..a18ccd2e71 100644 --- a/Content.Server/Cloning/RandomCloneSpawnerSystem.cs +++ b/Content.Server/Cloning/RandomCloneSpawnerSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Cloning.Components; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; +using Content.Shared.Objectives.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -15,7 +16,7 @@ public sealed class RandomCloneSpawnerSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly TargetSystem _target = default!; public override void Initialize() { @@ -34,7 +35,7 @@ public sealed class RandomCloneSpawnerSystem : EntitySystem return; } - var allHumans = _mind.GetAliveHumans(); + var allHumans = _target.GetAliveHumans(); if (allHumans.Count == 0) return; diff --git a/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs index 645b47d180..8a654b4ab9 100644 --- a/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs +++ b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs @@ -22,7 +22,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem private void OnMapInit(EntityUid uid, ChameleonClothingComponent component, MapInitEvent args) { - SetSelectedPrototype(uid, component.Default, true, component); + SetSelectedPrototype(uid, component.Default, true, component: component); } private void OnSelected(EntityUid uid, ChameleonClothingComponent component, ChameleonPrototypeSelectedMessage args) @@ -39,10 +39,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem UI.SetUiState(uid, ChameleonUiKey.Key, state); } - /// - /// Change chameleon items name, description and sprite to mimic other entity prototype. - /// - public override void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false, + public override void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false, bool validate = true, ChameleonClothingComponent? component = null) { if (!Resolve(uid, ref component, false)) @@ -56,8 +53,10 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem // make sure that it is valid change if (string.IsNullOrEmpty(protoId) || !_proto.TryIndex(protoId, out EntityPrototype? proto)) return; - if (!IsValidTarget(proto, component.Slot, component.RequireTag)) + + if (validate && !IsValidTarget(proto, component.Slot, component.RequireTag)) return; + component.Default = protoId; UpdateIdentityBlocker(uid, component, proto); diff --git a/Content.Server/Clothing/Systems/OutfitSystem.cs b/Content.Server/Clothing/Systems/OutfitSystem.cs index b1efbffd60..c1187657fd 100644 --- a/Content.Server/Clothing/Systems/OutfitSystem.cs +++ b/Content.Server/Clothing/Systems/OutfitSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Hands.Systems; using Content.Server.Preferences.Managers; +using Content.Server.Storage.EntitySystems; using Content.Shared.Access.Components; using Content.Shared.Clothing; using Content.Shared.Hands.Components; @@ -11,6 +12,8 @@ using Content.Shared.Preferences; using Content.Shared.Preferences.Loadouts; using Content.Shared.Roles; using Content.Shared.Station; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Storage; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -23,6 +26,8 @@ public sealed class OutfitSystem : EntitySystem [Dependency] private readonly HandsSystem _handSystem = default!; [Dependency] private readonly InventorySystem _invSystem = default!; [Dependency] private readonly SharedStationSpawningSystem _spawningSystem = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; + [Dependency] private readonly StorageSystem _storageSystem = default!; public bool SetOutfit(EntityUid target, string gear, Action? onEquipped = null, bool unremovable = false) { @@ -68,9 +73,35 @@ public sealed class OutfitSystem : EntitySystem } } + var coords = Transform(target).Coordinates; + foreach (var (slotName, storageContainers) in startingGear.Storage) + { + if (storageContainers.Count == 0) + continue; + + if (!_invSystem.TryGetSlotEntity(target, slotName, out var slotEnt)) + continue; + + if (TryComp(slotEnt, out var storage)) + { + foreach (var entProto in storageContainers) + { + var spawnedEntity = SpawnAtPosition(entProto, coords); + _storageSystem.Insert(slotEnt.Value, spawnedEntity, out _, user: null, storageComp: storage, playSound: false); + } + } + else if (TryComp(slotEnt, out var itemSlots)) + { + foreach (var entProto in storageContainers) + { + var spawnedEntity = SpawnAtPosition(entProto, coords); + _itemSlotsSystem.TryInsertEmpty((slotEnt.Value, itemSlots), spawnedEntity, null, excludeUserAudio: true); + } + } + } + if (TryComp(target, out HandsComponent? handsComponent)) { - var coords = Comp(target).Coordinates; foreach (var prototype in startingGear.Inhand) { var inhandEntity = Spawn(prototype, coords); diff --git a/Content.Server/Containers/ContainerCommand.cs b/Content.Server/Containers/ContainerCommand.cs new file mode 100644 index 0000000000..914c9025a0 --- /dev/null +++ b/Content.Server/Containers/ContainerCommand.cs @@ -0,0 +1,87 @@ +using System.Linq; +using Content.Server.Administration; +using Content.Shared.Administration; +using Robust.Shared.Containers; +using Robust.Shared.Toolshed; + +namespace Content.Server.Containers; + +[ToolshedCommand, AdminCommand(AdminFlags.Debug)] +public sealed class ContainerCommand : ToolshedCommand +{ + private SharedContainerSystem? _container; + + [CommandImplementation("contents")] + public IEnumerable ContainerQuery([PipedArgument] IEnumerable storageEnts, string id) => + storageEnts.SelectMany(x => ContainerQueryBase(x, id)); + + + public IEnumerable ContainerQueryBase(EntityUid ent, string id) + { + _container ??= GetSys(); + + if (!_container.TryGetContainer(ent, id, out var container)) + return []; + + return container.ContainedEntities; + } + + [CommandImplementation("get")] + public IEnumerable ContainerGet([PipedArgument] IEnumerable storageEnts, string id) => + storageEnts.Select(x => ContainerGetBase(x, id)).Where(s => s != null).Select(s => s!); + + [CommandImplementation("id")] + public IEnumerable ContainerId([PipedArgument] IEnumerable containers) => + containers.Select(x => x.ID); + + + public BaseContainer? ContainerGetBase(EntityUid ent, string id) + { + _container ??= GetSys(); + + if (!_container.TryGetContainer(ent, id, out var container)) + return null; + + return container; + } + + [CommandImplementation("insertmultiple")] + public BaseContainer ContainerInsert([PipedArgument] BaseContainer container, bool doForce, IEnumerable ents) + { + _container ??= GetSys(); + + foreach (var ent in ents) + { + if (doForce) + { + _container.Insert(ent, container, null, true); + } + else + { + _container.InsertOrDrop(ent, container); + } + } + return container; + } + + [CommandImplementation("insert")] + public BaseContainer ContainerInsert([PipedArgument] BaseContainer container, bool doForce, EntityUid ent) + { + return ContainerInsert(container, doForce, [ent]); + } + + [CommandImplementation("list")] + public IEnumerable ContainerList([PipedArgument] EntityUid ent) + { + _container ??= GetSys(); + + return _container.GetAllContainers(ent).Select(container => container.ID); + } + [CommandImplementation("getall")] + public IEnumerable ContainerGetAll([PipedArgument] EntityUid ent) + { + _container ??= GetSys(); + + return _container.GetAllContainers(ent); + } +} diff --git a/Content.Server/Corvax/GuideGenerator/EntityEntry.cs b/Content.Server/Corvax/GuideGenerator/EntityEntry.cs deleted file mode 100644 index 9ddd167328..0000000000 --- a/Content.Server/Corvax/GuideGenerator/EntityEntry.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System.Linq; -using System.Text.Json.Serialization; -using Content.Shared.Labels.Components; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Markdown.Mapping; -using Robust.Shared.Serialization.Markdown.Sequence; -using Robust.Shared.Serialization.Markdown.Value; - -namespace Content.Server.Corvax.GuideGenerator; - -public sealed class EntityEntry -{ - private static string[]? GetRootParents(EntityPrototype proto, IPrototypeManager prototypeManager) - { - if (proto.Parents is not { Length: > 0 }) - return null; - - var roots = new HashSet(StringComparer.Ordinal); - var visited = new HashSet(StringComparer.Ordinal); - - static IEnumerable GetParentIds(MappingDataNode mapping) - { - if (mapping.TryGet("parent", out ValueDataNode? parentValue)) - { - if (!string.IsNullOrWhiteSpace(parentValue.Value)) - yield return parentValue.Value; - - yield break; - } - - if (!mapping.TryGet("parent", out SequenceDataNode? parentSequence)) - yield break; - - foreach (var parentNode in parentSequence) - { - if (parentNode is not ValueDataNode valueNode) - continue; - - if (!string.IsNullOrWhiteSpace(valueNode.Value)) - yield return valueNode.Value; - } - } - - void Visit(string id) - { - if (!visited.Add(id)) - return; - - if (!YAMLEntry.TryGetRawMapping(prototypeManager, typeof(EntityPrototype), id, out var mapping) || - mapping == null) - { - roots.Add(id); - return; - } - - var parents = GetParentIds(mapping).ToArray(); - if (parents.Length == 0) - { - roots.Add(id); - return; - } - - foreach (var parent in parents) - { - Visit(parent); - } - } - - foreach (var parent in proto.Parents) - { - Visit(parent); - } - - return roots.Count > 0 ? roots.OrderBy(x => x, StringComparer.Ordinal).ToArray() : null; - } - - [JsonPropertyName("id")] - public string Id { get; } - - [JsonPropertyName("name")] - public string Name { get; } - - [JsonPropertyName("desc")] - public string Description { get; } - - [JsonPropertyName("suffix")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Suffix { get; } - - [JsonPropertyName("label")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Label { get; } - - [JsonPropertyName("parents")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string[]? Parents { get; } - - public EntityEntry(EntityPrototype proto) - { - var prototypeManager = IoCManager.Resolve(); - var loc = IoCManager.Resolve(); - - Id = proto.ID; - Name = TextTools.CapitalizeString(TextTools.GetDisplayName(proto, prototypeManager, loc)); - Description = proto.Description; - Suffix = string.IsNullOrWhiteSpace(proto.EditorSuffix) ? null : proto.EditorSuffix; - Parents = GetRootParents(proto, prototypeManager); - - Label = proto.Components.Values - .Select(x => x.Component) - .OfType() - .Select(lc => lc.CurrentLabel) - .Where(label => !string.IsNullOrEmpty(label)) - .Select(label => Loc.GetString(label!)) - .FirstOrDefault(); - } -} diff --git a/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs index 24c21cd229..d7562222d3 100644 --- a/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs @@ -1,12 +1,53 @@ using System.IO; using System.Linq; using System.Text.Json; +using System.Text.Encodings.Web; +using System.Text.Json.Serialization; +using Content.Shared.Labels.Components; using Robust.Shared.Prototypes; namespace Content.Server.Corvax.GuideGenerator; public sealed class EntityJsonGenerator { + [JsonPropertyName("id")] + public string Id { get; } + + [JsonPropertyName("name")] + public string Name { get; } + + [JsonPropertyName("desc")] + public string Description { get; } + + [JsonPropertyName("suffix")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Suffix { get; } + + [JsonPropertyName("label")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Label { get; } + + public EntityJsonGenerator(EntityPrototype proto) + { + var prototypeManager = IoCManager.Resolve(); + var loc = IoCManager.Resolve(); + + Id = proto.ID; + Name = TextTools.CapitalizeString(TextTools.GetDisplayName(proto, prototypeManager, loc)); + Description = proto.Description; + + if (!string.IsNullOrWhiteSpace(proto.EditorSuffix)) + Suffix = TextTools.GetEditorSuffix(proto.EditorSuffix, EntityNameDuplicatesJsonGenerator.IgnoredSuffixTokens, TextTools.NormalizeSuffixToken); + + Label = proto.Components.Values + .Select(x => x.Component) + .OfType() + .Select(lc => lc.CurrentLabel) + .Where(label => !string.IsNullOrEmpty(label)) + .Select(label => Loc.GetString(label!)) + .FirstOrDefault(); + } + public static void PublishJson(StreamWriter file) { var prototype = IoCManager.Resolve(); @@ -14,12 +55,13 @@ public sealed class EntityJsonGenerator prototype .EnumeratePrototypes() .Where(x => !x.Abstract) - .Select(x => new EntityEntry(x)) + .Select(x => new EntityJsonGenerator(x)) .ToDictionary(x => x.Id, x => x); var serializeOptions = new JsonSerializerOptions { WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; file.Write(JsonSerializer.Serialize(prototypes, serializeOptions)); diff --git a/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs index 117936e32b..cabbfff030 100644 --- a/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs @@ -2,19 +2,28 @@ using System.IO; using System.Linq; using System.Text.Encodings.Web; using System.Text.Json; +using Content.Shared.Actions.Components; using Content.Shared.Corvax.GuideGenerator; using Content.Shared.Labels.Components; -using Robust.Shared.Physics; using Robust.Shared.Prototypes; namespace Content.Server.Corvax.GuideGenerator; public static class EntityNameDuplicatesJsonGenerator { + private const string AbilitySuffix = "(способность)"; + + public static readonly string[] AllowedNameComponents = + [ + "Fixtures", + "Physics", + "Action" + ]; + // Suffix parts that should be ignored when building display names. - private static readonly HashSet IgnoredSuffixTokens = new(StringComparer.OrdinalIgnoreCase) + public static readonly HashSet IgnoredSuffixTokens = new(StringComparer.OrdinalIgnoreCase) { - "DO NOT MAP", + "do not map", "не маппить", }; @@ -31,10 +40,11 @@ public static class EntityNameDuplicatesJsonGenerator public static bool MatchesEntityNameFilter(EntityPrototype proto, IReadOnlySet allowedIds) { - var compFactory = IoCManager.Resolve(); + var hasAllowedComponent = AllowedNameComponents.Any(proto.Components.ContainsKey); + return !proto.Abstract && - proto.TryGetComponent(out _, compFactory) && - EntityProjectHelper.MatchesAllowedIds(proto.ID, allowedIds); + hasAllowedComponent && + EntityProjectHelper.MatchesAllowedIds(proto.ID, allowedIds); } private static Dictionary> GetDuplicatesName( @@ -42,6 +52,7 @@ public static class EntityNameDuplicatesJsonGenerator bool duplicatesOnly) { var loc = IoCManager.Resolve(); + var compFactory = IoCManager.Resolve(); var allowedIds = EntityProjectGenerator.GetProjectEntityIds(); return prototypeManager .EnumeratePrototypes() @@ -50,24 +61,17 @@ public static class EntityNameDuplicatesJsonGenerator { var name = TextTools.CapitalizeString(TextTools.GetDisplayName(p, prototypeManager, loc)); + if (p.TryGetComponent(out _, compFactory)) + name = $"{name} {AbilitySuffix}"; + var label = GetLabel(p); var rawSuffix = p.EditorSuffix; - var suffix = string.Empty; - if (!string.IsNullOrWhiteSpace(rawSuffix)) - { - var parts = rawSuffix - .Split([',', ';'], StringSplitOptions.RemoveEmptyEntries) - .Select(part => part.Trim()) - .Where(part => !IgnoredSuffixTokens.Contains(part)) - .ToArray(); - - if (parts.Length > 0) - suffix = string.Join(", ", parts).ToLowerInvariant(); - } + var suffix = TextTools.GetEditorSuffix(rawSuffix, IgnoredSuffixTokens, TextTools.NormalizeSuffixToken); return (Name: name, Label: label, Suffix: suffix); }) + .Where(g => !string.IsNullOrWhiteSpace(g.Key.Name)) .Where(g => !duplicatesOnly || g.Count() > 1) .ToDictionary( g => diff --git a/Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs new file mode 100644 index 0000000000..d7064185ce --- /dev/null +++ b/Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs @@ -0,0 +1,101 @@ +using System.Linq; +using System.Text.Json.Serialization; +using System.IO; +using System.Text.Json; +using System.Text.Encodings.Web; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Markdown.Mapping; +using Robust.Shared.Serialization.Markdown.Sequence; +using Robust.Shared.Serialization.Markdown.Value; + +namespace Content.Server.Corvax.GuideGenerator; + +public sealed class EntityParentJsonGenerator +{ + [JsonPropertyName("id")] + public string Id { get; } + + [JsonPropertyName("parents")] + public string[] Parents { get; } + + public EntityParentJsonGenerator(EntityPrototype proto) + { + var prototypeManager = IoCManager.Resolve(); + + Id = proto.ID; + Parents = GetParents(proto, prototypeManager); + } + + public static string[] GetParents(EntityPrototype proto, IPrototypeManager prototypeManager) + { + var parents = new HashSet(StringComparer.Ordinal); + var visited = new HashSet(StringComparer.Ordinal); + + void Visit(string id) + { + if (string.IsNullOrWhiteSpace(id) || !visited.Add(id)) + return; + + parents.Add(id); + + if (!YAMLEntry.TryGetRawMapping(prototypeManager, typeof(EntityPrototype), id, out var mapping) || + mapping == null) + { + return; + } + + foreach (var parent in GetParentIds(mapping)) + { + Visit(parent); + } + } + + foreach (var parent in proto.Parents ?? []) + { + Visit(parent); + } + + return parents.OrderBy(x => x, StringComparer.Ordinal).ToArray(); + } + + private static IEnumerable GetParentIds(MappingDataNode mapping) + { + if (mapping.TryGet("parent", out ValueDataNode? parentValue)) + { + if (!string.IsNullOrWhiteSpace(parentValue.Value)) + yield return parentValue.Value; + + yield break; + } + + if (!mapping.TryGet("parent", out SequenceDataNode? parentSequence)) + yield break; + + foreach (var parentNode in parentSequence) + { + if (parentNode is not ValueDataNode valueNode) + continue; + + if (!string.IsNullOrWhiteSpace(valueNode.Value)) + yield return valueNode.Value; + } + } + + public static void PublishJson(StreamWriter file) + { + var prototypeManager = IoCManager.Resolve(); + var prototypes = prototypeManager + .EnumeratePrototypes() + .Where(x => !x.Abstract) + .Select(x => new EntityParentJsonGenerator(x)) + .ToDictionary(x => x.Id, x => x.Parents); + + var serializeOptions = new JsonSerializerOptions + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + file.Write(JsonSerializer.Serialize(prototypes, serializeOptions)); + } +} diff --git a/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs index 2549b4c5ae..e4d07d2e4c 100644 --- a/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs @@ -3,9 +3,7 @@ using System.Reflection; using System.Text.Encodings.Web; using System.Text.Json; using Robust.Shared.ContentPack; -using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Markdown.Mapping; using Robust.Shared.Utility; @@ -154,8 +152,9 @@ public static class PrototypeJsonGenerator } } - return type.GetCustomAttributes(inherit: true).Any(attr => + return type.GetCustomAttributes(inherit: true) + .Any(attr => attr.GetType().Name is nameof(DataDefinitionAttribute) or nameof(SerializableAttribute)) - && HasUnsafeSerializedDataField(type, visited); + && HasUnsafeSerializedDataField(type, visited); } } diff --git a/Content.Server/Corvax/GuideGenerator/TextTools.cs b/Content.Server/Corvax/GuideGenerator/TextTools.cs index 021261ebb4..e1da52f623 100644 --- a/Content.Server/Corvax/GuideGenerator/TextTools.cs +++ b/Content.Server/Corvax/GuideGenerator/TextTools.cs @@ -1,3 +1,5 @@ +using System.Linq; +using System.Text.RegularExpressions; using Robust.Shared.Prototypes; namespace Content.Server.Corvax.GuideGenerator; @@ -60,4 +62,33 @@ public sealed class TextTools return proto.Name; } + + private static readonly Regex SuffixTokenEdgeGarbageRegex = + new(@"^[\p{P}\p{S}\s]+|[\p{P}\p{S}\s]+$", RegexOptions.Compiled); + + public static string NormalizeSuffixToken(string token) + { + return string.IsNullOrWhiteSpace(token) + ? string.Empty + : SuffixTokenEdgeGarbageRegex.Replace(token, string.Empty); + } + + public static string GetEditorSuffix( + string? editorSuffix, + IReadOnlySet ignoredTokens, + Func normalizeToken) + { + if (string.IsNullOrWhiteSpace(editorSuffix)) + return string.Empty; + + var parts = editorSuffix + .Split([',', ';'], StringSplitOptions.RemoveEmptyEntries) + .Select(part => part.Trim()) + .Where(part => !ignoredTokens.Contains(normalizeToken(part))) + .ToArray(); + + return parts.Length > 0 + ? string.Join(", ", parts).ToLowerInvariant() + : string.Empty; + } } diff --git a/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs b/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs index b5578164b1..a8a75f825a 100644 --- a/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs +++ b/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs @@ -36,7 +36,7 @@ public static class YAMLEntry { mapping = null; - if (KindsField?.GetValue(proto) is not object kinds) + if (KindsField?.GetValue(proto) is not { } kinds) return false; var itemProperty = kinds.GetType().GetProperty("Item"); diff --git a/Content.Server/Doors/Systems/AirlockSystem.cs b/Content.Server/Doors/Systems/AirlockSystem.cs index 7dfdebf367..40ecdc21be 100644 --- a/Content.Server/Doors/Systems/AirlockSystem.cs +++ b/Content.Server/Doors/Systems/AirlockSystem.cs @@ -1,81 +1,5 @@ -using Content.Server.Power.Components; -using Content.Server.Wires; -using Content.Shared.DeviceLinking.Events; -using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; -using Content.Shared.Interaction; -using Content.Shared.Power; -using Content.Shared.Wires; -using Robust.Shared.Player; namespace Content.Server.Doors.Systems; -public sealed class AirlockSystem : SharedAirlockSystem -{ - [Dependency] private readonly WiresSystem _wiresSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSignalReceived); - - SubscribeLocalEvent(OnPowerChanged); - SubscribeLocalEvent(OnActivate, before: new[] { typeof(DoorSystem) }); - } - - private void OnSignalReceived(EntityUid uid, AirlockComponent component, ref SignalReceivedEvent args) - { - if (args.Port == component.AutoClosePort && component.AutoClose) - { - component.AutoClose = false; - Dirty(uid, component); - } - } - - private void OnPowerChanged(EntityUid uid, AirlockComponent component, ref PowerChangedEvent args) - { - component.Powered = args.Powered; - Dirty(uid, component); - - if (!TryComp(uid, out DoorComponent? door)) - return; - - if (!args.Powered) - { - // stop any scheduled auto-closing - if (door.State == DoorState.Open) - DoorSystem.SetNextStateChange(uid, null); - } - else - { - UpdateAutoClose(uid, door: door); - } - } - - private void OnActivate(EntityUid uid, AirlockComponent component, ActivateInWorldEvent args) - { - if (args.Handled || !args.Complex) - return; - - if (TryComp(uid, out var panel) && - panel.Open && - TryComp(args.User, out var actor)) - { - if (TryComp(uid, out var wiresPanelSecurity) && - !wiresPanelSecurity.WiresAccessible) - return; - - _wiresSystem.OpenUserInterface(uid, actor.PlayerSession); - args.Handled = true; - return; - } - - if (component.KeepOpenIfClicked && component.AutoClose) - { - // Disable auto close - component.AutoClose = false; - Dirty(uid, component); - } - } -} +public sealed class AirlockSystem : SharedAirlockSystem; diff --git a/Content.Server/Dragon/Components/DragonComponent.cs b/Content.Server/Dragon/Components/DragonComponent.cs index 80461e156a..c634836b5a 100644 --- a/Content.Server/Dragon/Components/DragonComponent.cs +++ b/Content.Server/Dragon/Components/DragonComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chemistry.Components; using Content.Shared.NPC.Prototypes; using Robust.Shared.Audio; using Robust.Shared.Prototypes; @@ -65,5 +66,17 @@ namespace Content.Server.Dragon /// [DataField] public ProtoId Faction = "Dragon"; + + /// + /// The smoke to spawn upon rift timeout death. + /// + [DataField] + public EntProtoId SmokePrototype = "BloodSmoke"; + + /// + /// The solution to place into the smoke (mostly just needed for color) + /// + [DataField] + public Solution SmokeSolution = new ([new("Blood", 1)]); } } diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index 1c838939ec..8bc5fd83b1 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -1,8 +1,11 @@ +using Content.Server.Fluids.EntitySystems; using Content.Server.Objectives.Components; using Content.Server.Objectives.Systems; using Content.Server.Popups; using Content.Shared.Actions; +using Content.Shared.Chemistry.Components; using Content.Shared.Dragon; +using Content.Shared.Gibbing; using Content.Shared.Maps; using Content.Shared.Mind; using Content.Shared.Mind.Components; @@ -12,6 +15,7 @@ using Content.Shared.Movement.Systems; using Content.Shared.NPC.Systems; using Content.Shared.Zombies; using Robust.Shared.Audio.Systems; +using Robust.Shared.Map; using Robust.Shared.Map.Components; namespace Content.Server.Dragon; @@ -29,6 +33,8 @@ public sealed partial class DragonSystem : EntitySystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly TurfSystem _turf = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; + [Dependency] private readonly SmokeSystem _smoke = default!; private EntityQuery _objQuery; @@ -96,11 +102,14 @@ public sealed partial class DragonSystem : EntitySystem if (!_mobState.IsDead(uid)) comp.RiftAccumulator += frameTime; - // Delete it, naughty dragon! + // Gib it, naughty dragon! if (comp.RiftAccumulator >= comp.RiftMaxAccumulator) { - Roar(uid, comp); - QueueDel(uid); + Roar(uid, comp, Transform(uid).Coordinates); + var smoke = Spawn(comp.SmokePrototype, Transform(uid).Coordinates); + if (TryComp(smoke, out var smokeComp)) + _smoke.StartSmoke(smoke, comp.SmokeSolution, smokeComp.Duration, smokeComp.SpreadAmount, smokeComp); + _gibbing.Gib(uid); } } } @@ -200,10 +209,15 @@ public sealed partial class DragonSystem : EntitySystem _faction.AddFaction(ent.Owner, ent.Comp.Faction); } - private void Roar(EntityUid uid, DragonComponent comp) + private void Roar(EntityUid uid, DragonComponent comp, EntityCoordinates? coords = null) { if (comp.SoundRoar != null) - _audio.PlayPvs(comp.SoundRoar, uid); + { + if (coords != null) + _audio.PlayPvs(comp.SoundRoar, coords.Value); + else + _audio.PlayPvs(comp.SoundRoar, uid); + } } /// diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzuEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzuEntityEffectSystem.cs new file mode 100644 index 0000000000..f681afda5e --- /dev/null +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzuEntityEffectSystem.cs @@ -0,0 +1,16 @@ +using Content.Server.Botany.Components; +using Content.Shared.EntityEffects; +using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; + +namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; + +public sealed partial class PlantRemoveKudzuEntityEffectSystem : EntityEffectSystem +{ + protected override void Effect(Entity entity, ref EntityEffectEvent args) + { + if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) + return; + + entity.Comp.Seed.TurnIntoKudzu = false; + } +} diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index b8cb088531..cea568ce15 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -162,6 +162,7 @@ namespace Content.Server.Entry file.Flush(); } WriteFile("entity_prototypes.json", EntityJsonGenerator.PublishJson); + WriteFile("entity_parent.json", EntityParentJsonGenerator.PublishJson); WriteFile("loc.json", LocJsonGenerator.PublishJson); WriteFile("meta_license.json", MetaLicenseGenerator.PublishJson); WriteFile("prototype.json", PrototypeListGenerator.PublishJson); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index ab35f729db..ab65c04633 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -494,7 +494,6 @@ public sealed partial class ExplosionSystem dir, physics, xform, - _projectileQuery, throwForce); } } diff --git a/Content.Server/FeedbackSystem/ServerFeedbackManager.cs b/Content.Server/FeedbackSystem/ServerFeedbackManager.cs index 09edd8eefe..4ecb22bcc2 100644 --- a/Content.Server/FeedbackSystem/ServerFeedbackManager.cs +++ b/Content.Server/FeedbackSystem/ServerFeedbackManager.cs @@ -29,9 +29,6 @@ public sealed class ServerFeedbackManager : SharedFeedbackManager /// public override void SendToSession(ICommonSession session, List> popupPrototypes, bool remove = false) { - if (!NetManager.IsServer) - return; - var msg = new FeedbackPopupMessage { FeedbackPrototypes = popupPrototypes, @@ -44,9 +41,6 @@ public sealed class ServerFeedbackManager : SharedFeedbackManager /// public override void SendToAllSessions(List> popupPrototypes, bool remove = false) { - if (!NetManager.IsServer) - return; - var msg = new FeedbackPopupMessage { FeedbackPrototypes = popupPrototypes, @@ -59,9 +53,6 @@ public sealed class ServerFeedbackManager : SharedFeedbackManager /// public override void OpenForSession(ICommonSession session) { - if (!NetManager.IsServer) - return; - var msg = new OpenFeedbackPopupMessage(); NetManager.ServerSendMessage(msg, session.Channel); } @@ -69,9 +60,6 @@ public sealed class ServerFeedbackManager : SharedFeedbackManager /// public override void OpenForAllSessions() { - if (!NetManager.IsServer) - return; - var msg = new OpenFeedbackPopupMessage(); NetManager.ServerSendToAll(msg); } diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index c1d8e3b31e..cf1d6eb1a8 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -309,7 +309,7 @@ public sealed partial class GameTicker } /// - /// Gets all the gamerule entities which are currently active. + /// Gets all the gamerule entities that have been added. /// public IEnumerable GetAddedGameRules() { @@ -321,6 +321,20 @@ public sealed partial class GameTicker } } + /// + /// Gets all the gamerule entities with {T} component that have been added. + /// + public IEnumerable> GetAddedGameRules() where T : Component + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var ruleData)) + { + if (IsGameRuleAdded(uid, ruleData)) + yield return (uid, comp); + } + } + + /// /// Gets all the gamerule entities which are currently active. /// @@ -333,6 +347,18 @@ public sealed partial class GameTicker } } + /// + /// Gets all the gamerule entities with {T} component that are currently active. + /// + public IEnumerable> GetActiveGameRules() where T : Component + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out _, out _)) + { + yield return (uid, comp); + } + } + /// /// Gets all gamerule prototypes /// diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 7b6078648a..10e186f97d 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -353,17 +353,17 @@ namespace Content.Server.GameTicking DebugTools.AssertNotNull(data); - var newMind = _mind.CreateMind(data!.UserId, character.Name); - _mind.SetUserId(newMind, data.UserId); - jobPrototype = _prototypeManager.Index(jobId); - _playTimeTrackings.PlayerRolesChanged(player); - var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, jobId, character); DebugTools.AssertNotNull(mobMaybe); mob = mobMaybe!.Value; + var newMind = _mind.CreateMind(data.UserId, Name(mob)); + _mind.SetUserId(newMind, data.UserId); + + _playTimeTrackings.PlayerRolesChanged(player); + _mind.TransferTo(newMind, mob); _role.MindAddJobRole(newMind, silent: silent, jobPrototype: jobId); diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 00169f53cd..156a6d91f9 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -3,32 +3,46 @@ using Content.Server.Communications; using Content.Server.GameTicking.Rules.Components; using Content.Server.Nuke; using Content.Server.NukeOps; +using Content.Server.Pinpointer; using Content.Server.Popups; using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Shuttles.Events; using Content.Server.Shuttles.Systems; using Content.Server.Station.Components; +using Content.Server.StationRecords.Systems; using Content.Server.Store.Systems; +using Content.Shared.Access.Systems; using Content.Shared.GameTicking.Components; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.NPC.Components; using Content.Shared.NPC.Systems; using Content.Shared.Nuke; using Content.Shared.NukeOps; +using Content.Shared.Roles; using Content.Shared.Roles.Components; +using Content.Shared.Roles.Jobs; +using Content.Shared.Station; +using Content.Shared.Station.Components; +using Content.Shared.StationRecords; using Content.Shared.Store; +using Content.Shared.Store.Components; using Content.Shared.Tag; using Content.Shared.Zombies; +using Robust.Server.Player; +using Robust.Shared.Containers; using Robust.Shared.Map; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; +using System.Data; using System.Linq; -using Content.Shared.CombatMode.Pacification; -using Content.Shared.Station.Components; -using Content.Shared.Store.Components; -using Robust.Shared.Prototypes; +using System.Text; +using Content.Shared.CombatMode.Pacification;//Corvax-DionaPacifist namespace Content.Server.GameTicking.Rules; @@ -36,9 +50,18 @@ public sealed class NukeopsRuleSystem : GameRuleSystem { [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; + [Dependency] private readonly SharedIdCardSystem _idCard = default!; + [Dependency] private readonly SharedJobSystem _jobs = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly NavMapSystem _navMap = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly RoundEndSystem _roundEndSystem = default!; + [Dependency] private readonly SharedContainerSystem _containers = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; + [Dependency] private readonly SharedStationSystem _station = default!; + [Dependency] private readonly StationRecordsSystem _records = default!; [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly TagSystem _tag = default!; @@ -106,6 +129,63 @@ public sealed class NukeopsRuleSystem : GameRuleSystem args.AddLine(text); } + // Print disk location if nuke didn't explode and is not armed + List diskWinConditions = [WinCondition.NukeDiskOnCentCom, WinCondition.NukeDiskNotOnCentCom]; + if (component.WinConditions.Any(diskWinConditions.Contains)) + { + var diskQuery = AllEntityQuery(); + while (diskQuery.MoveNext(out var diskUid, out _, out var transform)) + { + StringBuilder text = new StringBuilder(Loc.GetString("nukeops-disk-location-title")); + + List containers = new List(); + bool carriedByMob = false; + + var tempParent = diskUid; + while (_containers.TryGetContainingContainer((tempParent, null), out var container) && !carriedByMob) + { + if (HasComp(container.Owner)) + { + carriedByMob = true; + } + var containermeta = MetaData(container.Owner); + containers.Add(containermeta.EntityName); + tempParent = container.Owner; + } + + string location = FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((diskUid, transform))); + + if (carriedByMob) + { + GetDiskCarrierData(tempParent, out var name, out var job, out var username); + text.Append(Loc.GetString("nukeops-disk-carried-by", + ("name", name), + ("job", job), + ("user", username), + ("location", location))); + } + else + { + if (containers.Count > 0) + { + string hierarchy = string.Empty; + for (var i = 0; i < containers.Count; i++) + { + hierarchy = (Loc.GetString( + "storage-hierarchy-list", + ("item", containers[i]), + ("existing-text", hierarchy), + ("items-left", containers.Count - i - 1))); + } + text.Append(hierarchy); + } + text.Append(" "); + text.Append(location); + } + args.AddLine(text.ToString()); + } + } + args.AddLine(Loc.GetString("nukeops-list-start")); var antags = _antag.GetAntagIdentifiers(uid); @@ -552,6 +632,74 @@ public sealed class NukeopsRuleSystem : GameRuleSystem return null; } + + private void GetDiskCarrierData(EntityUid carrier, + out string name, + out string job, + out string username) + { + name = Name(carrier); + job = Loc.GetString("job-name-unknown"); + username = "unknown"; // magic word in Fluent selector + + Entity? mind = null; + + if (_mind.TryGetMind(carrier, out _, out var mindComp)) + { + mind = (carrier, mindComp); + } + else + { + var allMinds = EntityQueryEnumerator(); + while (allMinds.MoveNext(out _, out mindComp)) + { + if (mindComp.CharacterName != name) + continue; + + mind = (carrier, mindComp); + break; + } + } + + if (mind is not null) + { + NetUserId? userId = mind.Value.Comp.UserId; + if (userId is not null && _player.TryGetPlayerData(userId.Value, out var sessionData)) + username = sessionData.UserName; + + // Role/job is the trickiest since it can be unknown in some cases + // For example, after "make ghost role" verb + var roles = _roles.MindGetAllRoleInfo(mind.Value.Owner); + if (roles.Count > 0) + { + job = Loc.GetString(roles.First().Name); + return; + } + + if (_jobs.MindTryGetJobName(mind, out var jobName)) + { + job = jobName; + return; + } + } + + // Try station records + var xform = Transform(carrier); + var station = _station.GetStationInMap(xform.MapID); + if (station != null && _records.GetRecordByName(station.Value, name) is { } id) + { + var key = new StationRecordKey(id, station.Value); + if (_records.TryGetRecord(key, out var record)) + { + job = record.JobTitle; + return; + } + } + + // Fallback to ID + if (_idCard.TryFindIdCard(carrier, out var idCard)) + job = idCard.Comp.LocalizedJobTitle ?? job; + } } /// diff --git a/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs b/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs index ab8864caaa..7eb076d0d5 100644 --- a/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs @@ -7,17 +7,20 @@ using Content.Shared.GameTicking.Components; using Content.Shared.Gibbing.Components; using Content.Shared.Medical.SuitSensor; using Content.Shared.Mind; +using Content.Shared.Objectives.Systems; +using Content.Shared.Random.Helpers; using Robust.Shared.Random; namespace Content.Server.GameTicking.Rules; public sealed class ParadoxCloneRuleSystem : GameRuleSystem { - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly CloningSystem _cloning = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SuitSensorSystem _sensor = default!; + [Dependency] private readonly TargetSystem _target = default!; public override void Initialize() { @@ -32,7 +35,7 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem(ev.Target) && !alwaysConvertible || !_mobState.IsAlive(ev.Target) || - HasComp(ev.Target)) + HasComp(ev.Target) || + !HasComp(ev.Used)) { return; } diff --git a/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs b/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs index 046ecedad6..b520a3f642 100644 --- a/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs @@ -1,11 +1,11 @@ using Content.Server.Antag; using Content.Server.GameTicking.Rules.Components; -using Content.Server.Mind; using Content.Server.Roles; using Content.Server.Shuttles.Systems; using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; +using Content.Shared.Objectives.Systems; using Content.Shared.Roles.Components; using Content.Shared.Survivor.Components; using Content.Shared.Tag; @@ -16,13 +16,13 @@ namespace Content.Server.GameTicking.Rules; public sealed class SurvivorRuleSystem : GameRuleSystem { - [Dependency] private readonly RoleSystem _role = default!; - [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; - [Dependency] private readonly TransformSystem _xform = default!; [Dependency] private readonly EmergencyShuttleSystem _eShuttle = default!; - [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly RoleSystem _role = default!; + [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly TargetSystem _target = default!; + [Dependency] private readonly TransformSystem _xform = default!; private static readonly ProtoId InvalidForSurvivorAntagTag = "InvalidForSurvivorAntag"; @@ -38,7 +38,7 @@ public sealed class SurvivorRuleSystem : GameRuleSystem { base.Started(uid, component, gameRule, args); - var allAliveHumanMinds = _mind.GetAliveHumans(); + var allAliveHumanMinds = _target.GetAliveHumans(); foreach (var humanMind in allAliveHumanMinds) { diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 3568f17306..bc053c80ba 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -18,6 +18,7 @@ using Robust.Shared.Random; using System.Linq; using System.Text; using Content.Server.Codewords; +using Robust.Shared.Map; namespace Content.Server.GameTicking.Rules; @@ -148,32 +149,22 @@ public sealed class TraitorRuleSystem : GameRuleSystem private (Note[]?, string) RequestUplink(EntityUid traitor, FixedPoint2 startingBalance, string briefing) { var pda = _uplink.FindUplinkTarget(traitor); - Note[]? code = null; Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink add"); - var uplinked = _uplink.AddUplink(traitor, startingBalance, pda, true); + var uplinked = _uplink.AddUplink(traitor, startingBalance, out var code, pda, giveDiscounts: true, bindToPda: false); - if (pda is not null && uplinked) + if (code != null && uplinked == AddUplinkResult.Pda) { Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink is PDA"); - // Codes are only generated if the uplink is a PDA - var ev = new GenerateUplinkCodeEvent(); - RaiseLocalEvent(pda.Value, ref ev); - if (ev.Code is { } generatedCode) - { - code = generatedCode; - - // If giveUplink is false the uplink code part is omitted - briefing = string.Format("{0}\n{1}", - briefing, - Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))); - return (code, briefing); - } - - Log.Error($"MakeTraitor {ToPrettyString(traitor)} failed to generate an uplink code on {ToPrettyString(pda)}."); + // If giveUplink is false the uplink code part is omitted + briefing = string.Format("{0}\n{1}", + briefing, + Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))); + return (code, briefing); } - else if (pda is null && uplinked) + + if (uplinked == AddUplinkResult.Implant) { Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink is implant"); briefing += "\n" + Loc.GetString("traitor-role-uplink-implant-short"); @@ -183,6 +174,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem Log.Error($"MakeTraitor failed on {ToPrettyString(traitor)} - No uplink could be added"); } + return (null, briefing); } diff --git a/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs index 0b6a700d11..932774e9a1 100644 --- a/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Destructible; using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; +using Content.Shared.Objectives.Systems; using Content.Shared.Xenoborgs.Components; using Robust.Shared.Timing; @@ -14,13 +15,14 @@ namespace Content.Server.GameTicking.Rules; public sealed class XenoborgsRuleSystem : GameRuleSystem { + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!; + [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly TargetSystem _target = default!; private static readonly Color AnnouncmentColor = Color.Gold; @@ -53,7 +55,7 @@ public sealed class XenoborgsRuleSystem : GameRuleSystem base.AppendRoundEndText(uid, component, gameRule, ref args); var numXenoborgs = GetNumberXenoborgs(); - var numHumans = _mindSystem.GetAliveHumans().Count; + var numHumans = _target.GetAliveHumans().Count; if (numXenoborgs < 5) args.AddLine(Loc.GetString("xenoborgs-crewmajor")); @@ -96,7 +98,7 @@ public sealed class XenoborgsRuleSystem : GameRuleSystem private void CheckRoundEnd(XenoborgsRuleComponent xenoborgsRuleComponent) { var numXenoborgs = GetNumberXenoborgs(); - var numHumans = _mindSystem.GetAliveHumans().Count; + var numHumans = _target.GetAliveHumans().Count; xenoborgsRuleComponent.MaxNumberXenoborgs = Math.Max(xenoborgsRuleComponent.MaxNumberXenoborgs, numXenoborgs); diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index b986c3b4d5..bf0b7afec0 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -17,7 +17,6 @@ using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Systems; using Content.Shared.Database; -using Content.Shared.Examine; using Content.Shared.Eye; using Content.Shared.FixedPoint; using Content.Shared.Follower; @@ -45,7 +44,6 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Timing; namespace Content.Server.Ghost { @@ -55,7 +53,6 @@ namespace Content.Server.Ghost [Dependency] private readonly IAdminLogManager _adminLog = default!; [Dependency] private readonly SharedEyeSystem _eye = default!; [Dependency] private readonly FollowerSystem _followerSystem = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly JobSystem _jobs = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly MindSystem _minds = default!; @@ -103,8 +100,6 @@ namespace Content.Server.Ghost SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnGhostShutdown); - SubscribeLocalEvent(OnGhostExamine); - SubscribeLocalEvent(OnMindRemovedMessage); SubscribeLocalEvent(OnMindUnvisitedMessage); SubscribeLocalEvent(OnPlayerDetached); @@ -288,8 +283,10 @@ namespace Content.Server.Ghost } _eye.RefreshVisibilityMask(uid); - var time = _gameTiming.CurTime; + var time = _gameTiming.RealTime; component.TimeOfDeath = time; + + Dirty(uid, component); } private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentShutdown args) @@ -320,17 +317,6 @@ namespace Content.Server.Ghost _actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction); } - private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args) - { - var timeSinceDeath = _gameTiming.CurTime.Subtract(component.TimeOfDeath); - - var deathTimeInfo = timeSinceDeath.Minutes > 0 - ? Loc.GetString("comp-ghost-examine-time-minutes", ("minutes", timeSinceDeath.Minutes)) - : Loc.GetString("comp-ghost-examine-time-seconds", ("seconds", timeSinceDeath.Seconds)); - - args.PushMarkup(deathTimeInfo); - } - #region Ghost Deletion private void OnMindRemovedMessage(EntityUid uid, GhostComponent component, MindRemovedMessage args) diff --git a/Content.Server/Implants/ChameleonControllerSystem.cs b/Content.Server/Implants/ChameleonControllerSystem.cs index e884e181ee..3de51a4949 100644 --- a/Content.Server/Implants/ChameleonControllerSystem.cs +++ b/Content.Server/Implants/ChameleonControllerSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Clothing.Systems; +using Content.Server.Clothing.Systems; using Content.Server.Preferences.Managers; using Content.Shared.Clothing; using Content.Shared.Clothing.Components; @@ -105,7 +105,7 @@ public sealed class ChameleonControllerSystem : SharedChameleonControllerSystem private void ChameleonControllerOutfitItemSelected(Entity ent, ref InventoryRelayedEvent args) { - if (!_inventory.TryGetContainingSlot(ent.Owner, out var slot)) + if (!ent.Comp.CanBeSetByController || !_inventory.TryGetContainingSlot(ent.Owner, out var slot)) return; _chameleonClothingSystem.SetSelectedPrototype(ent, GetGearForSlot(args, slot.Name), component: ent.Comp); diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 582b9cb2ac..08fe5b54cf 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -1,44 +1,5 @@ -using Content.Server.Store.Components; -using Content.Server.Store.Systems; using Content.Shared.Implants; -using Content.Shared.Interaction; -using Content.Shared.Popups; -using Content.Shared.Store.Components; namespace Content.Server.Implants; -public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem -{ - [Dependency] private readonly StoreSystem _store = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent>(OnStoreRelay); - } - - // TODO: This shouldn't be in the SubdermalImplantSystem - private void OnStoreRelay(EntityUid uid, StoreComponent store, ImplantRelayEvent implantRelay) - { - var args = implantRelay.Event; - - if (args.Handled) - return; - - // can only insert into yourself to prevent uplink checking with renault - if (args.Target != args.User) - return; - - if (!TryComp(args.Used, out var currency)) - return; - - // same as store code, but message is only shown to yourself - if (!_store.TryAddCurrency((args.Used, currency), (uid, store))) - return; - - args.Handled = true; - var msg = Loc.GetString("store-currency-inserted-implant", ("used", args.Used)); - _popup.PopupEntity(msg, args.User, args.User); - } -} +public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem; diff --git a/Content.Server/Inventory/InventoryCommand.cs b/Content.Server/Inventory/InventoryCommand.cs index e628ee6e01..2a7bcab909 100644 --- a/Content.Server/Inventory/InventoryCommand.cs +++ b/Content.Server/Inventory/InventoryCommand.cs @@ -12,7 +12,7 @@ public sealed class InventoryCommand : ToolshedCommand { private InventorySystem? _inventorySystem; - [CommandImplementation("query")] + [CommandImplementation("contents")] public IEnumerable InventoryQuery([PipedArgument] IEnumerable entities) => entities.SelectMany(InventoryQuery); diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index c8bba7dac3..7d6d86e379 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -24,7 +24,6 @@ using Content.Server.Preferences.Managers; using Content.Server.ServerInfo; using Content.Server.ServerUpdates; using Content.Server.Voting.Managers; -using Content.Server.Worldgen.Tools; using Content.Shared.Administration.Logs; using Content.Shared.Administration.Managers; using Content.Shared.Chat; @@ -67,7 +66,6 @@ internal static class ServerContentIoC deps.Register(); deps.Register(); deps.Register(); - deps.Register(); deps.Register(); deps.Register(); deps.Register(); diff --git a/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs b/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs index 04903241e3..7435a7706e 100644 --- a/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs +++ b/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs @@ -75,7 +75,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem public bool Rescan(Entity ent, EntityUid? user = null) { // Get all required transform information to start looking for the other parts based on their offset - if (!XformQuery.TryGetComponent(ent.Owner, out var xform) || !xform.Anchored) + if (!TryComp(ent.Owner, out TransformComponent? xform) || !xform.Anchored) return false; var gridUid = xform.GridUid; @@ -210,7 +210,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem { // If anchored, perform a rescan of this machine when the component starts so we can immediately // jump to an assembled state if needed. - if (XformQuery.TryGetComponent(ent.Owner, out var xform) && xform.Anchored) + if (TryComp(ent.Owner, out TransformComponent? xform) && xform.Anchored) Rescan(ent); } @@ -240,7 +240,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem private void OnPartConstructionNodeChanged(Entity ent, ref AfterConstructionChangeEntityEvent args) { - if (!XformQuery.TryGetComponent(ent.Owner, out var constructXform)) + if (!TryComp(ent.Owner, out TransformComponent? constructXform)) return; _lookupSystem.GetEntitiesInRange(constructXform.Coordinates, MaximumRange, _entitiesInRange); @@ -283,7 +283,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem // We're anchoring some construction, we have no idea which machine this might be for // so we have to just check everyone in range and perform a rescan. - if (!XformQuery.TryGetComponent(ent.Owner, out var constructXform)) + if (!TryComp(ent.Owner, out TransformComponent? constructXform)) return; _lookupSystem.GetEntitiesInRange(constructXform.Coordinates, MaximumRange, _entitiesInRange); diff --git a/Content.Server/Mind/Filters/TargetObjectiveMindFilter.cs b/Content.Server/Mind/Filters/TargetObjectiveMindFilter.cs index 6fc031d7c1..975f4c19bd 100644 --- a/Content.Server/Mind/Filters/TargetObjectiveMindFilter.cs +++ b/Content.Server/Mind/Filters/TargetObjectiveMindFilter.cs @@ -20,7 +20,7 @@ public sealed partial class TargetObjectiveMindFilter : MindFilter [DataField] public EntityWhitelist? Blacklist; - protected override bool ShouldRemove(Entity mind, EntityUid? excluded, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? excluded, IEntityManager entMan) { // ignore this filter if there is no user to check if (!entMan.TryGetComponent(excluded, out var excludedMind)) diff --git a/Content.Server/NPC/HTN/Preconditions/ItemTogglePrecondition.cs b/Content.Server/NPC/HTN/Preconditions/ItemTogglePrecondition.cs new file mode 100644 index 0000000000..431e68a55b --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/ItemTogglePrecondition.cs @@ -0,0 +1,39 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Item.ItemToggle.Components; + +namespace Content.Server.NPC.HTN.Preconditions; + +/// +/// Checks if the item in the active hand has and whether its +/// state matches the expected value. +/// +public sealed partial class ItemTogglePrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + /// + /// The activation state to check for. + /// + [DataField] + public bool Activated = true; + + public override bool IsMet(NPCBlackboard blackboard) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return false; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var heldEntity)) + return false; + + if (!_entManager.TryGetComponent(heldEntity, out var itemToggle)) + return false; + + if (!itemToggle.OnUse) + return false; + + return itemToggle.Activated == Activated; + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/NeedToRackBoltPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/NeedToRackBoltPrecondition.cs new file mode 100644 index 0000000000..7872a66f02 --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/NeedToRackBoltPrecondition.cs @@ -0,0 +1,38 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Systems; + +namespace Content.Server.NPC.HTN.Preconditions; + +/// +/// Checks if the NPC's gun needs bolt racking - either bolt is open OR bolt is closed but chamber is empty. +/// Returns true if gun needs racking to prepare for firing. +/// +public sealed partial class NeedToRackBoltPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + public override bool IsMet(NPCBlackboard blackboard) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return false; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var heldEntity)) + return false; + + if (!_entManager.TryGetComponent(heldEntity, out var chamberMagazine)) + return false; + + if (!chamberMagazine.CanRack) + return false; + + var gunSystem = _entManager.System(); + var chamberEntity = gunSystem.GetChamberEntity(heldEntity.Value); + bool hasRoundInChamber = chamberEntity is not null; + + return chamberMagazine.BoltClosed == false || !hasRoundInChamber; + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/WieldedPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/WieldedPrecondition.cs new file mode 100644 index 0000000000..dc9a9cbb7d --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/WieldedPrecondition.cs @@ -0,0 +1,45 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Wieldable; +using Content.Shared.Wieldable.Components; + +namespace Content.Server.NPC.HTN.Preconditions; + +/// +/// Checks if the item in the active hand has and whether its +/// state matches the expected value. +/// When is false, also checks that the item can actually be wielded. +/// +public sealed partial class WieldedPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + /// + /// The wield state to check for. + /// + [DataField] + public bool Wielded = true; + + public override bool IsMet(NPCBlackboard blackboard) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return false; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var heldEntity)) + return false; + + if (!_entManager.TryGetComponent(heldEntity, out var wieldable)) + return false; + + if (Wielded) + return wieldable.Wielded; + + if (wieldable.Wielded) + return false; + + var wieldableSystem = _entManager.System(); + return wieldableSystem.CanWield(heldEntity.Value, wieldable, owner, quiet: true); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/RackBoltOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/RackBoltOperator.cs new file mode 100644 index 0000000000..6dc8e49401 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/RackBoltOperator.cs @@ -0,0 +1,33 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Systems; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; + +/// +/// Operator that racks the bolt of a gun with . +/// +public sealed partial class RackBoltOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return HTNOperatorStatus.Failed; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var gunUid)) + return HTNOperatorStatus.Failed; + + if (!_entManager.TryGetComponent(gunUid, out var chamberMagazine)) + return HTNOperatorStatus.Failed; + + var gunSystem = _entManager.System(); + gunSystem.UseChambered(gunUid.Value, chamberMagazine, owner); + + return HTNOperatorStatus.Finished; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/WieldOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/WieldOperator.cs new file mode 100644 index 0000000000..98cd2afd4d --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/WieldOperator.cs @@ -0,0 +1,53 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Wieldable; +using Content.Shared.Wieldable.Components; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; + +/// +/// Operator that wields or unwields a weapon. +/// +public sealed partial class WieldOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + /// + /// If true, tries to wield the item. If false, tries to unwield it. + /// + [DataField] + public bool Wield = true; + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return HTNOperatorStatus.Failed; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var weaponUid)) + return HTNOperatorStatus.Failed; + + if (!_entManager.TryGetComponent(weaponUid, out var wieldable)) + return HTNOperatorStatus.Failed; + + var wieldableSystem = _entManager.System(); + + if (Wield) + { + if (wieldable.Wielded) + return HTNOperatorStatus.Failed; + + return wieldableSystem.TryWield(weaponUid.Value, wieldable, owner) + ? HTNOperatorStatus.Finished + : HTNOperatorStatus.Failed; + } + + if (!wieldable.Wielded) + return HTNOperatorStatus.Failed; + + return wieldableSystem.TryUnwield(weaponUid.Value, wieldable, owner) + ? HTNOperatorStatus.Finished + : HTNOperatorStatus.Failed; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/UseItemInHandOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/UseItemInHandOperator.cs new file mode 100644 index 0000000000..3048e043b5 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/UseItemInHandOperator.cs @@ -0,0 +1,24 @@ +using Content.Shared.Hands.EntitySystems; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Interactions; + +/// +/// Uses the item in the NPC's active hand. +/// +public sealed partial class UseItemInHandOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return HTNOperatorStatus.Failed; + + var handsSystem = _entManager.System(); + var success = handsSystem.TryUseItemInHand(owner, handName: activeHand); + + return success ? HTNOperatorStatus.Finished : HTNOperatorStatus.Failed; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/DisposalInsertOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/DisposalInsertOperator.cs new file mode 100644 index 0000000000..6693c3c63c --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/DisposalInsertOperator.cs @@ -0,0 +1,55 @@ +using Content.Server.Disposal.Unit; +using Content.Shared.Disposal.Components; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific; + +public sealed partial class DisposalInsertOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + private DisposalUnitSystem _disposalSystem = default!; + + /// + /// Target entity to flush + /// + [DataField(required: true)] + public string TargetKey = string.Empty; + + /// + /// Target disposal bin entity + /// + [DataField(required: true)] + public string DisposalTargetKey = string.Empty; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _disposalSystem = sysManager.GetEntitySystem(); + } + + public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status) + { + base.TaskShutdown(blackboard, status); + blackboard.Remove(TargetKey); + blackboard.Remove(DisposalTargetKey); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(TargetKey, out var target, _entManager) || _entManager.Deleted(target)) + return HTNOperatorStatus.Failed; + + if (!blackboard.TryGetValue(DisposalTargetKey, out var disposalUnitTarget, _entManager) || _entManager.Deleted(target)) + return HTNOperatorStatus.Failed; + + if (!_entManager.TryGetComponent(disposalUnitTarget, out var disposalComp)) + return HTNOperatorStatus.Failed; + + if (!_disposalSystem.TryInsert(disposalUnitTarget, target, owner, disposalComp)) + return HTNOperatorStatus.Failed; + + return HTNOperatorStatus.Finished; + + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickEntityNearMobOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickEntityNearMobOperator.cs new file mode 100644 index 0000000000..d07a9c2917 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickEntityNearMobOperator.cs @@ -0,0 +1,131 @@ +using System.Threading; +using System.Threading.Tasks; +using Content.Server.NPC.Pathfinding; +using Content.Shared.Interaction; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Whitelist; +using Robust.Server.Containers; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific; + +/// +/// Queries for nearby entities matching a whitelist/blacklist, and then searches for a mob near to that entity. +/// +public sealed partial class PickEntityNearMobOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + private EntityLookupSystem _lookup = default!; + private PathfindingSystem _pathfinding = default!; + private ContainerSystem _container = default!; + private EntityWhitelistSystem _entityWhitelist = default!; + + /// + /// Range to search for entities + /// + [DataField(required: true)] + public string RangeKey = default!; + + /// + /// Target mob that was found near NearbyEntityTargetKey + /// + [DataField(required: true)] + public string TargetKey = string.Empty; + + /// + /// Target entity that was found + /// + [DataField(required: true)] + public string NearbyEntityTargetKey = string.Empty; + + /// + /// Target entitycoordinates to move to. + /// + [DataField(required: true)] + public string TargetMoveKey = string.Empty; + + /// + /// Whitelist for what entities will get picked + /// + [DataField] + public EntityWhitelist? Whitelist; + + /// + /// Blacklist for what entities will NOT get picked + /// + [DataField] + public EntityWhitelist? Blacklist; + + /// + /// Range to search for mobs near the target entity + /// + [DataField(required: true)] + public string MobRangeKey = default!; + + /// + /// MobState to check for + /// + [DataField] + public MobState? MobState; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _lookup = sysManager.GetEntitySystem(); + _pathfinding = sysManager.GetEntitySystem(); + _container = sysManager.GetEntitySystem(); + _entityWhitelist = sysManager.GetEntitySystem(); + } + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(RangeKey, out var range, _entManager)) + return (false, null); + + if (!blackboard.TryGetValue(RangeKey, out var mobRange, _entManager)) + return (false, null); + + var mobState = _entManager.GetEntityQuery(); + + foreach (var entity in _lookup.GetEntitiesInRange(owner, range)) + { + if (!_entityWhitelist.CheckBoth(entity, Blacklist, Whitelist)) + continue; + + //checking if there is anyone NEAR the entity we found + foreach (var mob in _lookup.GetEntitiesInRange(entity, mobRange)) + { + if (mob == owner) + continue; + + if (_container.IsEntityInContainer(mob)) + continue; + + if (mobState.TryGetComponent(mob, out var state)) + { + if (MobState != null && state.CurrentState != MobState) + continue; + + var pathRange = SharedInteractionSystem.InteractionRange; + var path = await _pathfinding.GetPath(owner, mob, pathRange, cancelToken); + + if (path.Result == PathResult.NoPath) + return (false, null); + + return (true, new Dictionary() + { + {TargetKey, mob}, + {NearbyEntityTargetKey, entity}, + {TargetMoveKey, _entManager.GetComponent(mob).Coordinates}, + {NPCBlackboard.PathfindKey, path}, + }); + } + } + } + + return (false, null); + } +} diff --git a/Content.Server/NPC/Queries/Considerations/TargetIsVisibleCon.cs b/Content.Server/NPC/Queries/Considerations/TargetIsVisibleCon.cs new file mode 100644 index 0000000000..387c9e9809 --- /dev/null +++ b/Content.Server/NPC/Queries/Considerations/TargetIsVisibleCon.cs @@ -0,0 +1,3 @@ +namespace Content.Server.NPC.Queries.Considerations; + +public sealed partial class TargetIsVisibleCon : UtilityConsideration; diff --git a/Content.Server/NPC/Queries/Queries/PuddlesQuery.cs b/Content.Server/NPC/Queries/Queries/PuddlesQuery.cs deleted file mode 100644 index 0c88cc2de6..0000000000 --- a/Content.Server/NPC/Queries/Queries/PuddlesQuery.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Server.NPC.Queries.Queries; - -public sealed partial class PuddlesQuery : UtilityQuery -{ - -} diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 89b49cbbe9..b30f813a66 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -30,7 +30,10 @@ using Content.Shared.Atmos.Components; using System.Linq; using Content.Shared.Damage.Components; using Content.Shared.Damage.Systems; +using Content.Shared.Mobs.Components; using Content.Shared.Temperature.Components; +using Content.Shared.Stealth; +using Content.Shared.Stealth.Components; namespace Content.Server.NPC.Systems; @@ -56,6 +59,7 @@ public sealed class NPCUtilitySystem : EntitySystem [Dependency] private readonly MobThresholdSystem _thresholdSystem = default!; [Dependency] private readonly TurretTargetSettingsSystem _turretTargetSettings = default!; [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedStealthSystem _stealth = default!; private EntityQuery _puddleQuery; private EntityQuery _xformQuery; @@ -285,6 +289,15 @@ public sealed class NPCUtilitySystem : EntitySystem return Math.Clamp(distance / radius, 0f, 1f); } + case TargetIsVisibleCon: + { + if (!TryComp(targetUid, out StealthComponent? stealth)) + return 1f; // If there is no StealthComponent, we see it. + + // Checking the visibility level + var visibility = _stealth.GetVisibility(targetUid, stealth); + return visibility >= 0.5f ? 1f : 0f; // Visibility threshold 0.5 + } case TargetAmmoCon: { if (!HasComp(targetUid)) @@ -304,12 +317,13 @@ public sealed class NPCUtilitySystem : EntitySystem } case TargetHealthCon con: { - if (!TryComp(targetUid, out DamageableComponent? damage)) + if (!TryComp(targetUid, out DamageableComponent? damage) || !TryComp(targetUid, out MobThresholdsComponent? threshold)) return 0f; + var totalDamage = _damageable.GetTotalDamage((targetUid, damage)); - if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, totalDamage, out var percentage)) + if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, totalDamage, out var percentage, threshold)) return Math.Clamp((float)(1 - percentage), 0f, 1f); - if (_thresholdSystem.TryGetIncapPercentage(targetUid, totalDamage, out var incapPercentage)) + if (_thresholdSystem.TryGetIncapPercentage(targetUid, totalDamage, out var incapPercentage, threshold)) return Math.Clamp((float)(1 - incapPercentage), 0f, 1f); return 0f; } diff --git a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs index 33b619732d..202d03c6ea 100644 --- a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs @@ -1,103 +1,5 @@ -using Content.Server.Fluids.EntitySystems; -using Content.Server.Nutrition.Components; -using Content.Server.Popups; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.IdentityManagement; -using Content.Shared.Nutrition; -using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.Rejuvenate; -using Content.Shared.Throwing; -using Content.Shared.Trigger.Components; -using Content.Shared.Trigger.Systems; -using Content.Shared.Chemistry.EntitySystems; -using JetBrains.Annotations; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -namespace Content.Server.Nutrition.EntitySystems -{ - [UsedImplicitly] - public sealed class CreamPieSystem : SharedCreamPieSystem - { - [Dependency] private readonly IngestionSystem _ingestion = default!; - [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly PuddleSystem _puddle = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutions = default!; - [Dependency] private readonly TriggerSystem _trigger = default!; +namespace Content.Server.Nutrition.EntitySystems; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSlice); - - SubscribeLocalEvent(OnRejuvenate); - } - - protected override void SplattedCreamPie(Entity entity) - { - // The entity is deleted, so play the sound at its position rather than parenting - var coordinates = Transform(entity).Coordinates; - _audio.PlayPvs(_audio.ResolveSound(entity.Comp1.Sound), coordinates, AudioParams.Default.WithVariation(0.125f)); - - if (Resolve(entity, ref entity.Comp2, false)) - { - if (_solutions.TryGetSolution(entity.Owner, entity.Comp2.Solution, out _, out var solution)) - _puddle.TrySpillAt(entity.Owner, solution, out _, false); - - _ingestion.SpawnTrash((entity, entity.Comp2)); - } - - ActivatePayload(entity); - - QueueDel(entity); - } - - // TODO - // A regression occured here. Previously creampies would activate their hidden payload if you tried to eat them. - // However, the refactor to IngestionSystem caused the event to not be reached, - // because eating is blocked if an item is inside the food. - - private void OnSlice(Entity entity, ref SliceFoodEvent args) - { - ActivatePayload(entity); - } - - private void ActivatePayload(EntityUid uid) - { - if (_itemSlots.TryGetSlot(uid, CreamPieComponent.PayloadSlotName, out var itemSlot)) - { - if (_itemSlots.TryEject(uid, itemSlot, user: null, out var item)) - { - if (TryComp(item.Value, out var timerTrigger)) - { - _trigger.ActivateTimerTrigger((item.Value, timerTrigger)); - } - } - } - } - - protected override void CreamedEntity(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) - { - _popup.PopupEntity(Loc.GetString("cream-pied-component-on-hit-by-message", - ("thrown", Identity.Entity(args.Thrown, EntityManager))), - uid, args.Target); - - var otherPlayers = Filter.PvsExcept(uid); - - _popup.PopupEntity(Loc.GetString("cream-pied-component-on-hit-by-message-others", - ("owner", Identity.Entity(uid, EntityManager)), - ("thrown", Identity.Entity(args.Thrown, EntityManager))), - uid, otherPlayers, false); - } - - private void OnRejuvenate(Entity entity, ref RejuvenateEvent args) - { - SetCreamPied(entity, entity.Comp, false); - } - } -} +public sealed class CreamPieSystem : SharedCreamPieSystem; diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs index ae35fa4825..e9ecba3454 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs @@ -106,7 +106,7 @@ namespace Content.Server.Nutrition.EntitySystems { if (args.Slot == "mask") { - _forensics.TransferDna(entity.Owner, args.Equipee, false); + _forensics.TransferDna(entity.Owner, args.EquipTarget, false); } } diff --git a/Content.Server/Objectives/Components/CounterConditionComponent.cs b/Content.Server/Objectives/Components/CounterConditionComponent.cs new file mode 100644 index 0000000000..c89c9aa9e9 --- /dev/null +++ b/Content.Server/Objectives/Components/CounterConditionComponent.cs @@ -0,0 +1,12 @@ +namespace Content.Server.Objectives.Components; + +/// +/// This is used as a generic counter for objectives. +/// Requires to function. +/// +[RegisterComponent] +public sealed partial class CounterConditionComponent : Component +{ + [DataField] + public int Count; +} diff --git a/Content.Server/Objectives/Components/HijackTradeStationConditionComponent.cs b/Content.Server/Objectives/Components/HijackTradeStationConditionComponent.cs new file mode 100644 index 0000000000..416c422837 --- /dev/null +++ b/Content.Server/Objectives/Components/HijackTradeStationConditionComponent.cs @@ -0,0 +1,11 @@ +using Content.Server.Objectives.Systems; + +namespace Content.Server.Objectives.Components; + +/// +/// Objective condition that requires the player to hijack the trade station. +/// +[RegisterComponent, Access(typeof(HijackTradeStationConditionSystem))] +public sealed partial class HijackTradeStationConditionComponent : Component +{ +} diff --git a/Content.Server/Objectives/Components/MailFraudConditionComponent.cs b/Content.Server/Objectives/Components/MailFraudConditionComponent.cs new file mode 100644 index 0000000000..850b7f2f13 --- /dev/null +++ b/Content.Server/Objectives/Components/MailFraudConditionComponent.cs @@ -0,0 +1,12 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Objective condition that requires the player to cut into letters and packages addressed to others. +/// Requires to function. +/// Requires to function. +/// +[RegisterComponent] +public sealed partial class MailFraudConditionComponent : Component +{ + +} diff --git a/Content.Server/Objectives/Components/PickRandomPersonComponent.cs b/Content.Server/Objectives/Components/PickRandomPersonComponent.cs index 2c864a80d4..16e539f320 100644 --- a/Content.Server/Objectives/Components/PickRandomPersonComponent.cs +++ b/Content.Server/Objectives/Components/PickRandomPersonComponent.cs @@ -16,7 +16,7 @@ public sealed partial class PickRandomPersonComponent : Component /// A pool to pick potential targets from. /// [DataField] - public IMindPool Pool = new AliveHumansPool(); + public MindPool Pool = new AliveHumansPool(); /// /// Filters to apply to . diff --git a/Content.Server/Objectives/Components/SupercriticalAnomaliesConditionComponent.cs b/Content.Server/Objectives/Components/SupercriticalAnomaliesConditionComponent.cs new file mode 100644 index 0000000000..038150d90c --- /dev/null +++ b/Content.Server/Objectives/Components/SupercriticalAnomaliesConditionComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Objective condition that requires a certain number of anomalies (defined by ) to go supercritical while the objective is in play. +/// +[RegisterComponent] +public sealed partial class SupercriticalAnomaliesConditionComponent : Component +{ + /// + /// The number of anomalies that have gone supercritical since this objective was added. + /// + [DataField] + public int SupercriticalAnomalies = 0; +} diff --git a/Content.Server/Objectives/Systems/ConditionCounterSystem.cs b/Content.Server/Objectives/Systems/ConditionCounterSystem.cs new file mode 100644 index 0000000000..1cf9318b53 --- /dev/null +++ b/Content.Server/Objectives/Systems/ConditionCounterSystem.cs @@ -0,0 +1,48 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Objectives.Components; +using JetBrains.Annotations; + +namespace Content.Server.Objectives.Systems; + +/// +/// This system handles returning the progress for CounterConditionComponents, +/// which simple increment for traitor NumberObjectives, e.g. cut into 12 envelopes. +/// +public sealed class CounterConditionSystem : EntitySystem +{ + [Dependency] private readonly NumberObjectiveSystem _number = default!; + [Dependency] private EntityQuery _compQuery = default!; + + /// + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnCounterGetProgress); + } + + private void OnCounterGetProgress(Entity ent, ref ObjectiveGetProgressEvent args) + { + args.Progress = GetProgress(ent, _number.GetTarget(ent.Owner)); + } + + private float GetProgress(Entity ent, int target) + { + // prevent divide-by-zero + if (target == 0) + return 1f; + + if (ent.Comp.Count >= target) + return 1f; + + return (float)ent.Comp.Count / target; + } + + [PublicAPI] + public void IncreaseCount(Entity objective) + { + if (_compQuery.Resolve(objective, ref objective.Comp)) + { + objective.Comp.Count++; + } + } +} diff --git a/Content.Server/Objectives/Systems/HijackTradeStationConditionSystem.cs b/Content.Server/Objectives/Systems/HijackTradeStationConditionSystem.cs new file mode 100644 index 0000000000..e5f88a28dd --- /dev/null +++ b/Content.Server/Objectives/Systems/HijackTradeStationConditionSystem.cs @@ -0,0 +1,35 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Cargo.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using NetCord; + +namespace Content.Server.Objectives.Systems; + +/// +/// Handles the Hijack Trade Station objective. +/// +public sealed class HijackTradeStationConditionSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetProgress); + } + + private void OnGetProgress(Entity ent, ref ObjectiveGetProgressEvent args) + { + var enumerator = EntityQueryEnumerator(); + args.Progress = 0f; + // If there's any hacked trade station, succeed. + while (enumerator.MoveNext(out var comp)) + { + if (!comp.Hacked) + continue; + + args.Progress = 1f; + return; + } + } +} diff --git a/Content.Server/Objectives/Systems/MailFraudObjectiveSystem.cs b/Content.Server/Objectives/Systems/MailFraudObjectiveSystem.cs new file mode 100644 index 0000000000..a82005fa57 --- /dev/null +++ b/Content.Server/Objectives/Systems/MailFraudObjectiveSystem.cs @@ -0,0 +1,38 @@ +using Content.Server.Mind; +using Content.Server.Objectives.Components; +using Content.Shared.Delivery; +using Content.Shared.FingerprintReader; + +namespace Content.Server.Objectives.Systems; + +public sealed partial class MailFraudObjectiveSystem : EntitySystem +{ + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly FingerprintReaderSystem _fingerprintReader = default!; + [Dependency] private readonly CounterConditionSystem _counterCondition = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnDeliveryOpened); + } + + private void OnDeliveryOpened(Entity ent, ref DeliveryOpenedEvent args) + { + if (!ent.Comp.WasPenalized) + return; //not fraud + + if (_fingerprintReader.IsAllowed(ent.Owner, args.User, out var _, showPopup: false, checkGloves: false)) + return; //cutting open your own letter + + if (!_mind.TryGetMind(args.User, out _, out var mind)) + return; + + foreach (var obj in mind.Objectives) + { + if (HasComp(obj)) + { + _counterCondition.IncreaseCount(obj); + } + } + } +} diff --git a/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs index b3075b2274..5b48b25ad4 100644 --- a/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs +++ b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs @@ -5,6 +5,7 @@ using Content.Server.GameTicking.Rules; using Content.Server.Revolutionary.Components; using Robust.Shared.Random; using System.Linq; +using Content.Shared.Objectives.Systems; namespace Content.Server.Objectives.Systems; @@ -14,8 +15,8 @@ namespace Content.Server.Objectives.Systems; /// public sealed class PickObjectiveTargetSystem : EntitySystem { - [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly TargetObjectiveSystem _objective = default!; + [Dependency] private readonly TargetSystem _target = default!; public override void Initialize() { @@ -51,7 +52,7 @@ public sealed class PickObjectiveTargetSystem : EntitySystem return; } - _target.SetTarget(ent.Owner, targetComp.Target.Value); + _objective.SetTarget(ent.Owner, targetComp.Target.Value); } private void OnRandomPersonAssigned(Entity ent, ref ObjectiveAssignedEvent args) @@ -68,12 +69,12 @@ public sealed class PickObjectiveTargetSystem : EntitySystem return; // couldn't find a target :( - if (_mind.PickFromPool(ent.Comp.Pool, ent.Comp.Filters, args.MindId) is not {} picked) + if (_target.PickFromPool(ent.Comp.Pool, ent.Comp.Filters, args.MindId) is not {} picked) { args.Cancelled = true; return; } - _target.SetTarget(ent, picked, target); + _objective.SetTarget(ent, picked, target); } } diff --git a/Content.Server/Objectives/Systems/SupercriticalAnomaliesConditionSystem.cs b/Content.Server/Objectives/Systems/SupercriticalAnomaliesConditionSystem.cs new file mode 100644 index 0000000000..93e649def2 --- /dev/null +++ b/Content.Server/Objectives/Systems/SupercriticalAnomaliesConditionSystem.cs @@ -0,0 +1,41 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Anomaly.Components; +using Content.Shared.Objectives.Components; + +namespace Content.Server.Objectives.Systems; + +public sealed partial class SupercriticalAnomaliesConditionSystem : EntitySystem +{ + [Dependency] private readonly NumberObjectiveSystem _numberObjective = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAnomalySupercrit); + SubscribeLocalEvent(OnGetProgress); + } + + private void OnAnomalySupercrit(ref AnomalyShutdownEvent args) + { + if (!args.Supercritical) + return; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var comp)) + { + comp.SupercriticalAnomalies += 1; + } + } + + private void OnGetProgress(Entity ent, ref ObjectiveGetProgressEvent args) + { + var target = _numberObjective.GetTarget(ent); + if (target == 0) + { + args.Progress = 0f; + return; + } + args.Progress = MathF.Min((float)ent.Comp.SupercriticalAnomalies / target, 1f); + } +} diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index 033d28301f..a33539e30c 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Access.Systems; using Content.Server.AlertLevel; using Content.Server.CartridgeLoader; @@ -17,6 +18,7 @@ using Content.Shared.Light; using Content.Shared.Light.EntitySystems; using Content.Shared.PDA; using Content.Shared.PDA.Ringer; +using Content.Shared.Store.Components; using Content.Shared.VoiceMask; using Robust.Server.Containers; using Robust.Server.GameObjects; @@ -191,7 +193,7 @@ namespace Content.Server.PDA var address = GetDeviceNetAddress(uid); var hasInstrument = HasComp(uid); - var showUplink = HasComp(uid) && IsUnlocked(uid); + var showUplink = TryGetUnlockedStore(uid, out _); UpdateStationName(uid, pda); UpdateAlertLevel(uid, pda); @@ -278,8 +280,19 @@ namespace Content.Server.PDA return; // check if its locked again to prevent malicious clients opening locked uplinks - if (HasComp(uid) && IsUnlocked(uid)) - _store.ToggleUi(msg.Actor, uid); + if (TryGetUnlockedStore(uid, out var store)) + { + if (store != uid) + { + if (TryComp(uid, out var remoteStore)) + remoteStore.Store = store; + _store.ToggleUi(msg.Actor, store.Value, remoteAccess: uid); + } + else + { + _store.ToggleUi(msg.Actor, store.Value); + } + } } private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaLockUplinkMessage msg) @@ -289,14 +302,24 @@ namespace Content.Server.PDA if (TryComp(uid, out var uplink)) { + if (TryComp(uid, out var remoteStore)) + remoteStore.Store = null; _ringer.LockUplink((uid, uplink)); UpdatePdaUi(uid, pda); } } - private bool IsUnlocked(EntityUid uid) + /// + /// Returns the currently unlocked store, if there is one. + /// + private bool TryGetUnlockedStore(EntityUid uid, [NotNullWhen(true)] out EntityUid? store) { - return !TryComp(uid, out var uplink) || uplink.Unlocked; + store = null; + if (!TryComp(uid, out var uplink) || !uplink.Unlocked) + return false; + + store = _store.GetStore(uid); + return store != null; } private void UpdateStationName(EntityUid uid, PdaComponent pda) diff --git a/Content.Server/PDA/Ringer/RingerAccessUplinkComponent.cs b/Content.Server/PDA/Ringer/RingerAccessUplinkComponent.cs new file mode 100644 index 0000000000..f7d124adef --- /dev/null +++ b/Content.Server/PDA/Ringer/RingerAccessUplinkComponent.cs @@ -0,0 +1,24 @@ +using Content.Shared.PDA; + +namespace Content.Server.PDA.Ringer; + +/// +/// Opens the store UI when a PDA's ringtone is set to the secret code. +/// Traitors are told the code when greeted. +/// +[RegisterComponent, Access(typeof(RingerSystem))] +public sealed partial class RingerAccessUplinkComponent : Component +{ + /// + /// Notes to set ringtone to in order to lock or unlock the uplink. + /// Set via GenerateUplinkCodeEvent. + /// + [DataField] + public Note[]? Code; + + /// + /// If set, the uplink store can only be opened with the given entity. + /// + [DataField] + public EntityUid? BoundEntity; +} diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs index b47ca0fde3..a86b44d389 100644 --- a/Content.Server/PDA/Ringer/RingerSystem.cs +++ b/Content.Server/PDA/Ringer/RingerSystem.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Server.Store.Systems; +using Content.Shared.GameTicking; using Content.Shared.PDA; using Content.Shared.PDA.Ringer; -using Content.Shared.Store.Components; +using Content.Shared.Store; using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Server.PDA.Ringer; @@ -14,15 +16,35 @@ public sealed class RingerSystem : SharedRingerSystem { [Dependency] private readonly IRobustRandom _random = default!; + public static Note[] AllowedNotes = + { + Note.C, + Note.D, + Note.E, + Note.F, + Note.G, + Note.A, + Note.B + }; + + /// + /// Stores the serialized version of any ringtone that can be excluded from new ringtone generations. + /// + [ViewVariables] + public readonly HashSet ReservedSerializedRingtones = new(); + /// public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnCurrencyInsert); - SubscribeLocalEvent(OnGenerateUplinkCode); + SubscribeLocalEvent(OnGenerateUplinkCode); + + SubscribeLocalEvent(CleanupReserved); + + InitialSetup(); } /// @@ -30,7 +52,11 @@ public sealed class RingerSystem : SharedRingerSystem /// private void OnMapInit(Entity ent, ref MapInitEvent args) { - UpdateRingerRingtone(ent, GenerateRingtone()); + var ringtone = GenerateRingtone(); + + ringtone ??= new Note[RingtoneLength] { Note.A, Note.A, Note.A, Note.A, Note.A, Note.A }; // Fallback + + UpdateRingerRingtone(ent, ringtone); } /// @@ -53,71 +79,211 @@ public sealed class RingerSystem : SharedRingerSystem /// /// Handles the for generating an uplink code. /// - private void OnGenerateUplinkCode(Entity ent, ref GenerateUplinkCodeEvent ev) + private void OnGenerateUplinkCode(Entity ent, ref GenerateUplinkCodeEvent ev) { - var code = GenerateRingtone(); + var code = GenerateRingtone(true, true); // Set the code on the component ent.Comp.Code = code; - // Return the code via the event ev.Code = code; } - /// - public override bool TryToggleUplink(EntityUid uid, Note[] ringtone, EntityUid? user = null) + private void InitialSetup() { - if (!TryComp(uid, out var uplink)) - return false; + ReservedSerializedRingtones.Clear(); + } - if (!HasComp(uid)) - return false; - - // Wasn't generated yet - if (uplink.Code is null) + /// + public override bool TryToggleUplink(Entity entity, Note[] ringtone, EntityUid? user = null) + { + if (!Resolve(entity, ref entity.Comp)) return false; // On the server, we always check if the code matches - if (!uplink.Code.SequenceEqual(ringtone)) + if (!TryMatchRingtoneToStore(ringtone, out var store, entity)) return false; - return ToggleUplinkInternal((uid, uplink)); + // If the store is not this entity, we make sure to properly set the remote store. + if (store != entity.Owner) + Store.SetRemoteStore(entity.Owner, store); + + return ToggleUplinkInternal((entity, entity.Comp)); } /// - /// Generates a random ringtone using the C pentatonic scale. + /// Generates a random ringtone using the C major scale. /// + /// Exclude any ringtone registered to ReservedSerializedRingtones. + /// Add the generated ringtone to ReservedSerializedRingtones. Requires ExcludeReserved to be true. /// An array of Notes representing the ringtone. /// The logic for this is on the Server so that we don't get a different result on the Client every time. - private Note[] GenerateRingtone() + private Note[]? GenerateRingtone(bool excludeReserved = false, bool reserveRingtone = false) { - // Default to using C pentatonic so it at least sounds not terrible. - return GenerateRingtone(new[] - { - Note.C, - Note.D, - Note.E, - Note.G, - Note.A - }); + // Default to using C major so it at least sounds not terrible. + return GenerateRingtone(AllowedNotes, excludeReserved, reserveRingtone); } /// /// Generates a random ringtone using the specified notes. /// /// The notes to choose from when generating the ringtone. + /// Exclude any ringtone registered to ReservedSerializedRingtones. + /// Add the generated ringtone to ReservedSerializedRingtones. Requires ExcludeReserved to be true. /// An array of Notes representing the ringtone. /// The logic for this is on the Server so that we don't get a different result on the Client every time. - private Note[] GenerateRingtone(Note[] notes) + private Note[]? GenerateRingtone(Note[] notes, bool excludeReserved = false, bool reserveRingtone = false) { - var ringtone = new Note[RingtoneLength]; + var excludedRingtones = excludeReserved ? ReservedSerializedRingtones.ToArray() : null; + + var maxPow = Math.Pow(notes.Length, RingtoneLength); + if (maxPow > int.MaxValue) + { + return null; + } + + var generatedRingtone = NextIntInRangeButExclude(0, Convert.ToInt32(maxPow) - 1, excludedRingtones); + + if (!TryDeserializeRingtone(notes, generatedRingtone, out var ringtone)) + return null; + + if (excludeReserved && reserveRingtone) + ReservedSerializedRingtones.Add(generatedRingtone); + + return ringtone; + } + + /// + /// Serialize a ringtone, representing it as an Int32. + /// + /// The array of notes used to generate the ringtone. + /// The ringtone which needs to be serialized. + /// The ringtone in a serialized format. + /// Whether the ringtone could be serialized or not. + private bool TrySerializeRingtone(Note[] allowedNotes, Note[] ringtone, [NotNullWhen(true)] out int? serializedRingtone) + { + var noteLength = allowedNotes.Length; + + // The serialization stores as an Int32, and therefore using Pow risks overshooting the max value, so we check for if that's a risk. + // If using 12 possible notes, you can have a ringtone sequence of 7 notes safely without overshooting. + var maxPow = Math.Pow(noteLength, ringtone.Length); + if (maxPow > int.MaxValue) + { + serializedRingtone = null; + return false; + } + + var serializationValue = 0; + + for (var i = 0; i < ringtone.Length; i++) + { + var pow = Math.Pow(noteLength, i); + var index = Array.IndexOf(allowedNotes, ringtone[i]); + if (index == -1) + { + serializedRingtone = null; + return false; + } + + serializationValue += Convert.ToInt32(pow) * index; + } + + serializedRingtone = serializationValue; + return true; + } + + /// + /// Deserialize a serialized ringtone into a Note array. + /// + /// The array of notes used to generate the ringtone. + /// The ringtone in a serialized format. + /// The ringtone resulting from the deserialization. + /// Whether the ringtone could be deserialized or not. + private bool TryDeserializeRingtone(Note[] allowedNotes, int serializedRingtone, [NotNullWhen(true)] out Note[]? ringtone) + { + var noteLength = allowedNotes.Length; + ringtone = new Note[RingtoneLength]; + + // The serialization stores as an Int32, and therefore using Pow risks overshooting the max value, so we check for if that's a risk. + // If using 12 possible notes, you can have a ringtone sequence of 7 notes safely without overshooting. + var maxPow = Math.Pow(noteLength, RingtoneLength); + if (maxPow > int.MaxValue) + { + ringtone = null; + return false; + } for (var i = 0; i < RingtoneLength; i++) { - ringtone[i] = _random.Pick(notes); + var pow = Math.Pow(noteLength, RingtoneLength - 1 - i); + var powInt = Convert.ToInt32(pow); + var val = serializedRingtone / powInt; + if (!AllowedNotes.TryGetValue(val, out var note)) + { + ringtone = null; + return false; + } + + ringtone[RingtoneLength - 1 - i] = note; + serializedRingtone -= val * powInt; } - return ringtone; + return true; + } + + /// + /// Try to get the store entity that has the matching ringer access. + /// + /// Notes from the ringer. + /// The store entity, if there is one. + /// The entity providing the code. + public bool TryMatchRingtoneToStore(Note[] notes, [NotNullWhen(true)] out EntityUid? store, EntityUid? ringer = null) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.Code != null && notes.SequenceEqual(comp.Code)) + { + if (comp.BoundEntity != null && comp.BoundEntity != ringer) + break; + + store = uid; + return true; + } + } + + store = null; + return false; + } + + private void CleanupReserved(RoundRestartCleanupEvent ev) + { + ReservedSerializedRingtones.Clear(); + } + + private int NextIntInRangeButExclude(int start, int end, int[]? excludes) + { + excludes ??= new int[0]; + Array.Sort(excludes); + var rangeLength = end - start - excludes.Length; + var randomInt = _random.Next(rangeLength) + start; + + for (var i = 0; i < excludes.Length; i++) + { + if (excludes[i] > randomInt) + { + return randomInt; + } + + randomInt++; + } + + return randomInt; + } + + public void SetBoundUplinkEntity(Entity entity, EntityUid? targetEntity) + { + entity.Comp.BoundEntity = targetEntity; } } diff --git a/Content.Server/Pinpointer/StationMapSystem.cs b/Content.Server/Pinpointer/StationMapSystem.cs index bf2d2e2817..1e5fa74b7c 100644 --- a/Content.Server/Pinpointer/StationMapSystem.cs +++ b/Content.Server/Pinpointer/StationMapSystem.cs @@ -22,9 +22,11 @@ public sealed class StationMapSystem : EntitySystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnUserParentChanged); - SubscribeLocalEvent(OnNukeopsStationSelected); + SubscribeLocalEvent(OnNukeOpsStationMap); + SubscribeLocalEvent(OnNukeopsStationSelected); - Subs.BuiEvents(StationMapUiKey.Key, subs => + Subs.BuiEvents(StationMapUiKey.Key, + subs => { subs.Event(OnStationMapOpened); subs.Event(OnStationMapClosed); @@ -36,18 +38,13 @@ public sealed class StationMapSystem : EntitySystem if (!ent.Comp.InitializeWithStation) return; - // If we ever find a need to make more exceptions like this, just turn this into an event. - if (HasComp(ent)) + var ev = new ChooseStationMapEvent(); + RaiseLocalEvent(ent, ref ev); + if (ev.Handled) { - foreach (var rule in _gameTicker.GetActiveGameRules()) - { - if (TryComp(rule, out var nukeopsRule) && nukeopsRule.TargetStation != null) - { - ent.Comp.TargetGrid = _station.GetLargestGrid((nukeopsRule.TargetStation.Value, null)); - Dirty(ent); - return; - } - } + ent.Comp.TargetGrid = ev.TargetGrid; + Dirty(ent); + return; } var station = _station.GetStationInMap(_xform.GetMapId(ent.Owner)); @@ -80,18 +77,50 @@ public sealed class StationMapSystem : EntitySystem comp.Map = uid; } - private void OnNukeopsStationSelected(Entity ent, ref NukeopsTargetStationSelectedEvent args) + private void OnNukeOpsStationMap(Entity entity, ref ChooseStationMapEvent args) { - if (args.TargetStation == null) + // If we have this component, we don't want a fallback map! + args.Handle(); + + foreach (var rule in _gameTicker.GetActiveGameRules()) + { + if (rule.Comp.TargetStation == null) + continue; + + args.TargetGrid = _station.GetLargestGrid((rule.Comp.TargetStation.Value, null)); + return; + } + } + + private void OnNukeopsStationSelected(ref NukeopsTargetStationSelectedEvent args) + { + if (args.TargetStation == null || !TryComp(args.RuleEntity, out var ruleGrids)) return; - if (!TryComp(ent, out var stationMap) || !TryComp(args.RuleEntity, out var ruleGrids)) - return; + var mapquery = EntityQueryEnumerator(); + while (mapquery.MoveNext(out var uid, out _, out var map)) + { + if (Transform(uid).MapID != ruleGrids.Map) + continue; - if (Transform(ent).MapID != ruleGrids.Map) - return; - - stationMap.TargetGrid = _station.GetLargestGrid((args.TargetStation.Value, null)); - Dirty(ent); + map.TargetGrid = _station.GetLargestGrid((args.TargetStation.Value, null)); + Dirty(uid, map); + } + } +} + +/// +/// Selects an alternative target for our station map! +/// If handled, this will not get the map of the current station. +/// +[ByRefEvent] +public record struct ChooseStationMapEvent +{ + public EntityUid? TargetGrid; + public bool Handled { get; private set; } + + public void Handle() + { + Handled = true; } } diff --git a/Content.Server/Procedural/DungeonSystem.Rooms.cs b/Content.Server/Procedural/DungeonSystem.Rooms.cs index e5b0981b3d..0854fbb172 100644 --- a/Content.Server/Procedural/DungeonSystem.Rooms.cs +++ b/Content.Server/Procedural/DungeonSystem.Rooms.cs @@ -7,6 +7,7 @@ using Content.Shared.Whitelist; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Utility; +using Robust.Shared.Random; namespace Content.Server.Procedural; @@ -19,7 +20,7 @@ public sealed partial class DungeonSystem /// /// Gets a random dungeon room matching the specified area, whitelist and size. /// - public DungeonRoomPrototype? GetRoomPrototype(Vector2i size, Random random, EntityWhitelist? whitelist = null) + public DungeonRoomPrototype? GetRoomPrototype(Vector2i size, IRobustRandom random, EntityWhitelist? whitelist = null) { return GetRoomPrototype(random, whitelist, minSize: size, maxSize: size); } @@ -27,7 +28,7 @@ public sealed partial class DungeonSystem /// /// Gets a random dungeon room matching the specified area and whitelist and size range /// - public DungeonRoomPrototype? GetRoomPrototype(Random random, + public DungeonRoomPrototype? GetRoomPrototype(IRobustRandom random, EntityWhitelist? whitelist = null, Vector2i? minSize = null, Vector2i? maxSize = null) @@ -77,7 +78,7 @@ public sealed partial class DungeonSystem MapGridComponent grid, Vector2i origin, DungeonRoomPrototype room, - Random random, + IRobustRandom random, HashSet? reservedTiles, bool clearExisting = false, bool rotation = false) @@ -96,7 +97,7 @@ public sealed partial class DungeonSystem SpawnRoom(gridUid, grid, finalTransform, room, reservedTiles, clearExisting); } - public Angle GetRoomRotation(DungeonRoomPrototype room, Random random) + public Angle GetRoomRotation(DungeonRoomPrototype room, IRobustRandom random) { var roomRotation = Angle.Zero; diff --git a/Content.Server/Procedural/RoomFillSystem.cs b/Content.Server/Procedural/RoomFillSystem.cs index f4ccab2367..96adb8dec3 100644 --- a/Content.Server/Procedural/RoomFillSystem.cs +++ b/Content.Server/Procedural/RoomFillSystem.cs @@ -1,4 +1,5 @@ using Robust.Shared.Map.Components; +using Robust.Shared.Random; namespace Content.Server.Procedural; @@ -6,6 +7,7 @@ public sealed class RoomFillSystem : EntitySystem { [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly SharedMapSystem _maps = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -19,8 +21,7 @@ public sealed class RoomFillSystem : EntitySystem if (xform.GridUid != null) { - var random = new Random(); - var room = _dungeon.GetRoomPrototype(random, component.RoomWhitelist, component.MinSize, component.MaxSize); + var room = _dungeon.GetRoomPrototype(_random, component.RoomWhitelist, component.MinSize, component.MaxSize); if (room != null) { @@ -30,7 +31,7 @@ public sealed class RoomFillSystem : EntitySystem mapGrid, _maps.LocalToTile(xform.GridUid.Value, mapGrid, xform.Coordinates) - new Vector2i(room.Size.X/2,room.Size.Y/2), room, - random, + _random, null, clearExisting: component.ClearExisting, rotation: component.Rotation); diff --git a/Content.Server/Radiation/Components/RadiationProtectionComponent.cs b/Content.Server/Radiation/Components/RadiationProtectionComponent.cs deleted file mode 100644 index 44b11af834..0000000000 --- a/Content.Server/Radiation/Components/RadiationProtectionComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Robust.Shared.Prototypes; -using Content.Shared.Damage.Prototypes; - -namespace Content.Server.Radiation.Components; - -/// -/// Exists for use as a status effect. -/// Adds the DamageProtectionBuffComponent to the entity and adds the specified DamageModifierSet to its list of modifiers. -/// -[RegisterComponent] -public sealed partial class RadiationProtectionComponent : Component -{ - /// - /// The radiation damage modifier for entities with this component. - /// - [DataField("modifier")] - public ProtoId RadiationProtectionModifierSetId = "PotassiumIodide"; -} diff --git a/Content.Server/Radiation/Systems/GeigerSystem.cs b/Content.Server/Radiation/Systems/GeigerSystem.cs index 77df6c09e2..eb69e2ccd8 100644 --- a/Content.Server/Radiation/Systems/GeigerSystem.cs +++ b/Content.Server/Radiation/Systems/GeigerSystem.cs @@ -47,7 +47,7 @@ public sealed class GeigerSystem : SharedGeigerSystem { if (geiger.Comp.AttachedToSuit) SetEnabled(geiger, true); - SetUser(geiger, args.Equipee); + SetUser(geiger, args.EquipTarget); } private void OnEquippedHand(Entity geiger, ref GotEquippedHandEvent args) diff --git a/Content.Server/Radiation/Systems/RadiationProtectionSystem.cs b/Content.Server/Radiation/Systems/RadiationProtectionSystem.cs deleted file mode 100644 index a32fa810c9..0000000000 --- a/Content.Server/Radiation/Systems/RadiationProtectionSystem.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Content.Server.Radiation.Components; -using Content.Shared.Damage.Components; -using Robust.Shared.Prototypes; - -namespace Content.Server.Radiation.EntitySystems; - -public sealed class RadiationProtectionSystem : EntitySystem -{ - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - } - - private void OnInit(EntityUid uid, RadiationProtectionComponent component, ComponentInit args) - { - if (!_prototypeManager.Resolve(component.RadiationProtectionModifierSetId, out var modifier)) - return; - var buffComp = EnsureComp(uid); - // add the damage modifier if it isn't in the dict yet - if (!buffComp.Modifiers.ContainsKey(component.RadiationProtectionModifierSetId)) - buffComp.Modifiers.Add(component.RadiationProtectionModifierSetId, modifier); - } - - private void OnShutdown(EntityUid uid, RadiationProtectionComponent component, ComponentShutdown args) - { - if (!TryComp(uid, out var buffComp)) - return; - // remove the damage modifier from the dict - buffComp.Modifiers.Remove(component.RadiationProtectionModifierSetId); - // if the dict is empty now, remove the buff component - if (buffComp.Modifiers.Count == 0) - RemComp(uid); - } -} diff --git a/Content.Server/Radiation/Systems/RadiationSystem.cs b/Content.Server/Radiation/Systems/RadiationSystem.cs index 4a4d0f8930..3fbc7e0d28 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Radiation.Components; using Content.Shared.Radiation.Components; using Content.Shared.Radiation.Events; +using Content.Shared.Radiation.Systems; using Content.Shared.Stacks; using Robust.Shared.Configuration; using Robust.Shared.Map; @@ -8,7 +9,7 @@ using Robust.Shared.Map.Components; namespace Content.Server.Radiation.Systems; -public sealed partial class RadiationSystem : EntitySystem +public sealed partial class RadiationSystem : SharedRadiationSystem { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; @@ -16,10 +17,9 @@ public sealed partial class RadiationSystem : EntitySystem [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] private readonly SharedMapSystem _maps = default!; - private EntityQuery _blockerQuery; - private EntityQuery _resistanceQuery; - private EntityQuery _gridQuery; - private EntityQuery _stackQuery; + [Dependency] private readonly EntityQuery _blockerQuery = default!; + [Dependency] private readonly EntityQuery _resistanceQuery = default!; + [Dependency] private readonly EntityQuery _gridQuery = default!; private float _accumulator; private List _sources = new(); @@ -29,11 +29,6 @@ public sealed partial class RadiationSystem : EntitySystem base.Initialize(); SubscribeCvars(); InitRadBlocking(); - - _blockerQuery = GetEntityQuery(); - _resistanceQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - _stackQuery = GetEntityQuery(); } public override void Update(float frameTime) diff --git a/Content.Server/Radio/EntitySystems/HeadsetSystem.cs b/Content.Server/Radio/EntitySystems/HeadsetSystem.cs index 7d16687d5f..13b331c778 100644 --- a/Content.Server/Radio/EntitySystems/HeadsetSystem.cs +++ b/Content.Server/Radio/EntitySystems/HeadsetSystem.cs @@ -58,7 +58,7 @@ public sealed class HeadsetSystem : SharedHeadsetSystem base.OnGotEquipped(uid, component, args); if (component.IsEquipped && component.Enabled) { - EnsureComp(args.Equipee).Headset = uid; + EnsureComp(args.EquipTarget).Headset = uid; UpdateRadioChannels(uid, component); } } @@ -67,7 +67,7 @@ public sealed class HeadsetSystem : SharedHeadsetSystem { base.OnGotUnequipped(uid, component, args); RemComp(uid); - RemComp(args.Equipee); + RemComp(args.EquipTarget); } public void SetEnabled(EntityUid uid, bool value, HeadsetComponent? component = null) diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index c5080c0b06..19b6a15530 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Server.Actions; +using Content.Server.Atmos.EntitySystems; using Content.Server.GameTicking; using Content.Server.Store.Systems; using Content.Shared.Alert; @@ -28,6 +29,7 @@ public sealed partial class RevenantSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly DamageableSystem _damage = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly GameTicker _ticker = default!; @@ -188,6 +190,23 @@ public sealed partial class RevenantSystem : EntitySystem } } + /// + /// Cools the area around the revenant. + /// The more essence they have, the colder it gets, up to a certain point. + /// + /// The revenant entity. + private void ChillArea(Entity ent) + { + var effectiveEssence = Math.Clamp(ent.Comp.Essence.Int(), 0, ent.Comp.ChillUpperBound.Float()); + // Parabolic curve based on essence, more essence = more delta q, flattening as upper bound is reached + var dQ = + 200 / ent.Comp.ChillScaling.Float() * MathF.Pow(effectiveEssence - ent.Comp.ChillUpperBound.Float(), 2) - + ent.Comp.ChillScaling.Float(); + + if (_atmosphere.GetContainingMixture(ent.Owner, true, true) is { } air) + _atmosphere.AddHeat(air, dQ); + } + public override void Update(float frameTime) { base.Update(frameTime); @@ -205,6 +224,8 @@ public sealed partial class RevenantSystem : EntitySystem { ChangeEssenceAmount(uid, rev.EssencePerSecond, rev, regenCap: true); } + + ChillArea((uid, rev)); } } } diff --git a/Content.Server/Revolutionary/Components/RevolutionaryConverterComponent.cs b/Content.Server/Revolutionary/Components/RevolutionaryConverterComponent.cs new file mode 100644 index 0000000000..61acf26d5b --- /dev/null +++ b/Content.Server/Revolutionary/Components/RevolutionaryConverterComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.Revolutionary.Components; + +/// +/// This is a marker component that indicates that a flash can be used to convert someone into a revolutionary. +/// Viva la revolution! +/// +[RegisterComponent] +public sealed partial class RevolutionaryConverterComponent : Component; diff --git a/Content.Server/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs index 4492c23172..2d5caae2db 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -318,7 +318,7 @@ namespace Content.Server.RoundEnd Loc.GetString( "round-end-system-round-restart-eta-announcement", ("time", time), - ("units", Loc.GetString(unitsLocString)))); + ("units", Loc.GetString(unitsLocString, ("amount", time))))); Timer.Spawn(countdownTime.Value, AfterEndRoundRestart, _countdownTokenSource.Token); } diff --git a/Content.Server/Shuttles/Components/StationEmergencyShuttleComponent.cs b/Content.Server/Shuttles/Components/StationEmergencyShuttleComponent.cs index d4fcfb81c6..cfb667e1f0 100644 --- a/Content.Server/Shuttles/Components/StationEmergencyShuttleComponent.cs +++ b/Content.Server/Shuttles/Components/StationEmergencyShuttleComponent.cs @@ -26,35 +26,42 @@ public sealed partial class StationEmergencyShuttleComponent : Component /// /// The announcement made when the shuttle has successfully docked with the station. /// + [DataField] public LocId DockedAnnouncement = "emergency-shuttle-docked"; /// /// Sound played when the shuttle has successfully docked with the station. /// + [DataField] public SoundSpecifier DockedAudio = new SoundPathSpecifier("/Audio/Announcements/shuttle_dock.ogg"); /// /// The announcement made when the shuttle is unable to dock and instead parks in nearby space. /// + [DataField] public LocId NearbyAnnouncement = "emergency-shuttle-nearby"; /// /// Sound played when the shuttle is unable to dock and instead parks in nearby space. /// + [DataField] public SoundSpecifier NearbyAudio = new SoundPathSpecifier("/Audio/Misc/notice1.ogg"); /// /// The announcement made when the shuttle is unable to find a station. /// + [DataField] public LocId FailureAnnouncement = "emergency-shuttle-good-luck"; /// /// Sound played when the shuttle is unable to find a station. /// + [DataField] public SoundSpecifier FailureAudio = new SoundPathSpecifier("/Audio/Misc/notice1.ogg"); /// /// Text appended to the docking announcement if the launch time has been extended. /// + [DataField] public LocId LaunchExtendedMessage = "emergency-shuttle-extended"; } diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs index f675e19ecb..1a82d9ac63 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs @@ -264,7 +264,7 @@ public sealed partial class ShuttleSystem if (direction.LengthSquared() > minsq) { _stuns.TryCrawling(ent.Owner, knockdownTime); - _throwing.TryThrow(ent, direction, ent.Comp, Transform(ent), _projQuery, direction.Length(), playSound: false); + _throwing.TryThrow(ent, direction, ent.Comp, Transform(ent), direction.Length(), playSound: false); } else { @@ -422,7 +422,7 @@ public sealed partial class ShuttleSystem else { var direction = throwDirection * tileData.DistanceFactor; - _throwing.TryThrow(localEnt, direction, physics, localEnt.Comp, _projQuery, direction.Length(), playSound: false); + _throwing.TryThrow(localEnt, direction, physics, localEnt.Comp, direction.Length(), playSound: false); } } diff --git a/Content.Server/Silicons/Laws/IonLawSystem.cs b/Content.Server/Silicons/Laws/IonLawSystem.cs index 9d928b37a0..b8bc955825 100644 --- a/Content.Server/Silicons/Laws/IonLawSystem.cs +++ b/Content.Server/Silicons/Laws/IonLawSystem.cs @@ -1,4 +1,5 @@ -using Content.Shared.Dataset; +using Content.Server.StationRecords.Systems; +using Content.Shared.Dataset; using Content.Shared.Silicons.Laws; using Content.Shared.Station; using Content.Shared.StationRecords; @@ -16,7 +17,7 @@ public sealed class IonLawSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedStationSystem _stationSystem = default!; - [Dependency] private readonly SharedStationRecordsSystem _stationRecordsSystem = default!; + [Dependency] private readonly StationRecordsSystem _stationRecordsSystem = default!; [Dependency] private readonly ILogManager _logManager = default!; private ISawmill _sawmill = default!; diff --git a/Content.Server/Silicons/StationAi/StationAiSystem.cs b/Content.Server/Silicons/StationAi/StationAiSystem.cs index cc2f7a4e2f..b16c98f56b 100644 --- a/Content.Server/Silicons/StationAi/StationAiSystem.cs +++ b/Content.Server/Silicons/StationAi/StationAiSystem.cs @@ -67,6 +67,7 @@ public sealed class StationAiSystem : SharedStationAiSystem private readonly ProtoId _aiWireSnippedChatNotificationPrototype = "AiWireSnipped"; private readonly ProtoId _aiLosingPowerChatNotificationPrototype = "AiLosingPower"; private readonly ProtoId _aiCriticalPowerChatNotificationPrototype = "AiCriticalPower"; + private readonly ProtoId _aiTakingDamageChatNotificationPrototype = "AiTakingDamage"; private readonly ProtoId _stationAiJob = "StationAi"; private readonly EntProtoId _stationAiBrain = "StationAiBrain"; @@ -235,7 +236,7 @@ public sealed class StationAiSystem : SharedStationAiSystem private void OnDamageChanged(Entity entity, ref DamageChangedEvent args) { - UpdateCoreIntegrityAlert(entity); + UpdateCoreIntegrityAlert(entity, args.DamageIncreased); UpdateDamagedAccent(entity); } @@ -285,7 +286,7 @@ public sealed class StationAiSystem : SharedStationAiSystem } } - private void UpdateCoreIntegrityAlert(Entity ent) + private void UpdateCoreIntegrityAlert(Entity ent, bool damageIncreased = false) { if (!TryComp(ent, out var damageable)) return; @@ -303,6 +304,12 @@ public sealed class StationAiSystem : SharedStationAiSystem var damageLevel = Math.Round(damagePercent.Float() * proto.MaxSeverity); _alerts.ShowAlert(held.Value, _damageAlert, (short)Math.Clamp(damageLevel, 0, proto.MaxSeverity)); + + if (damageIncreased) + { + var ev = new ChatNotificationEvent(_aiTakingDamageChatNotificationPrototype, ent); + RaiseLocalEvent(held.Value, ref ev); + } } private void OnDoAfterAttempt(Entity ent, ref DoAfterAttemptEvent args) diff --git a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs index 6905b38b43..837ba05df1 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -50,7 +50,7 @@ namespace Content.Server.Singularity.EntitySystems SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnAnchorStateChanged); SubscribeLocalEvent(OnSignalReceived); - SubscribeLocalEvent(OnDestructionAttempted); + SubscribeLocalEvent(OnDestruction); SubscribeLocalEvent(OnDeconstructed); // you shouldn't be able to deconstruct locked emitters but out of scope to fix SubscribeLocalEvent(OnLockToggled); } @@ -297,11 +297,9 @@ namespace Content.Server.Singularity.EntitySystems } } - private void OnDestructionAttempted(Entity ent, ref DestructionAttemptEvent args) + private void OnDestruction(Entity ent, ref DestructionEventArgs args) { - // warn engineering their containment engine needs IMMEDIATE repairs - // this doesn't change much for natural loosing through emitter destruction given any meteor warning serves the same purpose - // can also be used to scare engineering though given it broadcasts its location you need a renamed station beacon to really scare them + // Engineering needs to know if an emitter is destroyed so they can replace it before the engine looses. AlertRadio(ent, ent.Comp.LocDestroyed); } diff --git a/Content.Server/Sound/EmitSoundSystem.cs b/Content.Server/Sound/EmitSoundSystem.cs index 1720d67d02..38878b147f 100644 --- a/Content.Server/Sound/EmitSoundSystem.cs +++ b/Content.Server/Sound/EmitSoundSystem.cs @@ -1,14 +1,12 @@ using Content.Shared.Sound; using Content.Shared.Sound.Components; using Robust.Shared.Timing; -using Robust.Shared.Network; namespace Content.Server.Sound; public sealed class EmitSoundSystem : SharedEmitSoundSystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly INetManager _net = default!; public override void Update(float frameTime) { @@ -49,9 +47,6 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem private void SpamEmitSoundReset(Entity entity) { - if (_net.IsClient) - return; - entity.Comp.NextSound = _timing.CurTime + ((entity.Comp.MinInterval < entity.Comp.MaxInterval) ? Random.Next(entity.Comp.MinInterval, entity.Comp.MaxInterval) : entity.Comp.MaxInterval); diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index a0d923dd1e..a6b75f953c 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -21,7 +21,9 @@ namespace Content.Server.Stack /// Spawns a new entity and moves an amount to it from the stack. /// Moves nothing if amount is greater than ent's stack count. /// - /// How much to move to the new entity. + /// Entity to split in a new stack. + /// How much to move to the new entity. + /// Where to spawn the new stack /// Null if StackComponent doesn't resolve, or amount to move is greater than ent has available. [PublicAPI] public EntityUid? Split(Entity ent, int amount, EntityCoordinates spawnPosition) diff --git a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs index 01c31c9d57..1bc9c561b7 100644 --- a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs +++ b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Access.Systems; using Content.Server.Forensics; using Content.Server.GameTicking; @@ -45,6 +46,7 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly RoleSystem _role = default!; [Dependency] private readonly IdCardSystem _idCard = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -248,6 +250,27 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem return false; } + /// + /// Gets a random record from the station's record entries. + /// + /// The EntityId of the station from which you want to get the record. + /// The resulting entry. + /// Type to get from the record set. + /// True if a record was obtained. False otherwise. + public bool TryGetRandomRecord(Entity ent, [NotNullWhen(true)] out T? entry) + { + entry = default; + + if (!Resolve(ent.Owner, ref ent.Comp)) + return false; + + if (ent.Comp.Records.Keys.Count == 0) + return false; + + var key = _random.Pick(ent.Comp.Records.Keys); + + return ent.Comp.Records.TryGetRecordEntry(key, out entry); + } /// /// Get the name for a record, or an empty string if it has no record. diff --git a/Content.Server/Storage/StorageCommand.cs b/Content.Server/Storage/StorageCommand.cs index 1c39801622..d5855388fa 100644 --- a/Content.Server/Storage/StorageCommand.cs +++ b/Content.Server/Storage/StorageCommand.cs @@ -52,7 +52,7 @@ public sealed class StorageCommand : ToolshedCommand return null; } - [CommandImplementation("query")] + [CommandImplementation("contents")] public IEnumerable StorageQuery([PipedArgument] IEnumerable storageEnts, bool recursive) => storageEnts.SelectMany(x => StorageQueryRecursiveBase(x, recursive)); diff --git a/Content.Server/Store/Systems/StoreSystem.Refund.cs b/Content.Server/Store/Systems/StoreSystem.Refund.cs index cb92a42c10..f4ecd87040 100644 --- a/Content.Server/Store/Systems/StoreSystem.Refund.cs +++ b/Content.Server/Store/Systems/StoreSystem.Refund.cs @@ -4,11 +4,14 @@ using Content.Shared.Interaction.Events; using Content.Shared.Store.Components; using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Containers; +using Robust.Shared.Timing; namespace Content.Server.Store.Systems; public sealed partial class StoreSystem { + [Dependency] private readonly IGameTiming _timing = default!; + private void InitializeRefund() { SubscribeLocalEvent(OnStoreTerminating); diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index dcd6189fbf..6d4973cc70 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -7,16 +7,12 @@ using Content.Shared.Actions; using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Hands.EntitySystems; -using Content.Shared.Mind; using Content.Shared.Mindshield.Components; using Content.Shared.NPC.Systems; -using Content.Shared.PDA.Ringer; using Content.Shared.Store; using Content.Shared.Store.Components; using Content.Shared.UserInterface; -using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Server.Store.Systems; @@ -24,15 +20,14 @@ namespace Content.Server.Store.Systems; public sealed partial class StoreSystem { [Dependency] private readonly IAdminLogManager _admin = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly ActionUpgradeSystem _actionUpgrade = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly StackSystem _stack = default!; - [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly StackSystem _stack = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; private void InitializeUi() { @@ -41,6 +36,16 @@ public sealed partial class StoreSystem SubscribeLocalEvent(OnRequestWithdraw); SubscribeLocalEvent(OnRequestRefund); SubscribeLocalEvent(OnRefundEntityDeleted); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); } private void OnRefundEntityDeleted(Entity ent, ref RefundEntityDeletedEvent args) @@ -48,73 +53,12 @@ public sealed partial class StoreSystem ent.Comp.BoughtEntities.Remove(args.Uid); } - /// - /// Toggles the store Ui open and closed - /// - /// the person doing the toggling - /// the store being toggled - /// - public void ToggleUi(EntityUid user, EntityUid storeEnt, StoreComponent? component = null) + private void RemoteStoreRelay(Entity entity, object ev) { - if (!Resolve(storeEnt, ref component)) + if (entity.Comp.Store == null || !TryComp(entity.Comp.Store, out var store)) return; - if (!TryComp(user, out var actor)) - return; - - if (!_ui.TryToggleUi(storeEnt, StoreUiKey.Key, actor.PlayerSession)) - return; - - UpdateUserInterface(user, storeEnt, component); - } - - /// - /// Closes the store UI for everyone, if it's open - /// - public void CloseUi(EntityUid uid, StoreComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - _ui.CloseUi(uid, StoreUiKey.Key); - } - - /// - /// Updates the user interface for a store and refreshes the listings - /// - /// The person who if opening the store ui. Listings are filtered based on this. - /// The store entity itself - /// The store component being refreshed. - public void UpdateUserInterface(EntityUid? user, EntityUid store, StoreComponent? component = null) - { - if (!Resolve(store, ref component)) - return; - - //this is the person who will be passed into logic for all listing filtering. - if (user != null) //if we have no "buyer" for this update, then don't update the listings - { - component.LastAvailableListings = GetAvailableListings(component.AccountOwner ?? user.Value, store, component) - .ToHashSet(); - } - - //dictionary for all currencies, including 0 values for currencies on the whitelist - Dictionary, FixedPoint2> allCurrency = new(); - foreach (var supported in component.CurrencyWhitelist) - { - allCurrency.Add(supported, FixedPoint2.Zero); - - if (component.Balance.TryGetValue(supported, out var value)) - allCurrency[supported] = value; - } - - // TODO: if multiple users are supposed to be able to interact with a single BUI & see different - // stores/listings, this needs to use session specific BUI states. - - // only tell operatives to lock their uplink if it can be locked - var showFooter = HasComp(store); - - var state = new StoreUpdateState(component.LastAvailableListings, allCurrency, showFooter, component.RefundAllowed); - _ui.SetUiState(store, StoreUiKey.Key, state); + RaiseLocalEvent(entity.Comp.Store.Value, ev); } private void OnRequestUpdate(EntityUid uid, StoreComponent component, StoreRequestUpdateInterfaceMessage args) @@ -179,6 +123,13 @@ public sealed partial class StoreSystem component.BalanceSpent[currency] += amount; } + //apply components + if (listing.ProductComponents != null) + { + if (_proto.Resolve(listing.ProductComponents, out var productComponentsEntity)) + EntityManager.AddComponents(buyer, productComponentsEntity.Components); + } + //spawn entity if (listing.ProductEntity != null) { @@ -205,7 +156,7 @@ public sealed partial class StoreSystem EntityUid? actionId; // I guess we just allow duplicate actions? // Allow duplicate actions and just have a single list buy for the buy-once ones. - if (listing.ApplyToMob || !_mind.TryGetMind(buyer, out var mind, out _)) + if (listing.ApplyToMob || !Mind.TryGetMind(buyer, out var mind, out _)) actionId = _actions.AddAction(buyer, listing.ProductAction); else actionId = _actionContainer.AddAction(mind, listing.ProductAction); @@ -281,10 +232,11 @@ public sealed partial class StoreSystem _admin.Add(LogType.StorePurchase, logImpact, - $"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _proto)}\" from {ToPrettyString(uid)}{logExtraInfo}."); + $"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, Proto)}\" from {ToPrettyString(uid)}{logExtraInfo}."); listing.PurchaseAmount++; //track how many times something has been purchased - _audio.PlayEntity(component.BuySuccessSound, msg.Actor, uid); //cha-ching! + if (msg.SoundSource != null && GetEntity(msg.SoundSource) != null) + _audio.PlayEntity(component.BuySuccessSound, msg.Actor, GetEntity(msg.SoundSource.Value)); //cha-ching! var buyFinished = new StoreBuyFinishedEvent { @@ -313,7 +265,7 @@ public sealed partial class StoreSystem return; //make sure a malicious client didn't send us random shit - if (!_proto.TryIndex(msg.Currency, out var proto)) + if (!Proto.TryIndex(msg.Currency, out var proto)) return; //we need an actually valid entity to spawn. This check has been done earlier, but just in case. diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index 7c5c99b5b4..b8e92f10bf 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -1,42 +1,25 @@ -using System.Linq; -using Content.Server.Store.Components; -using Content.Shared.FixedPoint; using Content.Shared.Implants.Components; -using Content.Shared.Interaction; -using Content.Shared.Popups; -using Content.Shared.Stacks; +using Content.Shared.Store; using Content.Shared.Store.Components; -using Content.Shared.Store.Events; using Content.Shared.UserInterface; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Store.Systems; -/// -/// Manages general interactions with a store and different entities, -/// getting listings for stores, and interfacing with the store UI. -/// -public sealed partial class StoreSystem : EntitySystem +public sealed partial class StoreSystem : SharedStoreSystem { - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly IGameTiming _timing = default!; - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStoreOpenAttempt); - SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(BeforeActivatableUiOpen); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnImplantActivate); - SubscribeLocalEvent(OnIntrinsicStoreAction); + + SubscribeLocalEvent(OnImplantActivate); InitializeUi(); InitializeCommand(); @@ -47,6 +30,10 @@ public sealed partial class StoreSystem : EntitySystem { RefreshAllListings(component); component.StartingMap = Transform(uid).MapUid; + + // Add the bui key if it does not exist already (the check is needed to make sure that we don't overwrite existing InterfaceData). + if (!UI.HasUi(uid, StoreUiKey.Key)) + UI.SetUi(uid, StoreUiKey.Key, new InterfaceData("StoreBoundUserInterface")); } private void OnStartup(EntityUid uid, StoreComponent component, ComponentStartup args) @@ -72,7 +59,7 @@ public sealed partial class StoreSystem : EntitySystem if (!component.OwnerOnly) return; - if (!_mind.TryGetMind(args.User, out var mind, out _)) + if (!Mind.TryGetMind(args.User, out var mind, out _)) return; component.AccountOwner ??= mind; @@ -82,133 +69,16 @@ public sealed partial class StoreSystem : EntitySystem return; if (!args.Silent) - _popup.PopupEntity(Loc.GetString("store-not-account-owner", ("store", uid)), uid, args.User); + Popup.PopupEntity(Loc.GetString("store-not-account-owner", ("store", uid)), uid, args.User); args.Cancel(); } - private void OnAfterInteract(EntityUid uid, CurrencyComponent component, AfterInteractEvent args) + private void OnImplantActivate(Entity entity, ref OpenUplinkImplantEvent args) { - if (args.Handled || !args.CanReach) + if (GetRemoteStore(entity.AsNullable()) is not { } store) return; - if (!TryComp(args.Target, out var store)) - return; - - var ev = new CurrencyInsertAttemptEvent(args.User, args.Target.Value, args.Used, store); - RaiseLocalEvent(args.Target.Value, ev); - if (ev.Cancelled) - return; - - if (!TryAddCurrency((uid, component), (args.Target.Value, store))) - return; - - args.Handled = true; - var msg = Loc.GetString("store-currency-inserted", ("used", args.Used), ("target", args.Target)); - _popup.PopupEntity(msg, args.Target.Value, args.User); - } - - private void OnImplantActivate(EntityUid uid, StoreComponent component, OpenUplinkImplantEvent args) - { - ToggleUi(args.Performer, uid, component); - } - - /// - /// Gets the value from an entity's currency component. - /// Scales with stacks. - /// - /// - /// If this result is intended to be used with , - /// consider using instead to ensure that the currency is consumed in the process. - /// - /// - /// - /// The value of the currency - public Dictionary GetCurrencyValue(EntityUid uid, CurrencyComponent component) - { - var amount = EntityManager.GetComponentOrNull(uid)?.Count ?? 1; - return component.Price.ToDictionary(v => v.Key, p => p.Value * amount); - } - - /// - /// Tries to add a currency to a store's balance. Note that if successful, this will consume the currency in the process. - /// - public bool TryAddCurrency(Entity currency, Entity store) - { - if (!Resolve(currency.Owner, ref currency.Comp)) - return false; - - if (!Resolve(store.Owner, ref store.Comp)) - return false; - - var value = currency.Comp.Price; - if (TryComp(currency.Owner, out StackComponent? stack) && stack.Count != 1) - { - value = currency.Comp.Price - .ToDictionary(v => v.Key, p => p.Value * stack.Count); - } - - if (!TryAddCurrency(value, store, store.Comp)) - return false; - - // Avoid having the currency accidentally be re-used. E.g., if multiple clients try to use the currency in the - // same tick - currency.Comp.Price.Clear(); - if (stack != null) - _stack.SetCount((currency.Owner, stack), 0); - - QueueDel(currency); - return true; - } - - /// - /// Tries to add a currency to a store's balance - /// - /// The value to add to the store - /// - /// The store to add it to - /// Whether or not the currency was succesfully added - public bool TryAddCurrency(Dictionary currency, EntityUid uid, StoreComponent? store = null) - { - if (!Resolve(uid, ref store)) - return false; - - //verify these before values are modified - foreach (var type in currency) - { - if (!store.CurrencyWhitelist.Contains(type.Key)) - return false; - } - - foreach (var type in currency) - { - if (!store.Balance.TryAdd(type.Key, type.Value)) - store.Balance[type.Key] += type.Value; - } - - UpdateUserInterface(null, uid, store); - return true; - } - - private void OnIntrinsicStoreAction(Entity ent, ref IntrinsicStoreActionEvent args) - { - ToggleUi(args.Performer, ent.Owner, ent.Comp); - } - -} - -public sealed class CurrencyInsertAttemptEvent : CancellableEntityEventArgs -{ - public readonly EntityUid User; - public readonly EntityUid Target; - public readonly EntityUid Used; - public readonly StoreComponent Store; - - public CurrencyInsertAttemptEvent(EntityUid user, EntityUid target, EntityUid used, StoreComponent store) - { - User = user; - Target = target; - Used = used; - Store = store; + ToggleUi(args.Performer, store, store.Comp, entity, entity.Comp); } } diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs index e8c53de9eb..723ddbf0ff 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs @@ -30,7 +30,7 @@ public sealed class SurveillanceCameraMicrophoneSystem : EntitySystem // This function ensures that chat popups appear on camera views that have connected microphones. foreach (var (_, __, camera, xform) in EntityQuery()) { - if (camera.ActiveViewers.Count == 0) + if (camera.ActivePvsViewers.Count == 0) continue; // get range to camera. This way wispers will still appear as obfuscated if they are too far from the camera's microphone @@ -41,7 +41,7 @@ public sealed class SurveillanceCameraMicrophoneSystem : EntitySystem if (range < 0 || range > ev.VoiceRange) continue; - foreach (var viewer in camera.ActiveViewers) + foreach (var viewer in camera.ActivePvsViewers) { // if the player has not already received the chat message, send it to them but don't log it to the chat // window. This is simply so that it appears in camera. diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.Collide.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.Collide.cs new file mode 100644 index 0000000000..4015a65cc9 --- /dev/null +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.Collide.cs @@ -0,0 +1,101 @@ +using Content.Shared.Power.EntitySystems; +using Content.Shared.SurveillanceCamera; +using Content.Shared.SurveillanceCamera.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; + +namespace Content.Server.SurveillanceCamera; + +public partial class SurveillanceCameraSystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] private readonly EntityQuery _cameraQuery = default!; + + public void InitializeCollide() + { + SubscribeLocalEvent(OnPreventCollide); + SubscribeLocalEvent(OnStart); + SubscribeLocalEvent(OnEnd); + + SubscribeLocalEvent(OnCollideShutdown); + SubscribeLocalEvent(OnOverrideState); + } + + private void OnCollideShutdown(Entity ent, ref ComponentShutdown args) + { + // TODO: Check this on the event. + if (TerminatingOrDeleted(ent.Owner)) + return; + + // Regenerate contacts for everything we were colliding with. + var contacts = _physics.GetContacts(ent.Owner); + + while (contacts.MoveNext(out var contact)) + { + if (!contact.IsTouching) + continue; + + var other = contact.OtherEnt(ent.Owner); + + if (_cameraQuery.HasComp(other)) + { + _physics.RegenerateContacts(other); + } + } + } + + // You may be wondering what de fok this is doing here. + // At the moment there's no easy way to do collision whitelists based on components. + private void OnPreventCollide(Entity ent, ref PreventCollideEvent args) + { + if (!_cameraQuery.HasComp(args.OtherEntity)) + { + args.Cancelled = true; + } + } + + private void OnEnd(Entity ent, ref EndCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + if (!_cameraQuery.TryComp(args.OtherEntity, out var cameraCollider)) + return; + + // TODO: Engine bug IsTouching box2d yay. + var contacts = _physics.GetTouchingContacts(args.OtherEntity) - 1; + + if (contacts > 0) + return; + + cameraCollider.Enabled = false; + Dirty(args.OtherEntity, cameraCollider); + UpdateVisuals(args.OtherEntity); + } + + private void OnStart(Entity ent, ref StartCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + if (!_cameraQuery.TryComp(args.OtherEntity, out var cameraCollider)) + return; + + cameraCollider.Enabled = true; + Dirty(args.OtherEntity, cameraCollider); + UpdateVisuals(args.OtherEntity); + } + + private void OnOverrideState(Entity ent, ref SurveillanceCameraGetIsViewedExternallyEvent args) + { + if (ent.Comp.RequiresPower && !_power.IsPowered(ent.Owner)) + return; + + if (!ent.Comp.Enabled) + return; + + args.Viewed = true; + } +} + diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs index cdd032c79c..09c2b39447 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs @@ -13,15 +13,15 @@ using Content.Shared.DeviceNetwork.Components; namespace Content.Server.SurveillanceCamera; -public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem +public sealed partial class SurveillanceCameraSystem : SharedSurveillanceCameraSystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly ViewSubscriberSystem _viewSubscriberSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterface = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly SurveillanceCameraMapSystem _cameraMapSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; // Pings a surveillance camera subnet. All cameras will always respond // with a data message if they are on the same subnet. @@ -61,6 +61,8 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem SubscribeLocalEvent(OnPacketReceived); SubscribeLocalEvent(OnSetName); SubscribeLocalEvent(OnSetNetwork); + + InitializeCollide(); } private void OnPacketReceived(EntityUid uid, SurveillanceCameraComponent component, DeviceNetworkPacketEvent args) @@ -232,7 +234,7 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem var ev = new SurveillanceCameraDeactivateEvent(camera); - RemoveActiveViewers(camera, new(component.ActiveViewers), null, component); + RemoveActiveViewers(camera, new(component.ActivePvsViewers), null, component); component.Active = false; // Send a targetted event to all monitors. @@ -249,6 +251,25 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem UpdateVisuals(camera, component); } + /// + /// Checks whether the camera is being viewed through by anyone at all. + /// + /// The camera to check + /// True if the camera is looked through, otherwise False. + public bool IsGettingViewed(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + if (ent.Comp.ActivePvsViewers.Count > 0 || ent.Comp.ActiveMonitors.Count > 0) + return true; + + var ev = new SurveillanceCameraGetIsViewedExternallyEvent(); + RaiseLocalEvent(ent, ref ev); + + return ev.Viewed; + } + public override void SetActive(EntityUid camera, bool setting, SurveillanceCameraComponent? component = null) { if (!Resolve(camera, ref component)) @@ -284,7 +305,8 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem } _viewSubscriberSystem.AddViewSubscriber(camera, actor.PlayerSession); - component.ActiveViewers.Add(player); + + component.ActivePvsViewers.Add(player); if (monitor != null) { @@ -346,7 +368,7 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem if (Resolve(player, ref actor)) _viewSubscriberSystem.RemoveViewSubscriber(camera, actor.PlayerSession); - component.ActiveViewers.Remove(player); + component.ActivePvsViewers.Remove(player); if (monitor != null) { @@ -391,7 +413,7 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem key = SurveillanceCameraVisuals.Active; } - if (component.ActiveViewers.Count > 0 || component.ActiveMonitors.Count > 0) + if (IsGettingViewed((uid, component))) { key = SurveillanceCameraVisuals.InUse; } diff --git a/Content.Server/Telephone/TelephoneSystem.cs b/Content.Server/Telephone/TelephoneSystem.cs index 0314a116b9..052fcd8871 100644 --- a/Content.Server/Telephone/TelephoneSystem.cs +++ b/Content.Server/Telephone/TelephoneSystem.cs @@ -118,12 +118,12 @@ public sealed class TelephoneSystem : SharedTelephoneSystem // If speaker entity has TTS, the telephone will speak with the same voice if(TryComp(args.MessageSource, out var ttsSpeaker)) { - var ttsTelephone = EnsureComp(entity); + var ttsTelephone = EnsureComp(speaker); ttsTelephone.VoicePrototypeId = ttsSpeaker.VoicePrototypeId; } else // Remove TTS if the speaker has no TTS { - RemComp(entity); + RemComp(speaker); } // Corvax-TTS-End _chat.TrySendInGameICMessage(speaker, args.Message, volume, range, nameOverride: name, checkRadioPrefix: false); diff --git a/Content.Server/Toolshed/TypeParsers/StatusEffects/StatusEffectCompletionParser.cs b/Content.Server/Toolshed/TypeParsers/StatusEffects/StatusEffectCompletionParser.cs index 6b605e98d3..839f7fa9ca 100644 --- a/Content.Server/Toolshed/TypeParsers/StatusEffects/StatusEffectCompletionParser.cs +++ b/Content.Server/Toolshed/TypeParsers/StatusEffects/StatusEffectCompletionParser.cs @@ -11,6 +11,6 @@ public sealed class StatusEffectCompletionParser : CustomCompletionParser().StatusEffectPrototypes, GetArgHint(arg)); + return CompletionResult.FromHintOptions(IoCManager.Resolve().System().StatusEffectPrototypes, GetArgHint(arg)); } } diff --git a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs index 8350c01f7a..bf00f5ebd8 100644 --- a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs +++ b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs @@ -73,7 +73,13 @@ public sealed class AddUplinkCommand : LocalizedEntityCommands } // Finally add uplink - if (!_uplinkSystem.AddUplink(user, 20, uplinkEntity: uplinkEntity, giveDiscounts: isDiscounted)) + var result = _uplinkSystem.AddUplink(user, 20, out var code, uplinkEntity: uplinkEntity, giveDiscounts: isDiscounted); + + if (code != null && result == AddUplinkResult.Pda) + shell.WriteLine(Loc.GetString("add-uplink-command-success-pda", ("code", string.Join("-", code).Replace("sharp", "#")))); + else if (result == AddUplinkResult.Implant) + shell.WriteLine(Loc.GetString("add-uplink-command-success-implant")); + else if (result == AddUplinkResult.Failure) shell.WriteLine(Loc.GetString("add-uplink-command-error-2")); } diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs index e8ed868dfb..1daee849dd 100644 --- a/Content.Server/Traitor/Uplink/UplinkSystem.cs +++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.PDA.Ringer; using Content.Server.Store.Systems; using Content.Server.StoreDiscount.Systems; using Content.Shared.FixedPoint; @@ -9,6 +10,7 @@ using Content.Shared.Mind; using Content.Shared.PDA; using Content.Shared.Store; using Content.Shared.Store.Components; +using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.Server.Traitor.Uplink; @@ -21,38 +23,110 @@ public sealed class UplinkSystem : EntitySystem [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!; [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly RingerSystem _ringer = default!; + public static readonly EntProtoId TraitorUplinkStore = "StorePresetRemoteUplink"; public static readonly ProtoId TelecrystalCurrencyPrototype = "Telecrystal"; private static readonly EntProtoId FallbackUplinkImplant = "UplinkImplant"; private static readonly ProtoId FallbackUplinkCatalog = "UplinkUplinkImplanter"; + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnRemoteStoreImplanted); + } + + private void OnRemoteStoreImplanted(Entity entity, ref ImplantImplantedEvent args) + { + if (_mind.GetMind(args.Implanted) is not { } mind ) + return; + + var storeEnumerator = EntityQueryEnumerator(); + while (storeEnumerator.MoveNext(out var uid, out _, out var store)) + { + if (store.AccountOwner != mind) + continue; + + entity.Comp.Store = uid; + return; + } + + // If we didn't have an uplink, make an empty one. + entity.Comp.Store = Spawn(TraitorUplinkStore, MapCoordinates.Nullspace); + SetUplink(args.Implanted, entity.Comp.Store.Value, 0, false); + Log.Error($"{ToPrettyString(args.Implanted)} did not have an uplink when they were implanted."); + } + /// /// Adds an uplink to the target /// /// The person who is getting the uplink /// The amount of currency on the uplink. If null, will just use the amount specified in the preset. + /// The code which was generated, if any. /// The entity that will actually have the uplink functionality. Defaults to the PDA if null. /// Marker that enables discounts for uplink items. - /// Whether or not the uplink was added successfully - public bool AddUplink( + /// Binds the uplink to the specific uplink entity. + /// Whether the uplink was added successfully to a PDA, implant or not at all. + public AddUplinkResult AddUplink( EntityUid user, FixedPoint2 balance, + out Note[]? code, EntityUid? uplinkEntity = null, - bool giveDiscounts = false) + bool giveDiscounts = false, + bool bindToPda = false) { - // Try to find target item if none passed + code = null; + var storeEntity = Spawn(TraitorUplinkStore, MapCoordinates.Nullspace); + if (TryAddEntityUplink(user, balance, out var generatedCode, uplinkEntity, storeEntity, giveDiscounts, bindToPda)) + { + code = generatedCode; + return AddUplinkResult.Pda; + } + + if (TryImplantUplink(user, storeEntity, balance, giveDiscounts)) + { + return AddUplinkResult.Implant; + } + + Del(storeEntity); + return AddUplinkResult.Failure; + } + + public bool TryAddEntityUplink( + EntityUid user, + FixedPoint2 balance, + out Note[]? code, + EntityUid? uplinkEntity, + EntityUid storeEntity, + bool giveDiscounts = false, + bool bindToPda = false) + { + code = null; uplinkEntity ??= FindUplinkTarget(user); if (uplinkEntity == null) - return ImplantUplink(user, balance, giveDiscounts); + return false; - EnsureComp(uplinkEntity.Value); + var ev = new GenerateUplinkCodeEvent(); + RaiseLocalEvent(storeEntity, ref ev); - SetUplink(user, uplinkEntity.Value, balance, giveDiscounts); + if (ev.Code == null) + { + QueueDel(storeEntity); + return false; + } - // TODO add BUI. Currently can't be done outside of yaml -_- - // ^ What does this even mean? + code = ev.Code; + + if (bindToPda) + { + var accessComp = EnsureComp(storeEntity); + _ringer.SetBoundUplinkEntity((storeEntity, accessComp), uplinkEntity.Value); + } + + SetUplink(user, storeEntity, balance, giveDiscounts); return true; } @@ -60,25 +134,25 @@ public sealed class UplinkSystem : EntitySystem /// /// Configure TC for the uplink /// - private void SetUplink(EntityUid user, EntityUid uplink, FixedPoint2 balance, bool giveDiscounts) + private void SetUplink(EntityUid user, EntityUid store, FixedPoint2 balance, bool giveDiscounts) { if (!_mind.TryGetMind(user, out var mind, out _)) return; - var store = EnsureComp(uplink); + var storeComp = EnsureComp(store); - store.AccountOwner = mind; + storeComp.AccountOwner = mind; - store.Balance.Clear(); + storeComp.Balance.Clear(); _store.TryAddCurrency(new Dictionary { { TelecrystalCurrencyPrototype, balance } }, - uplink, - store); + store, + storeComp); var uplinkInitializedEvent = new StoreInitializedEvent( TargetUser: mind, - Store: uplink, + Store: store, UseDiscounts: giveDiscounts, - Listings: _store.GetAvailableListings(mind, uplink, store) + Listings: _store.GetAvailableListings(mind, store, storeComp) .ToArray()); RaiseLocalEvent(ref uplinkInitializedEvent); } @@ -86,9 +160,9 @@ public sealed class UplinkSystem : EntitySystem /// /// Implant an uplink as a fallback measure if the traitor had no PDA /// - private bool ImplantUplink(EntityUid user, FixedPoint2 balance, bool giveDiscounts) + public bool TryImplantUplink(EntityUid user, EntityUid storeEntity, FixedPoint2 balance, bool giveDiscounts) { - if (!_proto.Resolve(FallbackUplinkCatalog, out var catalog)) + if (!_proto.Resolve(FallbackUplinkCatalog, out var catalog)) return false; if (!catalog.Cost.TryGetValue(TelecrystalCurrencyPrototype, out var cost)) @@ -99,15 +173,15 @@ public sealed class UplinkSystem : EntitySystem else balance = balance - cost; + SetUplink(user, storeEntity, balance, giveDiscounts); var implant = _subdermalImplant.AddImplant(user, FallbackUplinkImplant); - if (!HasComp(implant)) + if (!HasComp(implant)) { Log.Error($"Implant does not have the store component {implant}"); return false; } - SetUplink(user, implant.Value, balance, giveDiscounts); return true; } @@ -124,18 +198,25 @@ public sealed class UplinkSystem : EntitySystem { var pdaUid = containerSlot.ContainedEntity; - if (HasComp(pdaUid) && HasComp(pdaUid)) - return pdaUid; + if (HasComp(pdaUid) && HasComp(pdaUid)) + return pdaUid.Value; } } // Also check hands foreach (var item in _handsSystem.EnumerateHeld(user)) { - if (HasComp(item) && HasComp(item)) + if (HasComp(item) && HasComp(item)) return item; } return null; } } + +public enum AddUplinkResult +{ + Pda, + Implant, + Failure, +} diff --git a/Content.Server/Wagging/WaggingSystem.cs b/Content.Server/Wagging/WaggingSystem.cs index 6ece6f0d95..d369ea8689 100644 --- a/Content.Server/Wagging/WaggingSystem.cs +++ b/Content.Server/Wagging/WaggingSystem.cs @@ -35,7 +35,13 @@ public sealed class WaggingSystem : EntitySystem if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; - EnsureComp(args.CloneUid); + // Make sure to set the datafields before adding the component so that the correct action gets spawned on map init. + var cloneComp = Factory.GetComponent(); + cloneComp.Action = ent.Comp.Action; + cloneComp.Layer = ent.Comp.Layer; + cloneComp.Organ = ent.Comp.Organ; + cloneComp.Suffix = ent.Comp.Suffix; + AddComp(args.CloneUid, cloneComp, true); } private void OnWaggingMapInit(Entity ent, ref MapInitEvent args) diff --git a/Content.Server/Weather/WeatherSystem.cs b/Content.Server/Weather/WeatherSystem.cs index 58e1eb2b98..b3a0bea882 100644 --- a/Content.Server/Weather/WeatherSystem.cs +++ b/Content.Server/Weather/WeatherSystem.cs @@ -10,8 +10,6 @@ public sealed class WeatherSystem : SharedWeatherSystem public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnCompInit); SubscribeLocalEvent(OnCompShutdown); } diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index c9fd429c47..e4d7861029 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -28,7 +28,6 @@ public sealed class WiresSystem : SharedWiresSystem [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ConstructionSystem _construction = default!; @@ -448,7 +447,7 @@ public sealed class WiresSystem : SharedWiresSystem { if (TryComp(args.User, out ActorComponent? actor)) { - _uiSystem.OpenUi(uid, WiresUiKey.Key, actor.PlayerSession); + UI.OpenUi(uid, WiresUiKey.Key, actor.PlayerSession); args.Handled = true; } } @@ -459,7 +458,7 @@ public sealed class WiresSystem : SharedWiresSystem if (args.Open) return; - _uiSystem.CloseUi(ent.Owner, WiresUiKey.Key); + UI.CloseUi(ent.Owner, WiresUiKey.Key); } private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args) @@ -548,7 +547,7 @@ public sealed class WiresSystem : SharedWiresSystem statuses.Sort((a, b) => a.position.CompareTo(b.position)); - _uiSystem.SetUiState((uid, ui), WiresUiKey.Key, new WiresBoundUserInterfaceState( + UI.SetUiState((uid, ui), WiresUiKey.Key, new WiresBoundUserInterfaceState( clientList.ToArray(), statuses.Select(p => new StatusEntry(p.key, p.value)).ToArray(), Loc.GetString(wires.BoardName), @@ -556,11 +555,6 @@ public sealed class WiresSystem : SharedWiresSystem wires.WireSeed)); } - public void OpenUserInterface(EntityUid uid, ICommonSession player) - { - _uiSystem.OpenUi(uid, WiresUiKey.Key, player); - } - /// /// Tries to get a wire on this entity by its integer id. /// @@ -602,7 +596,7 @@ public sealed class WiresSystem : SharedWiresSystem if (!args.WiresAccessible) { - _uiSystem.CloseUi(uid, WiresUiKey.Key); + UI.CloseUi(uid, WiresUiKey.Key); } } diff --git a/Content.Server/Worldgen/Components/BiomeSelectionComponent.cs b/Content.Server/Worldgen/Components/BiomeSelectionComponent.cs deleted file mode 100644 index 08571cd588..0000000000 --- a/Content.Server/Worldgen/Components/BiomeSelectionComponent.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems.Biomes; -using Robust.Shared.Prototypes; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for selecting the biome(s) to be used during world generation. -/// -[RegisterComponent] -[Access(typeof(BiomeSelectionSystem))] -public sealed partial class BiomeSelectionComponent : Component -{ - /// - /// The list of biomes available to this selector. - /// - /// This is always sorted by priority after ComponentStartup. - [DataField(required: true)] - public List> Biomes = new(); -} - diff --git a/Content.Server/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs b/Content.Server/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs deleted file mode 100644 index 28724d20a4..0000000000 --- a/Content.Server/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems.Carvers; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Worldgen.Components.Carvers; - -/// -/// This is used for carving out empty space in the game world, providing byways through the debris field. -/// -[RegisterComponent] -[Access(typeof(NoiseRangeCarverSystem))] -public sealed partial class NoiseRangeCarverComponent : Component -{ - /// - /// The noise channel to use as a density controller. - /// - /// This noise channel should be mapped to exactly the range [0, 1] unless you want a lot of warnings in the log. - [DataField("noiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string NoiseChannel { get; private set; } = default!; - - /// - /// The index of ranges in which to cut debris generation. - /// - [DataField("ranges", required: true)] - public List Ranges { get; private set; } = default!; -} - diff --git a/Content.Server/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs b/Content.Server/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs deleted file mode 100644 index a1317ae2ed..0000000000 --- a/Content.Server/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Content.Server.Worldgen.Systems.Debris; -using Content.Shared.Maps; -using Robust.Shared.Prototypes; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for constructing asteroid debris. -/// -[RegisterComponent] -[Access(typeof(BlobFloorPlanBuilderSystem))] -public sealed partial class BlobFloorPlanBuilderComponent : Component -{ - /// - /// The probability that placing a floor tile will add up to three-four neighboring tiles as well. - /// - [DataField("blobDrawProb")] public float BlobDrawProb; - - /// - /// The maximum radius for the structure. - /// - [DataField("radius", required: true)] public float Radius; - - /// - /// The tiles to be used for the floor plan. - /// - [DataField(required: true)] - public List> FloorTileset { get; private set; } = default!; - - /// - /// The number of floor tiles to place when drawing the asteroid layout. - /// - [DataField("floorPlacements", required: true)] - public int FloorPlacements { get; private set; } -} - diff --git a/Content.Server/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs b/Content.Server/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs deleted file mode 100644 index ae61f0581e..0000000000 --- a/Content.Server/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems.Debris; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for controlling the debris feature placer. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed partial class DebrisFeaturePlacerControllerComponent : Component -{ - /// - /// Whether or not to clip debris that would spawn at a location that has a density of zero. - /// - [DataField("densityClip")] public bool DensityClip = true; - - /// - /// Whether or not entities are already spawned. - /// - public bool DoSpawns = true; - - [DataField("ownedDebris")] public Dictionary OwnedDebris = new(); - - /// - /// The chance spawning a piece of debris will just be cancelled randomly. - /// - [DataField("randomCancelChance")] public float RandomCancellationChance = 0.1f; - - /// - /// Radius in which there should be no objects for debris to spawn. - /// - [DataField("safetyZoneRadius")] public float SafetyZoneRadius = 16.0f; - - /// - /// The noise channel to use as a density controller. - /// - [DataField("densityNoiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string DensityNoiseChannel { get; private set; } = default!; -} - diff --git a/Content.Server/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs b/Content.Server/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs deleted file mode 100644 index af4ef7f1cf..0000000000 --- a/Content.Server/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems.Debris; -using Content.Server.Worldgen.Tools; -using Content.Shared.Storage; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for selecting debris with a probability determined by a noise channel. -/// Takes priority over SimpleDebrisSelectorComponent and should likely be used in combination. -/// -[RegisterComponent] -[Access(typeof(NoiseDrivenDebrisSelectorSystem))] -public sealed partial class NoiseDrivenDebrisSelectorComponent : Component -{ - private EntitySpawnCollectionCache? _cache; - - /// - /// The prototype-facing debris table entries. - /// - [DataField("debrisTable", required: true)] - private List _entries = default!; - - /// - /// The debris entity spawn collection. - /// - public EntitySpawnCollectionCache CachedDebrisTable - { - get - { - _cache ??= new EntitySpawnCollectionCache(_entries); - return _cache; - } - } - - /// - /// The noise channel to use as a density controller. - /// - /// This noise channel should be mapped to exactly the range [0, 1] unless you want a lot of warnings in the log. - [DataField("noiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string NoiseChannel { get; private set; } = default!; -} - diff --git a/Content.Server/Worldgen/Components/Debris/OwnedDebrisComponent.cs b/Content.Server/Worldgen/Components/Debris/OwnedDebrisComponent.cs deleted file mode 100644 index b211277997..0000000000 --- a/Content.Server/Worldgen/Components/Debris/OwnedDebrisComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Systems.Debris; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for attaching a piece of debris to it's owning controller. -/// Mostly just syncs deletion. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed partial class OwnedDebrisComponent : Component -{ - /// - /// The last location in the controller's internal structure for this debris. - /// - [DataField("lastKey")] public Vector2 LastKey; - - /// - /// The DebrisFeaturePlacerController-having entity that owns this. - /// - [DataField("owningController")] public EntityUid OwningController; -} - diff --git a/Content.Server/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs b/Content.Server/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs deleted file mode 100644 index 5db9bad925..0000000000 --- a/Content.Server/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Content.Server.Worldgen.Systems.Debris; -using Content.Server.Worldgen.Tools; -using Content.Shared.Storage; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for a very simple debris selection for simple biomes. Just uses a spawn table. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed partial class SimpleDebrisSelectorComponent : Component -{ - private EntitySpawnCollectionCache? _cache; - - /// - /// The prototype-facing debris table entries. - /// - [DataField("debrisTable", required: true)] - private List _entries = default!; - - /// - /// The debris entity spawn collection. - /// - public EntitySpawnCollectionCache CachedDebrisTable - { - get - { - _cache ??= new EntitySpawnCollectionCache(_entries); - return _cache; - } - } -} - diff --git a/Content.Server/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs b/Content.Server/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs deleted file mode 100644 index 4865773bf3..0000000000 --- a/Content.Server/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Linq; -using Content.Server.Worldgen.Systems.Debris; -using Content.Server.Worldgen.Tools; -using Content.Shared.Maps; -using Content.Shared.Storage; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for populating a grid with random entities automatically. -/// -[RegisterComponent] -[Access(typeof(SimpleFloorPlanPopulatorSystem))] -public sealed partial class SimpleFloorPlanPopulatorComponent : Component -{ - private Dictionary? _caches; - - /// - /// The prototype facing floor plan populator entries. - /// - [DataField("entries", required: true, - customTypeSerializer: typeof(PrototypeIdDictionarySerializer, ContentTileDefinition>))] - private Dictionary> _entries = default!; - - /// - /// The spawn collections used to place entities on different tile types. - /// - [ViewVariables] - public Dictionary Caches - { - get - { - if (_caches is null) - { - _caches = _entries - .Select(x => - new KeyValuePair(x.Key, - new EntitySpawnCollectionCache(x.Value))) - .ToDictionary(x => x.Key, x => x.Value); - } - - return _caches; - } - } -} - diff --git a/Content.Server/Worldgen/Components/LoadedChunkComponent.cs b/Content.Server/Worldgen/Components/LoadedChunkComponent.cs deleted file mode 100644 index d2743ad4ab..0000000000 --- a/Content.Server/Worldgen/Components/LoadedChunkComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for marking a chunk as loaded. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed partial class LoadedChunkComponent : Component -{ - /// - /// The current list of entities loading this chunk. - /// - [ViewVariables] public List? Loaders = null; -} - diff --git a/Content.Server/Worldgen/Components/LocalityLoaderComponent.cs b/Content.Server/Worldgen/Components/LocalityLoaderComponent.cs deleted file mode 100644 index 1d37ab34c9..0000000000 --- a/Content.Server/Worldgen/Components/LocalityLoaderComponent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for sending a signal to the entity it's on to load contents whenever a loader gets close enough. -/// Does not support unloading. -/// -[RegisterComponent] -[Access(typeof(LocalityLoaderSystem))] -public sealed partial class LocalityLoaderComponent : Component -{ - /// - /// The maximum distance an entity can be from the loader for it to not load. - /// Once a loader is closer than this, the event is fired and this component removed. - /// - [DataField("loadingDistance")] public int LoadingDistance = 32; -} - diff --git a/Content.Server/Worldgen/Components/NoiseIndexComponent.cs b/Content.Server/Worldgen/Components/NoiseIndexComponent.cs deleted file mode 100644 index 06d84d2f85..0000000000 --- a/Content.Server/Worldgen/Components/NoiseIndexComponent.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for containing configured noise generators. -/// -[RegisterComponent] -[Access(typeof(NoiseIndexSystem))] -public sealed partial class NoiseIndexComponent : Component -{ - /// - /// An index of generators, to avoid having to recreate them every time a noise channel is used. - /// Keyed by noise generator prototype ID. - /// - [Access(typeof(NoiseIndexSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.None)] - public Dictionary Generators { get; } = new(); -} - diff --git a/Content.Server/Worldgen/Components/WorldChunkComponent.cs b/Content.Server/Worldgen/Components/WorldChunkComponent.cs deleted file mode 100644 index 3a91c00756..0000000000 --- a/Content.Server/Worldgen/Components/WorldChunkComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for marking an entity as being a world chunk. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed partial class WorldChunkComponent : Component -{ - /// - /// The coordinates of the chunk, in chunk space. - /// - [DataField("coordinates")] public Vector2i Coordinates; - - /// - /// The map this chunk belongs to. - /// - [DataField("map")] public EntityUid Map; -} - diff --git a/Content.Server/Worldgen/Components/WorldControllerComponent.cs b/Content.Server/Worldgen/Components/WorldControllerComponent.cs deleted file mode 100644 index 63580e7541..0000000000 --- a/Content.Server/Worldgen/Components/WorldControllerComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Server.Worldgen.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for controlling overall world loading, containing an index of all chunks in the map. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed partial class WorldControllerComponent : Component -{ - /// - /// The prototype to use for chunks on this world map. - /// - [DataField("chunkProto", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ChunkProto = "WorldChunk"; - - /// - /// An index of chunks owned by the controller. - /// - [DataField("chunks")] public Dictionary Chunks = new(); -} - diff --git a/Content.Server/Worldgen/Components/WorldLoaderComponent.cs b/Content.Server/Worldgen/Components/WorldLoaderComponent.cs deleted file mode 100644 index e6bb7781e9..0000000000 --- a/Content.Server/Worldgen/Components/WorldLoaderComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for allowing some objects to load the game world. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed partial class WorldLoaderComponent : Component -{ - /// - /// The radius in which the loader loads the world. - /// - [ViewVariables(VVAccess.ReadWrite)] [DataField("radius")] - public int Radius = 128; -} - diff --git a/Content.Server/Worldgen/GridPointsNearEnumerator.cs b/Content.Server/Worldgen/GridPointsNearEnumerator.cs deleted file mode 100644 index 24b710626b..0000000000 --- a/Content.Server/Worldgen/GridPointsNearEnumerator.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; - -namespace Content.Server.Worldgen; - -/// -/// A struct enumerator of points on a grid within the given radius. -/// -public struct GridPointsNearEnumerator -{ - private readonly int _radius; - private readonly Vector2i _center; - private int _x; - private int _y; - - /// - /// Initializes a new enumerator with the given center and radius. - /// - public GridPointsNearEnumerator(Vector2i center, int radius) - { - _radius = radius; - _center = center; - _x = -_radius; - _y = -_radius; - } - - /// - /// Gets the next point in the enumeration. - /// - /// The computed point, if any - /// Success - [Pure] - public bool MoveNext([NotNullWhen(true)] out Vector2i? chunk) - { - while (!(_x * _x + _y * _y <= _radius * _radius)) - { - if (_y > _radius) - { - chunk = null; - return false; - } - - if (_x > _radius) - { - _x = -_radius; - _y++; - } - else - { - _x++; - } - } - - chunk = _center + new Vector2i(_x, _y); - _x++; - return true; - } -} - diff --git a/Content.Server/Worldgen/Prototypes/BiomePrototype.cs b/Content.Server/Worldgen/Prototypes/BiomePrototype.cs deleted file mode 100644 index 75e4670e88..0000000000 --- a/Content.Server/Worldgen/Prototypes/BiomePrototype.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Numerics; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; - -namespace Content.Server.Worldgen.Prototypes; - -/// -/// This is a prototype for biome selection, allowing the component list of a chunk to be amended based on the output -/// of noise channels at that location. -/// -[Prototype("spaceBiome")] -public sealed partial class BiomePrototype : IPrototype, IInheritingPrototype -{ - /// - [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] - public string[]? Parents { get; private set; } - - /// - [NeverPushInheritance] - [AbstractDataField] - public bool Abstract { get; private set; } - - /// - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// The valid ranges of noise values under which this biome can be picked. - /// - [DataField("noiseRanges", required: true)] - public Dictionary> NoiseRanges = default!; - - /// - /// Higher priority biomes get picked before lower priority ones. - /// - [DataField("priority", required: true)] - public int Priority { get; private set; } - - /// - /// The components that get added to the target map. - /// - [DataField("chunkComponents")] - [AlwaysPushInheritance] - public ComponentRegistry ChunkComponents = new(); - - //TODO: Get someone to make this a method on componentregistry that does it Correctly. - /// - /// Applies the worldgen config to the given target (presumably a map.) - /// - public void Apply(EntityUid target, ISerializationManager serialization, IEntityManager entityManager) - { - // Add all components required by the prototype. Engine update for this whenst. - foreach (var data in ChunkComponents.Values) - { - var comp = (Component) serialization.CreateCopy(data.Component, notNullableOverride: true); - entityManager.AddComponent(target, comp); - } - } -} - diff --git a/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs b/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs deleted file mode 100644 index 41d89cbaa7..0000000000 --- a/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System.Numerics; -using Robust.Shared.Noise; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; - -namespace Content.Server.Worldgen.Prototypes; - -/// -/// This is a config for noise channels, used by worldgen. -/// -[Virtual] -public class NoiseChannelConfig -{ - /// - /// The noise type used by the noise generator. - /// - [DataField("noiseType")] - public FastNoiseLite.NoiseType NoiseType { get; private set; } = FastNoiseLite.NoiseType.Cellular; - - /// - /// The fractal type used by the noise generator. - /// - [DataField("fractalType")] - public FastNoiseLite.FractalType FractalType { get; private set; } = FastNoiseLite.FractalType.FBm; - - /// - /// Multiplied by pi in code when used. - /// - [DataField("fractalLacunarityByPi")] - public float FractalLacunarityByPi { get; private set; } = 2.0f / 3.0f; - - /// - /// Ranges of values that get clamped down to the "clipped" value. - /// - [DataField("clippingRanges")] - public List ClippingRanges { get; private set; } = new(); - - /// - /// The value clipped chunks are set to. - /// - [DataField("clippedValue")] - public float ClippedValue { get; private set; } - - /// - /// A value the output is multiplied by. - /// - [DataField("outputMultiplier")] - public float OutputMultiplier { get; private set; } = 1.0f; - - /// - /// A value the input is multiplied by. - /// - [DataField("inputMultiplier")] - public float InputMultiplier { get; private set; } = 1.0f; - - /// - /// Remaps the output of the noise function from the range (-1, 1) to (0, 1). This is done before all other output - /// transformations. - /// - [DataField("remapTo0Through1")] - public bool RemapTo0Through1 { get; private set; } - - /// - /// For when the transformation you need is too complex to describe in YAML. - /// - [DataField("noisePostProcess")] - public NoisePostProcess? NoisePostProcess { get; private set; } - - /// - /// For when you need a complex transformation of the input coordinates. - /// - [DataField("noiseCoordinateProcess")] - public NoiseCoordinateProcess? NoiseCoordinateProcess { get; private set; } - - /// - /// The "center" of the range of values. Or the minimum if mapped 0 through 1. - /// - [DataField("minimum")] - public float Minimum { get; private set; } -} - -[Prototype] -public sealed partial class NoiseChannelPrototype : NoiseChannelConfig, IPrototype, IInheritingPrototype -{ - /// - [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] - public string[]? Parents { get; private set; } - - /// - [NeverPushInheritance] - [AbstractDataField] - public bool Abstract { get; private set; } - - /// - [IdDataField] - public string ID { get; private set; } = default!; -} - -/// -/// A wrapper around FastNoise's noise generation, using noise channel configs. -/// -public struct NoiseGenerator -{ - private readonly NoiseChannelConfig _config; - private readonly FastNoiseLite _noise; - - /// - /// Produces a new noise generator from the given channel config and rng seed. - /// - public NoiseGenerator(NoiseChannelConfig config, int seed) - { - _config = config; - _noise = new FastNoiseLite(); - _noise.SetSeed(seed); - _noise.SetNoiseType(_config.NoiseType); - _noise.SetFractalType(_config.FractalType); - _noise.SetFractalLacunarity(_config.FractalLacunarityByPi * MathF.PI); - } - - /// - /// Evaluates the noise generator at the provided coordinates. - /// - /// Coordinates to use as input - /// Computed noise value - public float Evaluate(Vector2 coords) - { - var finCoords = coords * _config.InputMultiplier; - - if (_config.NoiseCoordinateProcess is not null) - finCoords = _config.NoiseCoordinateProcess.Process(finCoords); - - var value = _noise.GetNoise(finCoords.X, finCoords.Y); - - if (_config.RemapTo0Through1) - value = (value + 1.0f) / 2.0f; - - foreach (var range in _config.ClippingRanges) - { - if (range.X < value && value < range.Y) - { - value = _config.ClippedValue; - break; - } - } - - if (_config.NoisePostProcess is not null) - value = _config.NoisePostProcess.Process(value); - value *= _config.OutputMultiplier; - return value + _config.Minimum; - } -} - -/// -/// A processing class that adjusts the input coordinate space to a noise channel. -/// -[ImplicitDataDefinitionForInheritors] -public abstract partial class NoiseCoordinateProcess -{ - public abstract Vector2 Process(Vector2 inp); -} - -/// -/// A processing class that adjusts the final result of the noise channel. -/// -[ImplicitDataDefinitionForInheritors] -public abstract partial class NoisePostProcess -{ - public abstract float Process(float inp); -} - diff --git a/Content.Server/Worldgen/Prototypes/WorldgenConfigPrototype.cs b/Content.Server/Worldgen/Prototypes/WorldgenConfigPrototype.cs deleted file mode 100644 index c9107fa2bd..0000000000 --- a/Content.Server/Worldgen/Prototypes/WorldgenConfigPrototype.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; - -namespace Content.Server.Worldgen.Prototypes; - -/// -/// This is a prototype for controlling overall world generation. -/// The components included are applied to the map that world generation is configured on. -/// -[Prototype] -public sealed partial class WorldgenConfigPrototype : IPrototype -{ - /// - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// The components that get added to the target map. - /// - [DataField("components", required: true)] - public ComponentRegistry Components { get; private set; } = default!; - - //TODO: Get someone to make this a method on componentregistry that does it Correctly. - /// - /// Applies the worldgen config to the given target (presumably a map.) - /// - public void Apply(EntityUid target, ISerializationManager serialization, IEntityManager entityManager) - { - // Add all components required by the prototype. Engine update for this whenst. - foreach (var data in Components.Values) - { - var comp = (Component) serialization.CreateCopy(data.Component, notNullableOverride: true); - entityManager.AddComponent(target, comp); - } - } -} - diff --git a/Content.Server/Worldgen/Systems/BaseWorldSystem.cs b/Content.Server/Worldgen/Systems/BaseWorldSystem.cs deleted file mode 100644 index 7a9e74375c..0000000000 --- a/Content.Server/Worldgen/Systems/BaseWorldSystem.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Components; -using JetBrains.Annotations; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This provides some additional functions for world generation systems. -/// Exists primarily for convenience and to avoid code duplication. -/// -[PublicAPI] -public abstract class BaseWorldSystem : EntitySystem -{ - [Dependency] private readonly WorldControllerSystem _worldController = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - - /// - /// Gets a chunk's coordinates in chunk space as an integer value. - /// - /// - /// - /// Chunk space coordinates - [Pure] - public Vector2i GetChunkCoords(EntityUid ent, TransformComponent? xform = null) - { - if (!Resolve(ent, ref xform)) - throw new Exception("Failed to resolve transform, somehow."); - - return WorldGen.WorldToChunkCoords(_transformSystem.GetWorldPosition(xform)).Floored(); - } - - /// - /// Gets a chunk's coordinates in chunk space as a floating point value. - /// - /// - /// - /// Chunk space coordinates - [Pure] - public Vector2 GetFloatingChunkCoords(EntityUid ent, TransformComponent? xform = null) - { - if (!Resolve(ent, ref xform)) - throw new Exception("Failed to resolve transform, somehow."); - - return WorldGen.WorldToChunkCoords(_transformSystem.GetWorldPosition(xform)); - } - - /// - /// Attempts to get a chunk, creating it if it doesn't exist. - /// - /// Chunk coordinates to get the chunk entity for. - /// Map the chunk is in. - /// The controller this chunk belongs to. - /// A chunk, if available. - [Pure] - public EntityUid? GetOrCreateChunk(Vector2i chunk, EntityUid map, WorldControllerComponent? controller = null) - { - return _worldController.GetOrCreateChunk(chunk, map, controller); - } -} - diff --git a/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs b/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs deleted file mode 100644 index 1827f6deed..0000000000 --- a/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Linq; -using Content.Server.Worldgen.Components; -using Content.Server.Worldgen.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; - -namespace Content.Server.Worldgen.Systems.Biomes; - -/// -/// This handles biome selection, evaluating which biome to apply to a chunk based on noise channels. -/// -public sealed class BiomeSelectionSystem : BaseWorldSystem -{ - [Dependency] private readonly NoiseIndexSystem _noiseIdx = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly ISerializationManager _ser = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnBiomeSelectionStartup); - SubscribeLocalEvent(OnWorldChunkAdded); - } - - private void OnWorldChunkAdded(EntityUid uid, BiomeSelectionComponent component, ref WorldChunkAddedEvent args) - { - var coords = args.Coords; - foreach (var biomeId in component.Biomes) - { - var biome = _proto.Index(biomeId); - if (!CheckBiomeValidity(args.Chunk, biome, coords)) - continue; - - biome.Apply(args.Chunk, _ser, EntityManager); - return; - } - - Log.Error($"Biome selection ran out of biomes to select? See biomes list: {component.Biomes}"); - } - - private void OnBiomeSelectionStartup(EntityUid uid, BiomeSelectionComponent component, ComponentStartup args) - { - // surely this can't be THAAAAAAAAAAAAAAAT bad right???? - var sorted = component.Biomes - .Select(x => (Id: x, _proto.Index(x).Priority)) - .OrderByDescending(x => x.Priority) - .Select(x => x.Id) - .ToList(); - - component.Biomes = sorted; // my hopes and dreams rely on this being pre-sorted by priority. - } - - private bool CheckBiomeValidity(EntityUid chunk, BiomePrototype biome, Vector2i coords) - { - foreach (var (noise, ranges) in biome.NoiseRanges) - { - var value = _noiseIdx.Evaluate(chunk, noise, coords); - var anyValid = false; - foreach (var range in ranges) - { - if (range.X < value && value < range.Y) - { - anyValid = true; - break; - } - } - - if (!anyValid) - return false; - } - - return true; - } -} - diff --git a/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs b/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs deleted file mode 100644 index 1207d6f157..0000000000 --- a/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Content.Server.Worldgen.Components.Carvers; -using Content.Server.Worldgen.Systems.Debris; - -namespace Content.Server.Worldgen.Systems.Carvers; - -/// -/// This handles carving out holes in world generation according to a noise channel. -/// -public sealed class NoiseRangeCarverSystem : EntitySystem -{ - [Dependency] private readonly NoiseIndexSystem _index = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnPrePlaceDebris); - } - - private void OnPrePlaceDebris(EntityUid uid, NoiseRangeCarverComponent component, - ref PrePlaceDebrisFeatureEvent args) - { - var coords = WorldGen.WorldToChunkCoords(_transform.ToMapCoordinates(args.Coords).Position); - var val = _index.Evaluate(uid, component.NoiseChannel, coords); - - foreach (var (low, high) in component.Ranges) - { - if (low > val || high < val) - continue; - - args.Handled = true; - return; - } - } -} - diff --git a/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs b/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs deleted file mode 100644 index ba0a3a7132..0000000000 --- a/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Linq; -using Content.Server.Worldgen.Components.Debris; -using Content.Shared.Maps; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Systems.Debris; - -/// -/// This handles building the floor plans for "blobby" debris. -/// -public sealed class BlobFloorPlanBuilderSystem : BaseWorldSystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; - [Dependency] private readonly TileSystem _tiles = default!; - [Dependency] private readonly SharedMapSystem _map = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnBlobFloorPlanBuilderStartup); - } - - private void OnBlobFloorPlanBuilderStartup(EntityUid uid, BlobFloorPlanBuilderComponent component, - ComponentStartup args) - { - PlaceFloorplanTiles(uid, component, Comp(uid)); - } - - private void PlaceFloorplanTiles(EntityUid gridUid, BlobFloorPlanBuilderComponent comp, MapGridComponent grid) - { - // NO MORE THAN TWO ALLOCATIONS THANK YOU VERY MUCH. - // TODO: Just put these on a field instead then? - // Also the end of the method has a big LINQ which is gonna blow this out the water. - var spawnPoints = new HashSet(comp.FloorPlacements * 6); - var taken = new Dictionary(comp.FloorPlacements * 5); - - void PlaceTile(Vector2i point) - { - // Assume we already know that the spawn point is safe. - spawnPoints.Remove(point); - var north = point.Offset(Direction.North); - var south = point.Offset(Direction.South); - var east = point.Offset(Direction.East); - var west = point.Offset(Direction.West); - var radsq = Math.Pow(comp.Radius, - 2); // I'd put this outside but i'm not 100% certain caching it between calls is a gain. - - // The math done is essentially a fancy way of comparing the distance from 0,0 to the radius, - // and skipping the sqrt normally needed for dist. - if (!taken.ContainsKey(north) && Math.Pow(north.X, 2) + Math.Pow(north.Y, 2) <= radsq) - spawnPoints.Add(north); - if (!taken.ContainsKey(south) && Math.Pow(south.X, 2) + Math.Pow(south.Y, 2) <= radsq) - spawnPoints.Add(south); - if (!taken.ContainsKey(east) && Math.Pow(east.X, 2) + Math.Pow(east.Y, 2) <= radsq) - spawnPoints.Add(east); - if (!taken.ContainsKey(west) && Math.Pow(west.X, 2) + Math.Pow(west.Y, 2) <= radsq) - spawnPoints.Add(west); - - var tileDef = _tileDefinition[_random.Pick(comp.FloorTileset)]; - taken.Add(point, new Tile(tileDef.TileId, 0, _tiles.PickVariant((ContentTileDefinition) tileDef))); - } - - PlaceTile(Vector2i.Zero); - - for (var i = 0; i < comp.FloorPlacements; i++) - { - var point = _random.Pick(spawnPoints); - PlaceTile(point); - - if (comp.BlobDrawProb > 0.0f) - { - if (!taken.ContainsKey(point.Offset(Direction.North)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.North)); - if (!taken.ContainsKey(point.Offset(Direction.South)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.South)); - if (!taken.ContainsKey(point.Offset(Direction.East)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.East)); - if (!taken.ContainsKey(point.Offset(Direction.West)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.West)); - } - } - - _map.SetTiles(gridUid, grid, taken.Select(x => (x.Key, x.Value)).ToList()); - } -} - diff --git a/Content.Server/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs b/Content.Server/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs deleted file mode 100644 index b609f7e1f7..0000000000 --- a/Content.Server/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs +++ /dev/null @@ -1,278 +0,0 @@ -using System.Linq; -using System.Numerics; -using Content.Server.Worldgen.Components; -using Content.Server.Worldgen.Components.Debris; -using Content.Server.Worldgen.Tools; -using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; -using Robust.Shared.Utility; - -namespace Content.Server.Worldgen.Systems.Debris; - -/// -/// This handles placing debris within the world evenly with rng, primarily for structures like asteroid fields. -/// -public sealed class DebrisFeaturePlacerSystem : BaseWorldSystem -{ - [Dependency] private readonly NoiseIndexSystem _noiseIndex = default!; - [Dependency] private readonly PoissonDiskSampler _sampler = default!; - [Dependency] private readonly TransformSystem _xformSys = default!; - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - private ISawmill _sawmill = default!; - - private List> _mapGrids = new(); - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world.debris.feature_placer"); - SubscribeLocalEvent(OnChunkLoaded); - SubscribeLocalEvent(OnChunkUnloaded); - SubscribeLocalEvent(OnDebrisShutdown); - SubscribeLocalEvent(OnDebrisMove); - SubscribeLocalEvent( - OnTryGetPlacableDebrisEvent); - } - - /// - /// Handles debris moving, and making sure it stays parented to a chunk for loading purposes. - /// - private void OnDebrisMove(EntityUid uid, OwnedDebrisComponent component, ref MoveEvent args) - { - if (!HasComp(component.OwningController)) - return; // Redundant logic, prolly needs it's own handler for your custom system. - - var placer = Comp(component.OwningController); - var xform = args.Component; - var ownerXform = Transform(component.OwningController); - if (xform.MapUid is null || ownerXform.MapUid is null) - return; // not our problem - - if (xform.MapUid != ownerXform.MapUid) - { - _sawmill.Error($"Somehow debris {uid} left it's expected map! Unparenting it to avoid issues."); - RemCompDeferred(uid); - placer.OwnedDebris.Remove(component.LastKey); - return; - } - - placer.OwnedDebris.Remove(component.LastKey); - var newChunk = GetOrCreateChunk(GetChunkCoords(uid), xform.MapUid!.Value); - if (newChunk is null || !TryComp(newChunk, out var newPlacer)) - { - // Whelp. - RemCompDeferred(uid); - return; - } - - newPlacer.OwnedDebris[_xformSys.GetWorldPosition(xform)] = uid; // Change our owner. - component.OwningController = newChunk.Value; - } - - /// - /// Handles debris shutdown/detach. - /// - private void OnDebrisShutdown(EntityUid uid, OwnedDebrisComponent component, ComponentShutdown args) - { - if (!TryComp(component.OwningController, out var placer)) - return; - - placer.OwnedDebris[component.LastKey] = null; - if (Terminating(uid)) - placer.OwnedDebris.Remove(component.LastKey); - } - - /// - /// Queues all debris owned by the placer for garbage collection. - /// - private void OnChunkUnloaded(EntityUid uid, DebrisFeaturePlacerControllerComponent component, - ref WorldChunkUnloadedEvent args) - { - component.DoSpawns = true; - } - - /// - /// Handles providing a debris type to place for SimpleDebrisSelectorComponent. - /// This randomly picks a debris type from the EntitySpawnCollectionCache. - /// - private void OnTryGetPlacableDebrisEvent(EntityUid uid, SimpleDebrisSelectorComponent component, - ref TryGetPlaceableDebrisFeatureEvent args) - { - if (args.DebrisProto is not null) - return; - - var l = new List(1); - component.CachedDebrisTable.GetSpawns(_random, ref l); - - switch (l.Count) - { - case 0: - return; - case > 1: - _sawmill.Warning($"Got more than one possible debris type from {uid}. List: {string.Join(", ", l)}"); - break; - } - - args.DebrisProto = l[0]; - } - - /// - /// Handles loading in debris. This does the following: - /// - Checks if the debris is currently supposed to do spawns, if it isn't, aborts immediately. - /// - Evaluates the density value to be used for placement, if it's zero, aborts. - /// - Generates the points to generate debris at, if and only if they've not been selected already by a prior load. - /// - Does the following in a loop over all generated points: - /// - Raises an event to check if something else wants to intercept debris placement, if the event is handled, - /// continues to the next point without generating anything. - /// - Raises an event to get the debris type that should be used for generation. - /// - Spawns the given debris at the point, adding it to the placer's index. - /// - private void OnChunkLoaded(EntityUid uid, DebrisFeaturePlacerControllerComponent component, - ref WorldChunkLoadedEvent args) - { - if (component.DoSpawns == false) - return; - - component.DoSpawns = false; // Don't repeat yourself if this crashes. - - if (!TryComp(args.Chunk, out var chunk)) - return; - - var chunkMap = chunk.Map; - - if (!TryComp(chunkMap, out var map)) - return; - - var densityChannel = component.DensityNoiseChannel; - var density = _noiseIndex.Evaluate(uid, densityChannel, chunk.Coordinates + new Vector2(0.5f, 0.5f)); - if (density == 0) - return; - - List? points = null; - - // If we've been loaded before, reuse the same coordinates. - if (component.OwnedDebris.Count != 0) - { - //TODO: Remove LINQ. - points = component.OwnedDebris - .Where(x => !Deleted(x.Value)) - .Select(static x => x.Key) - .ToList(); - } - - points ??= GeneratePointsInChunk(args.Chunk, density, chunk.Coordinates, chunkMap); - - var mapId = map.MapId; - - var safetyBounds = Box2.UnitCentered.Enlarged(component.SafetyZoneRadius); - var failures = 0; // Avoid severe log spam. - foreach (var point in points) - { - if (component.OwnedDebris.TryGetValue(point, out var existing)) - { - DebugTools.Assert(Exists(existing)); - continue; - } - - var pointDensity = _noiseIndex.Evaluate(uid, densityChannel, WorldGen.WorldToChunkCoords(point)); - if (pointDensity == 0 && component.DensityClip || _random.Prob(component.RandomCancellationChance)) - continue; - - if (HasCollisions(mapId, safetyBounds.Translated(point))) - continue; - - var coords = new EntityCoordinates(chunkMap, point); - - var preEv = new PrePlaceDebrisFeatureEvent(coords, args.Chunk); - RaiseLocalEvent(uid, ref preEv); - if (uid != args.Chunk) - RaiseLocalEvent(args.Chunk, ref preEv); - - if (preEv.Handled) - continue; - - var debrisFeatureEv = new TryGetPlaceableDebrisFeatureEvent(coords, args.Chunk); - RaiseLocalEvent(uid, ref debrisFeatureEv); - - if (debrisFeatureEv.DebrisProto == null) - { - // Try on the chunk...? - if (uid != args.Chunk) - RaiseLocalEvent(args.Chunk, ref debrisFeatureEv); - - if (debrisFeatureEv.DebrisProto == null) - { - // Nope. - failures++; - continue; - } - } - - var ent = Spawn(debrisFeatureEv.DebrisProto, coords); - component.OwnedDebris.Add(point, ent); - - var owned = EnsureComp(ent); - owned.OwningController = uid; - owned.LastKey = point; - } - - if (failures > 0) - _sawmill.Error($"Failed to place {failures} debris at chunk {args.Chunk}"); - } - - /// - /// Checks to see if the potential spawn point is clear - /// - /// - /// - /// - private bool HasCollisions(MapId mapId, Box2 point) - { - _mapGrids.Clear(); - _mapManager.FindGridsIntersecting(mapId, point, ref _mapGrids); - return _mapGrids.Count > 0; - } - - /// - /// Generates the points to put into a chunk using a poisson disk sampler. - /// - private List GeneratePointsInChunk(EntityUid chunk, float density, Vector2 coords, EntityUid map) - { - var offs = (int) ((WorldGen.ChunkSize - WorldGen.ChunkSize / 8.0f) / 2.0f); - var topLeft = new Vector2(-offs, -offs); - var lowerRight = new Vector2(offs, offs); - var enumerator = _sampler.SampleRectangle(topLeft, lowerRight, density); - var debrisPoints = new List(); - - var realCenter = WorldGen.ChunkToWorldCoordsCentered(coords.Floored()); - - while (enumerator.MoveNext(out var debrisPoint)) - { - debrisPoints.Add(realCenter + debrisPoint.Value); - } - - return debrisPoints; - } -} - -/// -/// Fired directed on the debris feature placer controller and the chunk, ahead of placing a debris piece. -/// -[ByRefEvent] -[PublicAPI] -public record struct PrePlaceDebrisFeatureEvent(EntityCoordinates Coords, EntityUid Chunk, bool Handled = false); - -/// -/// Fired directed on the debris feature placer controller and the chunk, to select which debris piece to place. -/// -[ByRefEvent] -[PublicAPI] -public record struct TryGetPlaceableDebrisFeatureEvent(EntityCoordinates Coords, EntityUid Chunk, - string? DebrisProto = null); - diff --git a/Content.Server/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs b/Content.Server/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs deleted file mode 100644 index a02ff81362..0000000000 --- a/Content.Server/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Content.Server.Worldgen.Components.Debris; -using Robust.Server.GameObjects; -using Robust.Shared.Physics; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Systems.Debris; - -/// -/// This handles selecting debris with probability decided by a noise channel. -/// -public sealed class NoiseDrivenDebrisSelectorSystem : BaseWorldSystem -{ - [Dependency] private readonly NoiseIndexSystem _index = default!; - [Dependency] private readonly TransformSystem _xformSys = default!; - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - private ISawmill _sawmill = default!; - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world.debris.noise_debris_selector"); - // Event is forcibly ordered to always be handled after the simple selector. - SubscribeLocalEvent(OnSelectDebrisKind, - after: new[] {typeof(DebrisFeaturePlacerSystem)}); - } - - private void OnSelectDebrisKind(EntityUid uid, NoiseDrivenDebrisSelectorComponent component, - ref TryGetPlaceableDebrisFeatureEvent args) - { - var coords = WorldGen.WorldToChunkCoords(_xformSys.ToMapCoordinates(args.Coords).Position); - var prob = _index.Evaluate(uid, component.NoiseChannel, coords); - - if (prob is < 0 or > 1) - { - _sawmill.Error( - $"Sampled a probability of {prob}, which is outside the [0, 1] range, at {coords} aka {args.Coords}."); - return; - } - - if (!_random.Prob(prob)) - return; - - var l = new List(1); - component.CachedDebrisTable.GetSpawns(_random, ref l); - - switch (l.Count) - { - case 0: - return; - case > 1: - _sawmill.Warning($"Got more than one possible debris type from {uid}. List: {string.Join(", ", l)}"); - break; - } - - args.DebrisProto = l[0]; - } -} - diff --git a/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs b/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs deleted file mode 100644 index dcb7b7fc8f..0000000000 --- a/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Content.Server.Worldgen.Components.Debris; -using Content.Shared.Maps; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Systems.Debris; - -/// -/// This handles populating simple structures, simply using a loot table for each tile. -/// -public sealed class SimpleFloorPlanPopulatorSystem : BaseWorldSystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedMapSystem _map = default!; - [Dependency] private readonly TurfSystem _turf = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnFloorPlanBuilt); - } - - private void OnFloorPlanBuilt(EntityUid uid, SimpleFloorPlanPopulatorComponent component, - LocalStructureLoadedEvent args) - { - var placeables = new List(4); - var grid = Comp(uid); - var enumerator = _map.GetAllTilesEnumerator(uid, grid); - while (enumerator.MoveNext(out var tile)) - { - var coords = _map.GridTileToLocal(uid, grid, tile.Value.GridIndices); - var selector = _turf.GetContentTileDefinition(tile.Value).ID; - if (!component.Caches.TryGetValue(selector, out var cache)) - continue; - - placeables.Clear(); - cache.GetSpawns(_random, ref placeables); - - foreach (var proto in placeables) - { - if (proto is null) - continue; - - Spawn(proto, coords); - } - } - } -} - diff --git a/Content.Server/Worldgen/Systems/LocalityLoaderSystem.cs b/Content.Server/Worldgen/Systems/LocalityLoaderSystem.cs deleted file mode 100644 index fb02e8aa0b..0000000000 --- a/Content.Server/Worldgen/Systems/LocalityLoaderSystem.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Content.Server.Worldgen.Components; -using Robust.Server.GameObjects; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This handles loading in objects based on distance from player, using some metadata on chunks. -/// -public sealed class LocalityLoaderSystem : BaseWorldSystem -{ - [Dependency] private readonly TransformSystem _xformSys = default!; - - /// - public override void Update(float frameTime) - { - var e = EntityQueryEnumerator(); - var loadedQuery = GetEntityQuery(); - var xformQuery = GetEntityQuery(); - var controllerQuery = GetEntityQuery(); - - while (e.MoveNext(out var uid, out var loadable, out var xform)) - { - if (!controllerQuery.TryGetComponent(xform.MapUid, out var controller)) - { - RaiseLocalEvent(uid, new LocalStructureLoadedEvent()); - RemCompDeferred(uid); - continue; - } - - var coords = GetChunkCoords(uid, xform); - var done = false; - for (var i = -1; i < 2 && !done; i++) - { - for (var j = -1; j < 2 && !done; j++) - { - var chunk = GetOrCreateChunk(coords + (i, j), xform.MapUid!.Value, controller); - if (!loadedQuery.TryGetComponent(chunk, out var loaded) || loaded.Loaders is null) - continue; - - foreach (var loader in loaded.Loaders) - { - if (!xformQuery.TryGetComponent(loader, out var loaderXform)) - continue; - - if ((_xformSys.GetWorldPosition(loaderXform) - _xformSys.GetWorldPosition(xform)).Length() > loadable.LoadingDistance) - continue; - - RaiseLocalEvent(uid, new LocalStructureLoadedEvent()); - RemCompDeferred(uid); - done = true; - break; - } - } - } - } - } -} - -/// -/// A directed fired on a loadable entity when a local loader enters it's vicinity. -/// -public record struct LocalStructureLoadedEvent; - diff --git a/Content.Server/Worldgen/Systems/NoiseIndexSystem.cs b/Content.Server/Worldgen/Systems/NoiseIndexSystem.cs deleted file mode 100644 index 5a7e02c803..0000000000 --- a/Content.Server/Worldgen/Systems/NoiseIndexSystem.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Components; -using Content.Server.Worldgen.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This handles the noise index. -/// -public sealed class NoiseIndexSystem : EntitySystem -{ - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - /// - /// Gets a particular noise channel from the index on the given entity. - /// - /// The holder of the index - /// The channel prototype ID - /// An initialized noise generator - public NoiseGenerator Get(EntityUid holder, string protoId) - { - var idx = EnsureComp(holder); - if (idx.Generators.TryGetValue(protoId, out var generator)) - return generator; - var proto = _prototype.Index(protoId); - var gen = new NoiseGenerator(proto, _random.Next()); - idx.Generators[protoId] = gen; - return gen; - } - - /// - /// Attempts to evaluate the given noise channel using the generator on the given entity. - /// - /// The holder of the index - /// The channel prototype ID - /// The coordinates to evaluate at - /// The result of evaluation - public float Evaluate(EntityUid holder, string protoId, Vector2 coords) - { - var gen = Get(holder, protoId); - return gen.Evaluate(coords); - } -} - diff --git a/Content.Server/Worldgen/Systems/WorldControllerSystem.cs b/Content.Server/Worldgen/Systems/WorldControllerSystem.cs deleted file mode 100644 index 19c777e1ad..0000000000 --- a/Content.Server/Worldgen/Systems/WorldControllerSystem.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System.Linq; -using Content.Server.Worldgen.Components; -using Content.Shared.Ghost; -using Content.Shared.Mind.Components; -using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Timing; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This handles putting together chunk entities and notifying them about important changes. -/// -public sealed class WorldControllerSystem : EntitySystem -{ - [Dependency] private readonly TransformSystem _xformSys = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - - private const int PlayerLoadRadius = 2; - - private ISawmill _sawmill = default!; - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world"); - SubscribeLocalEvent(OnChunkLoadedCore); - SubscribeLocalEvent(OnChunkUnloadedCore); - SubscribeLocalEvent(OnChunkShutdown); - } - - /// - /// Handles deleting chunks properly. - /// - private void OnChunkShutdown(EntityUid uid, WorldChunkComponent component, ComponentShutdown args) - { - if (!TryComp(component.Map, out var controller)) - return; - - if (HasComp(uid)) - { - var ev = new WorldChunkUnloadedEvent(uid, component.Coordinates); - RaiseLocalEvent(component.Map, ref ev); - RaiseLocalEvent(uid, ref ev, broadcast: true); - } - - controller.Chunks.Remove(component.Coordinates); - } - - /// - /// Handles the inner logic of loading a chunk, i.e. events. - /// - private void OnChunkLoadedCore(EntityUid uid, LoadedChunkComponent component, ComponentStartup args) - { - if (!TryComp(uid, out var chunk)) - return; - - var ev = new WorldChunkLoadedEvent(uid, chunk.Coordinates); - RaiseLocalEvent(chunk.Map, ref ev); - RaiseLocalEvent(uid, ref ev, broadcast: true); - //_sawmill.Debug($"Loaded chunk {ToPrettyString(uid)} at {chunk.Coordinates}"); - } - - /// - /// Handles the inner logic of unloading a chunk, i.e. events. - /// - private void OnChunkUnloadedCore(EntityUid uid, LoadedChunkComponent component, ComponentShutdown args) - { - if (!TryComp(uid, out var chunk)) - return; - - if (Terminating(uid)) - return; // SAFETY: This is in case a loaded chunk gets deleted, to avoid double unload. - - var ev = new WorldChunkUnloadedEvent(uid, chunk.Coordinates); - RaiseLocalEvent(chunk.Map, ref ev); - RaiseLocalEvent(uid, ref ev); - //_sawmill.Debug($"Unloaded chunk {ToPrettyString(uid)} at {coords}"); - } - - /// - public override void Update(float frameTime) - { - //there was a to-do here about every frame alloc but it turns out it's a nothing burger here. - var chunksToLoad = new Dictionary>>(); - - var controllerEnum = EntityQueryEnumerator(); - while (controllerEnum.MoveNext(out var uid, out _)) - { - chunksToLoad[uid] = new Dictionary>(); - } - - if (chunksToLoad.Count == 0) - return; // Just bail early. - - var loaderEnum = EntityQueryEnumerator(); - - while (loaderEnum.MoveNext(out var uid, out var worldLoader, out var xform)) - { - var mapOrNull = xform.MapUid; - if (mapOrNull is null) - continue; - var map = mapOrNull.Value; - if (!chunksToLoad.ContainsKey(map)) - continue; - - var wc = _xformSys.GetWorldPosition(xform); - var coords = WorldGen.WorldToChunkCoords(wc); - var chunks = new GridPointsNearEnumerator(coords.Floored(), - (int) Math.Ceiling(worldLoader.Radius / (float) WorldGen.ChunkSize) + 1); - - var set = chunksToLoad[map]; - - while (chunks.MoveNext(out var chunk)) - { - if (!set.TryGetValue(chunk.Value, out _)) - set[chunk.Value] = new List(4); - set[chunk.Value].Add(uid); - } - } - - var mindEnum = EntityQueryEnumerator(); - var ghostQuery = GetEntityQuery(); - - // Mindful entities get special privilege as they're always a player and we don't want the illusion being broken around them. - while (mindEnum.MoveNext(out var uid, out var mind, out var xform)) - { - if (!mind.HasMind) - continue; - if (ghostQuery.HasComponent(uid)) - continue; - var mapOrNull = xform.MapUid; - if (mapOrNull is null) - continue; - var map = mapOrNull.Value; - if (!chunksToLoad.ContainsKey(map)) - continue; - - var wc = _xformSys.GetWorldPosition(xform); - var coords = WorldGen.WorldToChunkCoords(wc); - var chunks = new GridPointsNearEnumerator(coords.Floored(), PlayerLoadRadius); - - var set = chunksToLoad[map]; - - while (chunks.MoveNext(out var chunk)) - { - if (!set.TryGetValue(chunk.Value, out _)) - set[chunk.Value] = new List(4); - set[chunk.Value].Add(uid); - } - } - - var loadedEnum = EntityQueryEnumerator(); - var chunksUnloaded = 0; - - // Make sure these chunks get unloaded at the end of the tick. - while (loadedEnum.MoveNext(out var uid, out var _, out var chunk)) - { - var coords = chunk.Coordinates; - - if (!chunksToLoad[chunk.Map].ContainsKey(coords)) - { - RemCompDeferred(uid); - chunksUnloaded++; - } - } - - if (chunksUnloaded > 0) - _sawmill.Debug($"Queued {chunksUnloaded} chunks for unload."); - - if (chunksToLoad.All(x => x.Value.Count == 0)) - return; - - var startTime = _gameTiming.RealTime; - var count = 0; - var loadedQuery = GetEntityQuery(); - var controllerQuery = GetEntityQuery(); - foreach (var (map, chunks) in chunksToLoad) - { - var controller = controllerQuery.GetComponent(map); - foreach (var (chunk, loaders) in chunks) - { - var ent = GetOrCreateChunk(chunk, map, controller); // Ensure everything loads. - LoadedChunkComponent? c = null; - if (ent is not null && !loadedQuery.TryGetComponent(ent.Value, out c)) - { - c = AddComp(ent.Value); - count += 1; - } - - if (c is not null) - c.Loaders = loaders; - } - } - - if (count > 0) - { - var timeSpan = _gameTiming.RealTime - startTime; - _sawmill.Debug($"Loaded {count} chunks in {timeSpan.TotalMilliseconds:N2}ms."); - } - } - - /// - /// Attempts to get a chunk, creating it if it doesn't exist. - /// - /// Chunk coordinates to get the chunk entity for. - /// Map the chunk is in. - /// The controller this chunk belongs to. - /// A chunk, if available. - [Pure] - public EntityUid? GetOrCreateChunk(Vector2i chunk, EntityUid map, WorldControllerComponent? controller = null) - { - if (!Resolve(map, ref controller)) - throw new Exception($"Tried to use {ToPrettyString(map)} as a world map, without actually being one."); - - if (controller.Chunks.TryGetValue(chunk, out var ent)) - return ent; - return CreateChunkEntity(chunk, map, controller); - } - - /// - /// Constructs a new chunk entity, attaching it to the map. - /// - /// The coordinates the new chunk should be initialized for. - /// - /// - /// - private EntityUid CreateChunkEntity(Vector2i chunkCoords, EntityUid map, WorldControllerComponent controller) - { - var chunk = Spawn(controller.ChunkProto, MapCoordinates.Nullspace); - StartupChunkEntity(chunk, chunkCoords, map, controller); - _metaData.SetEntityName(chunk, $"Chunk {chunkCoords.X}/{chunkCoords.Y}"); - return chunk; - } - - private void StartupChunkEntity(EntityUid chunk, Vector2i coords, EntityUid map, - WorldControllerComponent controller) - { - if (!TryComp(chunk, out var chunkComponent)) - { - _sawmill.Error($"Chunk {ToPrettyString(chunk)} is missing WorldChunkComponent."); - return; - } - - ref var chunks = ref controller.Chunks; - - chunks[coords] = chunk; // Add this entity to chunk index. - chunkComponent.Coordinates = coords; - chunkComponent.Map = map; - var ev = new WorldChunkAddedEvent(chunk, coords); - RaiseLocalEvent(map, ref ev, broadcast: true); - } -} - -/// -/// A directed event fired when a chunk is initially set up in the world. The chunk is not loaded at this point. -/// -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkAddedEvent(EntityUid Chunk, Vector2i Coords); - -/// -/// A directed event fired when a chunk is loaded into the world, i.e. a player or other world loader has entered vicinity. -/// -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkLoadedEvent(EntityUid Chunk, Vector2i Coords); - -/// -/// A directed event fired when a chunk is unloaded from the world, i.e. no world loaders remain nearby. -/// -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkUnloadedEvent(EntityUid Chunk, Vector2i Coords); diff --git a/Content.Server/Worldgen/Systems/WorldgenConfigSystem.cs b/Content.Server/Worldgen/Systems/WorldgenConfigSystem.cs deleted file mode 100644 index cc0ec62733..0000000000 --- a/Content.Server/Worldgen/Systems/WorldgenConfigSystem.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Content.Server.Administration; -using Content.Server.GameTicking; -using Content.Server.GameTicking.Events; -using Content.Server.Worldgen.Components; -using Content.Server.Worldgen.Prototypes; -using Content.Shared.Administration; -using Content.Shared.CCVar; -using Robust.Shared.Configuration; -using Robust.Shared.Console; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Utility; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This handles configuring world generation during round start. -/// -public sealed class WorldgenConfigSystem : EntitySystem -{ - [Dependency] private readonly GameTicker _gameTicker = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly IConsoleHost _conHost = default!; - [Dependency] private readonly SharedMapSystem _map = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly ISerializationManager _ser = default!; - - private bool _enabled; - private string _worldgenConfig = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnLoadingMaps); - _conHost.RegisterCommand("applyworldgenconfig", Loc.GetString("cmd-applyworldgenconfig-description"), Loc.GetString("cmd-applyworldgenconfig-help"), ApplyWorldgenConfigCommand); - Subs.CVar(_cfg, CCVars.WorldgenEnabled, b => _enabled = b, true); - Subs.CVar(_cfg, CCVars.WorldgenConfig, s => _worldgenConfig = s, true); - } - - [AdminCommand(AdminFlags.Mapping)] - private void ApplyWorldgenConfigCommand(IConsoleShell shell, string argstr, string[] args) - { - if (args.Length != 2) - { - shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", ("properAmount", 2), ("currentAmount", args.Length))); - return; - } - - if (!int.TryParse(args[0], out var mapInt) || !_map.MapExists(new MapId(mapInt))) - { - shell.WriteError(Loc.GetString("shell-invalid-map-id")); - return; - } - - var map = _map.GetMapOrInvalid(new MapId(mapInt)); - - if (!_proto.TryIndex(args[1], out var proto)) - { - shell.WriteError(Loc.GetString("shell-argument-must-be-prototype", ("index", 2), ("prototypeName", "cmd-applyworldgenconfig-prototype"))); - return; - } - - proto.Apply(map, _ser, EntityManager); - shell.WriteLine(Loc.GetString("cmd-applyworldgenconfig-success")); - } - - /// - /// Applies the world config to the default map if enabled. - /// - private void OnLoadingMaps(RoundStartingEvent ev) - { - if (_enabled == false) - return; - - var target = _map.GetMapOrInvalid(_gameTicker.DefaultMap); - Log.Debug($"Trying to configure {_gameTicker.DefaultMap}, aka {ToPrettyString(target)} aka {target}"); - var cfg = _proto.Index(_worldgenConfig); - - cfg.Apply(target, _ser, EntityManager); // Apply the config to the map. - - DebugTools.Assert(HasComp(target)); - } -} - diff --git a/Content.Server/Worldgen/Tools/EntitySpawnCollectionCache.cs b/Content.Server/Worldgen/Tools/EntitySpawnCollectionCache.cs deleted file mode 100644 index 5480575427..0000000000 --- a/Content.Server/Worldgen/Tools/EntitySpawnCollectionCache.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Linq; -using Content.Shared.Storage; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Tools; - -/// -/// A faster version of EntitySpawnCollection that requires caching to work. -/// -public sealed class EntitySpawnCollectionCache -{ - [ViewVariables] private readonly Dictionary _orGroups = new(); - - public EntitySpawnCollectionCache(IEnumerable entries) - { - // collect groups together, create singular items that pass probability - foreach (var entry in entries) - { - if (!_orGroups.TryGetValue(entry.GroupId ?? string.Empty, out var orGroup)) - { - orGroup = new OrGroup(); - _orGroups.Add(entry.GroupId ?? string.Empty, orGroup); - } - - orGroup.Entries.Add(entry); - orGroup.CumulativeProbability += entry.SpawnProbability; - } - } - - /// - /// Using a collection of entity spawn entries, picks a random list of entity prototypes to spawn from that collection. - /// - /// - /// This does not spawn the entities. The caller is responsible for doing so, since it may want to do something - /// special to those entities (offset them, insert them into storage, etc) - /// - /// Resolve param. - /// List that spawned entities are inserted into. - /// A list of entity prototypes that should be spawned. - /// This is primarily useful if you're calling it many times over, as it lets you reuse the list repeatedly. - public void GetSpawns(IRobustRandom random, ref List spawned) - { - // handle orgroup spawns - foreach (var spawnValue in _orGroups.Values) - { - //HACK: This doesn't seem to work without this if there's only a single orgroup entry. Not sure how to fix the original math properly, but it works in every other case. - if (spawnValue.Entries.Count == 1) - { - var entry = spawnValue.Entries.First(); - var amount = entry.Amount; - - if (entry.MaxAmount > amount) - amount = random.Next(amount, entry.MaxAmount); - - for (var index = 0; index < amount; index++) - { - spawned.Add(entry.PrototypeId); - } - - continue; - } - - // For each group use the added cumulative probability to roll a double in that range - var diceRoll = random.NextDouble() * spawnValue.CumulativeProbability; - // Add the entry's spawn probability to this value, if equals or lower, spawn item, otherwise continue to next item. - var cumulative = 0.0; - foreach (var entry in spawnValue.Entries) - { - cumulative += entry.SpawnProbability; - if (diceRoll > cumulative) - continue; - // Dice roll succeeded, add item and break loop - - var amount = entry.Amount; - - if (entry.MaxAmount > amount) - amount = random.Next(amount, entry.MaxAmount); - - for (var index = 0; index < amount; index++) - { - spawned.Add(entry.PrototypeId); - } - - break; - } - } - } - - private sealed class OrGroup - { - [ViewVariables] public List Entries { get; } = new(); - - [ViewVariables] public float CumulativeProbability { get; set; } - } -} - diff --git a/Content.Server/Worldgen/Tools/PoissonDiskSampler.cs b/Content.Server/Worldgen/Tools/PoissonDiskSampler.cs deleted file mode 100644 index cb7c5a1411..0000000000 --- a/Content.Server/Worldgen/Tools/PoissonDiskSampler.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Numerics; -using Robust.Shared.Random; -using Robust.Shared.Utility; - -namespace Content.Server.Worldgen.Tools; - -/// -/// An implementation of Poisson Disk Sampling, for evenly spreading points across a given area. -/// -public sealed class PoissonDiskSampler -{ - public const int DefaultPointsPerIteration = 30; - [Dependency] private readonly IRobustRandom _random = default!; - - /// - /// Samples for points within the given circle. - /// - /// Center of the sample - /// Radius of the sample - /// Minimum distance between points. Must be above 0! - /// The number of points placed per iteration of the algorithm - /// An enumerator of points - public SampleEnumerator SampleCircle(Vector2 center, float radius, float minimumDistance, - int pointsPerIteration = DefaultPointsPerIteration) - { - return Sample(center - new Vector2(radius, radius), center + new Vector2(radius, radius), radius, - minimumDistance, pointsPerIteration); - } - - /// - /// Samples for points within the given rectangle. - /// - /// The top left of the rectangle - /// The bottom right of the rectangle - /// Minimum distance between points. Must be above 0! - /// The number of points placed per iteration of the algorithm - /// An enumerator of points - public SampleEnumerator SampleRectangle(Vector2 topLeft, Vector2 lowerRight, float minimumDistance, - int pointsPerIteration = DefaultPointsPerIteration) - { - return Sample(topLeft, lowerRight, null, minimumDistance, pointsPerIteration); - } - - /// - /// Samples for points within the given rectangle, with an optional rejection distance. - /// - /// The top left of the rectangle - /// The bottom right of the rectangle - /// The distance at which points will be discarded, if any - /// Minimum distance between points. Must be above 0! - /// The number of points placed per iteration of the algorithm - /// An enumerator of points - public SampleEnumerator Sample(Vector2 topLeft, Vector2 lowerRight, float? rejectionDistance, - float minimumDistance, int pointsPerIteration) - { - // This still doesn't guard against dangerously low but non-zero distances, but this will do for now. - DebugTools.Assert(minimumDistance > 0, "Minimum distance must be above 0, or else an infinite number of points would be generated."); - - var settings = new SampleSettings - { - TopLeft = topLeft, LowerRight = lowerRight, - Dimensions = lowerRight - topLeft, - Center = (topLeft + lowerRight) / 2, - CellSize = minimumDistance / (float) Math.Sqrt(2), - MinimumDistance = minimumDistance, - RejectionSqDistance = rejectionDistance * rejectionDistance - }; - - settings.GridWidth = (int) (settings.Dimensions.X / settings.CellSize) + 1; - settings.GridHeight = (int) (settings.Dimensions.Y / settings.CellSize) + 1; - - var state = new State - { - Grid = new Vector2?[settings.GridWidth, settings.GridHeight], - ActivePoints = new List() - }; - - return new SampleEnumerator(this, state, settings, pointsPerIteration); - } - - private Vector2 AddFirstPoint(ref SampleSettings settings, ref State state) - { - while (true) - { - var d = _random.NextDouble(); - var xr = settings.TopLeft.X + settings.Dimensions.X * d; - - d = _random.NextDouble(); - var yr = settings.TopLeft.Y + settings.Dimensions.Y * d; - - var p = new Vector2((float) xr, (float) yr); - if (settings.RejectionSqDistance != null && - (settings.Center - p).LengthSquared() > settings.RejectionSqDistance) - continue; - - var index = Denormalize(p, settings.TopLeft, settings.CellSize); - - state.Grid[(int) index.X, (int) index.Y] = p; - - state.ActivePoints.Add(p); - return p; - } - } - - private Vector2? AddNextPoint(Vector2 point, ref SampleSettings settings, ref State state) - { - var q = GenerateRandomAround(point, settings.MinimumDistance); - - if (q.X >= settings.TopLeft.X && q.X < settings.LowerRight.X && - q.Y > settings.TopLeft.Y && q.Y < settings.LowerRight.Y && - (settings.RejectionSqDistance == null || - (settings.Center - q).LengthSquared() <= settings.RejectionSqDistance)) - { - var qIndex = Denormalize(q, settings.TopLeft, settings.CellSize); - var tooClose = false; - - for (var i = (int) Math.Max(0, qIndex.X - 2); - i < Math.Min(settings.GridWidth, qIndex.X + 3) && !tooClose; - i++) - for (var j = (int) Math.Max(0, qIndex.Y - 2); - j < Math.Min(settings.GridHeight, qIndex.Y + 3) && !tooClose; - j++) - { - if (state.Grid[i, j].HasValue && (state.Grid[i, j]!.Value - q).Length() < settings.MinimumDistance) - tooClose = true; - } - - if (!tooClose) - { - state.ActivePoints.Add(q); - state.Grid[(int) qIndex.X, (int) qIndex.Y] = q; - return q; - } - } - - return null; - } - - private Vector2 GenerateRandomAround(Vector2 center, float minimumDistance) - { - var d = _random.NextDouble(); - var radius = minimumDistance + minimumDistance * d; - - d = _random.NextDouble(); - var angle = Math.PI * 2 * d; - - var newX = radius * Math.Sin(angle); - var newY = radius * Math.Cos(angle); - - return new Vector2((float) (center.X + newX), (float) (center.Y + newY)); - } - - private static Vector2 Denormalize(Vector2 point, Vector2 origin, double cellSize) - { - return new Vector2((int) ((point.X - origin.X) / cellSize), (int) ((point.Y - origin.Y) / cellSize)); - } - - public struct SampleEnumerator - { - private PoissonDiskSampler _pds; - private State _state; - private SampleSettings _settings; - // These variables make up the state machine. - private bool _returnedFirstPoint; - private int _pointsPerIteration; - private int _iterationListIndex; - private bool _iterationFound; - private int _iterationPosition; - - // This has internal access because C# nested type access is being weird. - internal SampleEnumerator(PoissonDiskSampler pds, State state, SampleSettings settings, int ppi) - { - _pds = pds; - _state = state; - _settings = settings; - _pointsPerIteration = ppi; - } - - public bool MoveNext([NotNullWhen(true)] out Vector2? point) - { - // First point is chosen via a very particular method. - if (!_returnedFirstPoint) - { - _returnedFirstPoint = true; - point = _pds.AddFirstPoint(ref _settings, ref _state); - return true; - } - - // Remaining points have to be fed out carefully. - // We can be interrupted (by a successful point) mid-stream. - while (_state.ActivePoints.Count != 0) - { - if (_iterationPosition == 0) - { - // First point of iteration. - _iterationListIndex = _pds._random.Next(_state.ActivePoints.Count); - _iterationFound = false; - } - - var basePoint = _state.ActivePoints[_iterationListIndex]; - - point = _pds.AddNextPoint(basePoint, ref _settings, ref _state); - - // Set this now, return later after processing is complete. - _iterationFound |= point != null; - - // Iteration loop advance. - _iterationPosition++; - if (_iterationPosition == _pointsPerIteration) - { - // Reached end of this iteration. - _iterationPosition = 0; - if (!_iterationFound) - _state.ActivePoints.RemoveAt(_iterationListIndex); - } - - if (point != null) - return true; - } - point = null; - return false; - } - } - - internal struct State - { - public Vector2?[,] Grid; - public List ActivePoints; - } - - internal struct SampleSettings - { - public Vector2 TopLeft, LowerRight, Center; - public Vector2 Dimensions; - public float? RejectionSqDistance; - public float MinimumDistance; - public float CellSize; - public int GridWidth, GridHeight; - } -} - - - diff --git a/Content.Server/Worldgen/WorldGen.cs b/Content.Server/Worldgen/WorldGen.cs deleted file mode 100644 index 1ed20b9f1f..0000000000 --- a/Content.Server/Worldgen/WorldGen.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Diagnostics.Contracts; -using System.Numerics; - -namespace Content.Server.Worldgen; - -/// -/// Contains a few world-generation related constants and static functions. -/// -public static class WorldGen -{ - /// - /// The size of each chunk (isn't that self-explanatory.) - /// Be careful about how small you make this. - /// - public const int ChunkSize = 128; - - /// - /// Converts world coordinates to chunk coordinates. - /// - /// World coordinates - /// Chunk coordinates - [Pure] - public static Vector2i WorldToChunkCoords(Vector2i inp) - { - return (inp * new Vector2(1.0f / ChunkSize, 1.0f / ChunkSize)).Floored(); - } - - /// - /// Converts world coordinates to chunk coordinates. - /// - /// World coordinates - /// Chunk coordinates - [Pure] - public static Vector2 WorldToChunkCoords(Vector2 inp) - { - return inp * new Vector2(1.0f / ChunkSize, 1.0f / ChunkSize); - } - - /// - /// Converts chunk coordinates to world coordinates. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoords(Vector2i inp) - { - return inp * ChunkSize; - } - - /// - /// Converts chunk coordinates to world coordinates. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoords(Vector2 inp) - { - return inp * ChunkSize; - } - - /// - /// Converts chunk coordinates to world coordinates, getting the center of the chunk. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoordsCentered(Vector2i inp) - { - return inp * ChunkSize + Vector2i.One * (ChunkSize / 2); - } -} - diff --git a/Content.Shared/Access/Components/AccessReaderComponent.cs b/Content.Shared/Access/Components/AccessReaderComponent.cs index c261c7deca..32662d7838 100644 --- a/Content.Shared/Access/Components/AccessReaderComponent.cs +++ b/Content.Shared/Access/Components/AccessReaderComponent.cs @@ -38,7 +38,7 @@ public sealed partial class AccessReaderComponent : Component /// An unmodified copy of the original list of the access groups that grant access to this reader. /// /// - /// If null, the access lists of this entity have not been modified yet. + /// If null, entity isn't intialized yet. /// [DataField] public List>>? AccessListsOriginal = null; diff --git a/Content.Shared/Access/Components/IdCardConsoleComponent.cs b/Content.Shared/Access/Components/IdCardConsoleComponent.cs index f528647788..6a889a925b 100644 --- a/Content.Shared/Access/Components/IdCardConsoleComponent.cs +++ b/Content.Shared/Access/Components/IdCardConsoleComponent.cs @@ -26,9 +26,9 @@ public sealed partial class IdCardConsoleComponent : Component public readonly string FullName; public readonly string JobTitle; public readonly List> AccessList; - public readonly ProtoId JobPrototype; + public readonly ProtoId? JobPrototype; - public WriteToTargetIdMessage(string fullName, string jobTitle, List> accessList, ProtoId jobPrototype) + public WriteToTargetIdMessage(string fullName, string jobTitle, List> accessList, ProtoId? jobPrototype) { FullName = fullName; JobTitle = jobTitle; diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index f8c6d49244..19c1cb880b 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -41,6 +41,7 @@ public sealed class AccessReaderSystem : EntitySystem { base.Initialize(); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnLinkAttempt); @@ -52,13 +53,18 @@ public sealed class AccessReaderSystem : EntitySystem SubscribeLocalEvent(OnHandleState); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.AccessListsOriginal ??= [.. ent.Comp.AccessLists]; + Dirty(ent); + } + private void OnExamined(Entity ent, ref ExaminedEvent args) { - if (!GetMainAccessReader(ent, out var mainAccessReader)) + if (!GetMainAccessReader(ent, out var mainAccessReader) || + mainAccessReader.Value.Comp.AccessListsOriginal == null) return; - mainAccessReader.Value.Comp.AccessListsOriginal ??= new(mainAccessReader.Value.Comp.AccessLists); - var accessHasBeenModified = mainAccessReader.Value.Comp.AccessLists.Count != mainAccessReader.Value.Comp.AccessListsOriginal.Count; if (!accessHasBeenModified) diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index 485dc89580..18c85c3aaa 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -23,14 +23,7 @@ namespace Content.Shared.ActionBlocker { [Dependency] private readonly SharedContainerSystem _container = default!; - private EntityQuery _complexInteractionQuery; - - public override void Initialize() - { - base.Initialize(); - - _complexInteractionQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _complexInteractionQuery = default!; // These two methods should probably both live in SharedMoverController // but they're called in a million places and I'm not doing that diff --git a/Content.Shared/Actions/ActionContainerSystem.cs b/Content.Shared/Actions/ActionContainerSystem.cs index 534f0d3ee7..642131a365 100644 --- a/Content.Shared/Actions/ActionContainerSystem.cs +++ b/Content.Shared/Actions/ActionContainerSystem.cs @@ -23,14 +23,12 @@ public sealed class ActionContainerSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedMindSystem _mind = default!; - private EntityQuery _query; + [Dependency] private readonly EntityQuery _query = default!; public override void Initialize() { base.Initialize(); - _query = GetEntityQuery(); - SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnEntityRemoved); diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 87792df6bc..3f519e219e 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -35,19 +35,15 @@ public abstract partial class SharedActionsSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - private EntityQuery _actionQuery; - private EntityQuery _actionsQuery; - private EntityQuery _mindQuery; + [Dependency] private readonly EntityQuery _actionQuery = default!; + [Dependency] private readonly EntityQuery _actionsQuery = default!; + [Dependency] private readonly EntityQuery _mindQuery = default!; public override void Initialize() { base.Initialize(); InitializeActionDoAfter(); - _actionQuery = GetEntityQuery(); - _actionsQuery = GetEntityQuery(); - _mindQuery = GetEntityQuery(); - SubscribeLocalEvent(OnActionMapInit); SubscribeLocalEvent(OnActionShutdown); @@ -929,7 +925,7 @@ public abstract partial class SharedActionsSystem : EntitySystem if (GameTiming.ApplyingState) return; - var ev = new GetItemActionsEvent(_actionContainer, args.Equipee, args.Equipment, args.SlotFlags); + var ev = new GetItemActionsEvent(_actionContainer, args.EquipTarget, args.Equipment, args.SlotFlags); RaiseLocalEvent(args.Equipment, ev); if (ev.Actions.Count == 0) diff --git a/Content.Shared/Alert/AlertsSystem.cs b/Content.Shared/Alert/AlertsSystem.cs index 834a22b8de..5c751b938f 100644 --- a/Content.Shared/Alert/AlertsSystem.cs +++ b/Content.Shared/Alert/AlertsSystem.cs @@ -11,15 +11,13 @@ public abstract class AlertsSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - private EntityQuery _alertsQuery; + [Dependency] private readonly EntityQuery _alertsQuery = default!; private FrozenDictionary, AlertPrototype> _typeToAlert = default!; public override void Initialize() { base.Initialize(); - _alertsQuery = GetEntityQuery(); - SubscribeLocalEvent(HandleComponentStartup); SubscribeLocalEvent(HandleComponentShutdown); SubscribeLocalEvent(OnPlayerAttached); diff --git a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs index 4c3cdb0146..0b2c4b7a16 100644 --- a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs +++ b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs @@ -17,6 +17,8 @@ public abstract class SharedGravityAnomalySystem : EntitySystem [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly EntityQuery _physQuery = default!; + /// public override void Initialize() { @@ -30,17 +32,15 @@ public abstract class SharedGravityAnomalySystem : EntitySystem var range = component.MaxThrowRange * args.Severity * args.PowerModifier; var strength = component.MaxThrowStrength * args.Severity * args.PowerModifier; var lookup = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic | LookupFlags.Sundries); - var xformQuery = GetEntityQuery(); - var worldPos = _xform.GetWorldPosition(xform, xformQuery); - var physQuery = GetEntityQuery(); + var worldPos = _xform.GetWorldPosition(xform); foreach (var ent in lookup) { - if (physQuery.TryGetComponent(ent, out var phys) + if (_physQuery.TryGetComponent(ent, out var phys) && (phys.CollisionMask & (int) CollisionGroup.GhostImpassable) != 0) continue; - var foo = _xform.GetWorldPosition(ent, xformQuery) - worldPos; + var foo = _xform.GetWorldPosition(ent) - worldPos; _throwing.TryThrow(ent, foo * 10, strength, uid, 0); } } @@ -64,16 +64,14 @@ public abstract class SharedGravityAnomalySystem : EntitySystem var range = component.MaxThrowRange * 2 * args.PowerModifier; var strength = component.MaxThrowStrength * 2 * args.PowerModifier; var lookup = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic | LookupFlags.Sundries); - var xformQuery = GetEntityQuery(); - var physQuery = GetEntityQuery(); foreach (var ent in lookup) { - if (physQuery.TryGetComponent(ent, out var phys) + if (_physQuery.TryGetComponent(ent, out var phys) && (phys.CollisionMask & (int) CollisionGroup.GhostImpassable) != 0) continue; - var foo = _xform.GetWorldPosition(ent, xformQuery) - worldPos; + var foo = _xform.GetWorldPosition(ent) - worldPos; _throwing.TryThrow(ent, foo * 5, strength, uid, 0); } } diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index b63b25fa5d..03aead97c5 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -38,6 +38,8 @@ public abstract class SharedAnomalySystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly EntityQuery _physQuery = default!; + public override void Initialize() { base.Initialize(); @@ -409,7 +411,6 @@ public abstract class SharedAnomalySystem : EntitySystem if (tilerefs.Count == 0) return null; - var physQuery = GetEntityQuery(); var resultList = new List(); while (resultList.Count < amount) { @@ -437,7 +438,7 @@ public abstract class SharedAnomalySystem : EntitySystem var valid = true; foreach (var ent in _map.GetAnchoredEntities(xform.GridUid.Value, grid, tileref.GridIndices)) { - if (!physQuery.TryGetComponent(ent, out var body)) + if (!_physQuery.TryGetComponent(ent, out var body)) continue; if (body.BodyType != BodyType.Static || diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs index 10d303cd1d..ffd0daee5b 100644 --- a/Content.Shared/Atmos/Atmospherics.cs +++ b/Content.Shared/Atmos/Atmospherics.cs @@ -104,6 +104,21 @@ namespace Content.Shared.Atmos public const float OxygenMolesGasMiner = MolesCellGasMiner * OxygenStandard; public const float NitrogenMolesGasMiner = MolesCellGasMiner * NitrogenStandard; + /// + /// Converts Grams to Kilograms. + /// + public const float gToKg = 0.001f; + + /// + /// Convert kPa to Kg/m^2 + /// + public const float kPaToKg_m2 = 0.00980665f; + + /// + /// Convert Kg/m^2 to kPa + /// + public const float Kg_m2TokPa = 101.9716212978f; + #endregion /// @@ -138,8 +153,19 @@ namespace Content.Shared.Atmos /// public const float MinimumAirToSuspend = (MolesCellStandard * MinimumAirRatioToSuspend); - public const float MinimumTemperatureToMove = (T20C + 100f); + /// + /// The minimum difference in temperature between s + /// (s) required + /// for LINDA to report a pressure difference between them for space wind. + /// In Kelvin. + /// + public const float MinimumTemperatureToMove = 5f; + /// + /// The minimum difference in moles between s + /// (s) required for LINDA to + /// report a pressure difference between them for space wind. + /// public const float MinimumMolesDeltaToMove = (MolesCellStandard * MinimumAirRatioToMove); /// @@ -170,22 +196,6 @@ namespace Content.Shared.Atmos /// public const float SpaceHeatCapacity = 7000f; - /// - /// Dictionary of chemical abbreviations for - /// - public static Dictionary GasAbbreviations = new Dictionary() - { - [Gas.Ammonia] = Loc.GetString("gas-ammonia-abbreviation"), - [Gas.CarbonDioxide] = Loc.GetString("gas-carbon-dioxide-abbreviation"), - [Gas.Frezon] = Loc.GetString("gas-frezon-abbreviation"), - [Gas.Nitrogen] = Loc.GetString("gas-nitrogen-abbreviation"), - [Gas.NitrousOxide] = Loc.GetString("gas-nitrous-oxide-abbreviation"), - [Gas.Oxygen] = Loc.GetString("gas-oxygen-abbreviation"), - [Gas.Plasma] = Loc.GetString("gas-plasma-abbreviation"), - [Gas.Tritium] = Loc.GetString("gas-tritium-abbreviation"), - [Gas.WaterVapor] = Loc.GetString("gas-water-vapor-abbreviation"), - }; - #region Excited Groups /// @@ -224,7 +234,7 @@ namespace Content.Shared.Atmos /// /// Amount of heat released per mole of burnt hydrogen or tritium (hydrogen isotope) /// - public const float FireHydrogenEnergyReleased = 284e4f; + public const float FireHydrogenEnergyReleased = 284e3f; public const float FireMinimumTemperatureToExist = T0C + 100f; public const float FireMinimumTemperatureToSpread = T0C + 150f; public const float FireSpreadRadiosityScale = 0.85f; @@ -235,8 +245,8 @@ namespace Content.Shared.Atmos public const float SuperSaturationEnds = SuperSaturationThreshold / 3; public const float OxygenBurnRateBase = 1.4f; - public const float PlasmaMinimumBurnTemperature = (100f+T0C); - public const float PlasmaUpperTemperature = (1370f+T0C); + public const float PlasmaMinimumBurnTemperature = 100f + T0C; + public const float PlasmaUpperTemperature = 1370f + T0C; public const float PlasmaOxygenFullburn = 10f; public const float PlasmaBurnRateDelta = 9f; diff --git a/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs b/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs index 115cb54892..cc3914fe67 100644 --- a/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs +++ b/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs @@ -1,101 +1,103 @@ using Robust.Shared.GameStates; -using Robust.Shared.Map; using Robust.Shared.Serialization; namespace Content.Shared.Atmos.Components; +/// +/// Used for gas analyzers, an item that shows players the gas contents of an atmos +/// device they use it on or of the tile they are standing on. +/// [RegisterComponent, NetworkedComponent] public sealed partial class GasAnalyzerComponent : Component { - [ViewVariables] + /// + /// The target entity currently being analyzed. + /// + [DataField] public EntityUid? Target; - [ViewVariables] - public EntityUid User; + /// + /// The current user of the gas analyzer. + /// + [DataField] + public EntityUid? User; - [DataField("enabled"), ViewVariables(VVAccess.ReadWrite)] + /// + /// Is the analyzer currently active? + /// + [DataField] public bool Enabled; - - [Serializable, NetSerializable] - public enum GasAnalyzerUiKey - { - Key, - } - - /// - /// Atmospheric data is gathered in the system and sent to the user - /// - [Serializable, NetSerializable] - public sealed class GasAnalyzerUserMessage : BoundUserInterfaceMessage - { - public string DeviceName; - public NetEntity DeviceUid; - public bool DeviceFlipped; - public string? Error; - public GasMixEntry[] NodeGasMixes; - public GasAnalyzerUserMessage(GasMixEntry[] nodeGasMixes, string deviceName, NetEntity deviceUid, bool deviceFlipped, string? error = null) - { - NodeGasMixes = nodeGasMixes; - DeviceName = deviceName; - DeviceUid = deviceUid; - DeviceFlipped = deviceFlipped; - Error = error; - } - } - - /// - /// Contains information on a gas mix entry, turns into a tab in the UI - /// - [Serializable, NetSerializable] - public struct GasMixEntry - { - /// - /// Name of the tab in the UI - /// - public readonly string Name; - public readonly float Volume; - public readonly float Pressure; - public readonly float Temperature; - public readonly GasEntry[]? Gases; - - public GasMixEntry(string name, float volume, float pressure, float temperature, GasEntry[]? gases = null) - { - Name = name; - Volume = volume; - Pressure = pressure; - Temperature = temperature; - Gases = gases; - } - } - - /// - /// Individual gas entry data for populating the UI - /// - [Serializable, NetSerializable] - public struct GasEntry - { - public readonly string Name; - public readonly float Amount; - public readonly string Color; - - public GasEntry(string name, float amount, string color) - { - Name = name; - Amount = amount; - Color = color; - } - - public override string ToString() - { - // e.g. "Plasma: 2000 mol" - return Loc.GetString( - "gas-entry-info", - ("gasName", Name), - ("gasAmount", Amount)); - } - } } +/// +/// Atmospheric data is gathered in the system and sent to the user. +/// +[Serializable, NetSerializable] +public sealed class GasAnalyzerUserMessage(GasMixEntry[] nodeGasMixes, string deviceName, NetEntity deviceUid, bool deviceFlipped) : BoundUserInterfaceMessage +{ + public string DeviceName = deviceName; + public NetEntity DeviceUid = deviceUid; + public bool DeviceFlipped = deviceFlipped; + public GasMixEntry[] NodeGasMixes = nodeGasMixes; +} + +/// +/// Contains information on a gas mix entry, turns into a tab in the UI. +/// +[Serializable, NetSerializable] +public readonly record struct GasMixEntry(string Name, float Volume, float Pressure, float Temperature, GasEntry[]? Gases = null) +{ + /// + /// Name of the tab in the UI. + /// + public readonly string Name = Name; + /// + /// Volume of this gas mixture. + /// + public readonly float Volume = Volume; + /// + /// Pressure of this gas mixture. + /// + public readonly float Pressure = Pressure; + /// + /// Temperature of this gas mixture. + /// + public readonly float Temperature = Temperature; + /// + /// The gases contained in this gas mixture. + /// The gases below a certain mol threshold are not included. + /// + public readonly GasEntry[]? Gases = Gases; +} + +/// +/// Individual gas entry data for populating the UI. +/// +[Serializable, NetSerializable] +public readonly record struct GasEntry(Gas Gas, float Amount) +{ + /// + /// The gas this entry represents. + /// + public readonly Gas Gas = Gas; + /// + /// The gas amount in mol. + /// + public readonly float Amount = Amount; +} + +/// +/// Key for the GasAnalyzerBoundUserInterface. +/// +[Serializable, NetSerializable] +public enum GasAnalyzerUiKey +{ + Key, +} + +/// +/// Individual gas entry data for populating the UI +/// [Serializable, NetSerializable] public enum GasAnalyzerVisuals : byte { diff --git a/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs b/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs new file mode 100644 index 0000000000..573d3cbdf2 --- /dev/null +++ b/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs @@ -0,0 +1,83 @@ +using Robust.Shared.Audio; + +namespace Content.Shared.Atmos.Components; + +/// +/// An atmos device that can hold and release gas. Such as a Gas Tank or Gas Canister. +/// Abstract as both of these devices share a lot of similar behavior. +/// +public abstract partial class GasMaxPressureHolderComponent : Component, IGasMaxPressureHolder +{ + private const float DefaultIntegrity = 3f; + private const float DefaultOutputPressure = Atmospherics.OneAtmosphere; + + /// + /// Minimum release pressure possible for the release valve. + /// + [DataField, AutoNetworkedField] + public float MinReleasePressure { get; set; } = DefaultOutputPressure / 10; + + /// + /// Maximum release pressure possible for the release valve. + /// + [DataField, AutoNetworkedField] + public float MaxReleasePressure { get; set; } = 3 * DefaultOutputPressure; + + /// + /// Current Valve release pressure. + /// + [DataField, AutoNetworkedField] + public float ReleasePressure { get; set; } = DefaultOutputPressure; + + /// + /// Whether the release valve is open on this device. + /// + [DataField, AutoNetworkedField] + public bool ReleaseValveOpen { get; set; } + + /// + /// Sound made when the valve on this device is opened or closed. + /// + [DataField] + public SoundSpecifier ValveSound = + new SoundCollectionSpecifier("valveSqueak") + { + Params = AudioParams.Default.WithVolume(-5f), + }; + + /// + /// Sound made when air is leaked out of this device. + /// + [DataField] + public SoundSpecifier? ReleaseSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/spray.ogg") + { + Params = AudioParams.Default.WithVolume(-5f), + }; + + /// + /// The mixture of air contained in this device. + /// + [DataField, AutoNetworkedField] + public GasMixture Air { get; set; } + + // TODO ATMOS: Proper loud BANG sound, these are lethal concussive blast waves + [DataField] + public SoundSpecifier? RuptureSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/spray.ogg"); + + // The values chosen for safety and overpressure are arbitrary, if reactions change feel free to change these to whatever is most fun! + // Generally a lower overpressure value and lower safety pressure mean weaker bombs with shorter fuses. + [DataField] + public float SafetyPressure { get; set; } = 15 * Atmospherics.OneAtmosphere; + + [DataField] + public float Overpressure { get; set; } = 20 * Atmospherics.OneAtmosphere; + + [DataField] + public LocId? SafetyAlert { get; set; } = "gas-max-pressure-alert"; + + [DataField] + public float MaxIntegrity { get; set; } = DefaultIntegrity; + + [DataField] + public float Integrity { get; set; } = DefaultIntegrity; +} diff --git a/Content.Shared/Atmos/Components/GasTankComponent.cs b/Content.Shared/Atmos/Components/GasTankComponent.cs index 7ca6098555..31fa3a0d5a 100644 --- a/Content.Shared/Atmos/Components/GasTankComponent.cs +++ b/Content.Shared/Atmos/Components/GasTankComponent.cs @@ -5,18 +5,12 @@ using Robust.Shared.Prototypes; namespace Content.Shared.Atmos.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] -public sealed partial class GasTankComponent : Component, IGasMixtureHolder +public sealed partial class GasTankComponent : GasMaxPressureHolderComponent { - public const float MaxExplosionRange = 26f; - private const float DefaultLowPressure = 0f; - private const float DefaultOutputPressure = Atmospherics.OneAtmosphere; + private const float DefaultLowPressure = Atmospherics.OneAtmosphere; - public int Integrity = 3; public bool IsLowPressure => Air.Pressure <= TankLowPressure; - [DataField] - public SoundSpecifier RuptureSound = new SoundPathSpecifier("/Audio/Effects/spray.ogg"); - [DataField] public SoundSpecifier? ConnectSound = new SoundPathSpecifier("/Audio/Effects/internals.ogg") @@ -32,27 +26,12 @@ public sealed partial class GasTankComponent : Component, IGasMixtureHolder public EntityUid? ConnectStream; public EntityUid? DisconnectStream; - [DataField] - public GasMixture Air { get; set; } = new(); - /// /// Pressure at which tank should be considered 'low' such as for internals. /// [DataField] public float TankLowPressure = DefaultLowPressure; - /// - /// Distributed pressure. - /// - [DataField, AutoNetworkedField] - public float OutputPressure = DefaultOutputPressure; - - /// - /// The maximum allowed output pressure. - /// - [DataField] - public float MaxOutputPressure = 3 * DefaultOutputPressure; - /// /// Tank is connected to internals. /// @@ -69,52 +48,9 @@ public sealed partial class GasTankComponent : Component, IGasMixtureHolder [ViewVariables] public bool CheckUser; - /// - /// Pressure at which tanks start leaking. - /// - [DataField] - public float TankLeakPressure = 30 * Atmospherics.OneAtmosphere; - - /// - /// Pressure at which tank spills all contents into atmosphere. - /// - [DataField] - public float TankRupturePressure = 40 * Atmospherics.OneAtmosphere; - - /// - /// Base 3x3 explosion. - /// - [DataField] - public float TankFragmentPressure = 50 * Atmospherics.OneAtmosphere; - - /// - /// Increases explosion for each scale kPa above threshold. - /// - [DataField] - public float TankFragmentScale = 2.25f * Atmospherics.OneAtmosphere; - [DataField] public EntProtoId ToggleAction = "ActionToggleInternals"; [DataField, AutoNetworkedField] public EntityUid? ToggleActionEntity; - - /// - /// Valve to release gas from tank - /// - [DataField, AutoNetworkedField] - public bool IsValveOpen; - - /// - /// Gas release rate in L/s - /// - [DataField, AutoNetworkedField] - public float ValveOutputRate = 100f; - - [DataField] - public SoundSpecifier ValveSound = - new SoundCollectionSpecifier("valveSqueak") - { - Params = AudioParams.Default.WithVolume(-5f), - }; } diff --git a/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs b/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs new file mode 100644 index 0000000000..e05d80c96c --- /dev/null +++ b/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs @@ -0,0 +1,44 @@ +using Robust.Shared.Audio; + +namespace Content.Shared.Atmos.Components; + +/// +/// This is an interface meant to designate an atmos device which can hold, a maximum pressurized volume of gas. +/// This device may fail if it exceeds the maximum pressure for too long. +/// +public interface IGasMaxPressureHolder : IGasMixtureHolder +{ + /// + /// Sound made when this device is destroyed from its reaching 0. + /// + SoundSpecifier? RuptureSound { get; set; } + + /// + /// Maximum pressure at which this atmos device will activate any emergency safety features, if it has any. + /// + float SafetyPressure { get; set; } + + /// + /// Maximum pressure this device can handle before it starts losing . + /// + float Overpressure { get; set; } + + /// + /// Popup alert for when this entity's pressure exceeds max pressure. + /// + LocId? SafetyAlert { get; set; } + + /// + /// How many over-pressures until this gas tank detonates. + /// An overpressure is defined as pressure exceeding + /// This determines the maximum value + /// + float MaxIntegrity { get; set; } + + /// + /// How many over-pressures until this gas tank detonates. + /// An overpressure is defined as pressure exceeding + /// This determines the current value + /// + float Integrity { get; set; } +} diff --git a/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs b/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs new file mode 100644 index 0000000000..31e6a4f6c4 --- /dev/null +++ b/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs @@ -0,0 +1,145 @@ +using Content.Shared.Atmos.Components; +using Content.Shared.CCVar; +using Content.Shared.Destructible; +using Content.Shared.Explosion.EntitySystems; +using Content.Shared.Jittering; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; +using Robust.Shared.Serialization; + +namespace Content.Shared.Atmos.EntitySystems; + +/// +/// This handles gas volumes that have a maximum pressure, and the destructive results of them exceeding that pressure. +/// You may call it the "MaxCapSystem" if you so desire. +/// +public abstract class GasMaxPressureSystem : EntitySystem where T : IGasMaxPressureHolder, IComponent +{ + private float _maxExplosivePower; + + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; + [Dependency] protected readonly SharedAtmosphereSystem Atmos = default!; + [Dependency] private readonly SharedDestructibleSystem _destructible = default!; + [Dependency] private readonly SharedExplosionSystem _explosions = default!; + [Dependency] protected readonly SharedAudioSystem Audio = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDeviceUpdated); + + Subs.CVar(_cfg, CCVars.AtmosTankFragment, value => _maxExplosivePower = value, true); + } + + private void OnDeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + // We don't update our atmos device if it's in the process of being deleted. + if (CheckStatus(entity, args.dt)) + DeviceUpdated(entity, ref args); + } + + /// + /// Handler for our atmos device being updated. + /// + /// Gas holding atmos device. + /// + protected abstract void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args); + + /// + /// Handler for when this atmos device is about to break due to exceeding its maximum pressure too many times + /// + /// Gas holding atmos device. + protected virtual void BeforeDeviceFailure(Entity entity) + { + + } + + /// + /// Handler for when this atmos device loses integrity due to overpressure + /// + /// Gas holding atmos device. + protected virtual void AfterDeviceFailure(Entity entity) + { + + } + + /// + /// Handler for when this atmos device loses integrity due to overpressure + /// + /// Gas holding atmos device. + protected virtual void IntegrityLost(Entity entity) + { + + } + + /// + /// Handler for when this atmos device exceeds its safety parameters + /// + /// Gas holding atmos device. + protected virtual void SafetyMeasures(Entity entity) + { + + } + + /// + /// Checks the status of an atmos device that has a specified max pressure, and handles overpressure issues. + /// + /// Gas holding atmos device. + /// Time since the last status update. + /// True if the device hasn't failed. False if the device has failed and been destroyed. + protected bool CheckStatus(Entity entity, float dt) + { + var pressure = entity.Comp.Air.Pressure; + + // Better mixes mean bigger and faster explosions! + if (pressure > entity.Comp.Overpressure * (entity.Comp.Integrity + 1)) + { + Atmos.MergeContainingMixture(entity.Owner, entity.Comp.Air, excite: true); + Audio.PlayPvs(entity.Comp.RuptureSound, Transform(entity).Coordinates, AudioParams.Default.WithVariation(0.125f)); + + // Integrity failure, destroy ourselves! + _destructible.DestroyEntity(entity); + + var totalIntensity = (float)Math.Sqrt(Atmos.GetOverPressure(entity.Comp.Air)); + if (_maxExplosivePower > 0 && _maxExplosivePower < totalIntensity) + totalIntensity = _maxExplosivePower; + + _explosions.TriggerExplosive(entity, totalIntensity: totalIntensity); + + Dirty(entity); + return false; + } + + // Device begins to fail. + if (pressure > entity.Comp.Overpressure) + { + IntegrityLost(entity); + entity.Comp.Integrity -= dt; + Appearance.SetData(entity.Owner, GasIntegrity.Integrity, entity.Comp.Integrity); + Appearance.SetData(entity.Owner, GasIntegrity.MaxIntegrity, entity.Comp.MaxIntegrity); + } + else if (entity.Comp.Integrity < entity.Comp.MaxIntegrity) + { + entity.Comp.Integrity = Math.Min(entity.Comp.Integrity + dt, entity.Comp.MaxIntegrity); + Appearance.SetData(entity.Owner, GasIntegrity.Integrity, entity.Comp.Integrity); + Appearance.SetData(entity.Owner, GasIntegrity.MaxIntegrity, entity.Comp.MaxIntegrity); + } + + // Device tries to prevent failure. + if (pressure > entity.Comp.SafetyPressure) + SafetyMeasures(entity); + + return true; + } +} + +[Serializable, NetSerializable, Flags] +public enum GasIntegrity +{ + Integrity, + MaxIntegrity +} diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs index 3852c30974..5b953e6df4 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs @@ -86,4 +86,19 @@ public abstract partial class SharedAtmosphereSystem return ev.Handled; } + + /// + /// Gets the potential energy from overpressure between two gas mixtures. + /// + /// + /// Returns the potential energy of the overpressure in Joules. + /// Value will be positive if the potential energy is outward (mix1 -> mix2) + /// Value will be negative if potential energy is inward (mix2 -> mix1) + /// + [PublicAPI] + public float GetOverPressure(GasMixture mix1, GasMixture? mix2 = null) + { + return (mix1.Pressure - (mix2?.Pressure ?? 0)) * mix1.Volume; + } + } diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs index c7267393dd..240711f048 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs @@ -16,10 +16,18 @@ public abstract partial class SharedAtmosphereSystem */ /// - /// Cached array of gas specific heats. + /// Cached array of molar heat capacities of the gases. /// - public float[] GasSpecificHeats => _gasSpecificHeats; - private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases]; + public float[] GasMolarHeatCapacities => _gasMolarHeatCapacities; + + private float[] _gasMolarHeatCapacities = new float[Atmospherics.AdjustedNumberOfGases]; + + /// + /// Cached array of gas specific mols + /// + public float[] GasMolarMasses => _gasMolarMasses; + + private float[] _gasMolarMasses = new float[Atmospherics.AdjustedNumberOfGases]; /// /// Mask used to determine if a gas is flammable or not. @@ -69,8 +77,6 @@ public abstract partial class SharedAtmosphereSystem GasReagents[idx] = gasPrototype.Reagent; } - Array.Resize(ref _gasSpecificHeats, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4)); - for (var i = 0; i < GasPrototypes.Length; i++) { /* @@ -81,7 +87,8 @@ public abstract partial class SharedAtmosphereSystem If you would like the unscaled specific heat, you'd need to multiply by HeatScale again. TODO ATMOS: please just make this 2 separate arrays instead of invoking multiplication every time. */ - _gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat / HeatScale; + _gasMolarHeatCapacities[i] = GasPrototypes[i].MolarHeatCapacity / HeatScale; + _gasMolarMasses[i] = GasPrototypes[i].MolarMass; // """Mask""" built here. Used to determine if a gas is fuel/oxidizer or not decently quickly and clearly. GasFuelMask[i] = GasPrototypes[i].IsFuel ? 1 : 0; @@ -232,6 +239,244 @@ public abstract partial class SharedAtmosphereSystem return GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable); } + /// + /// Gets the mass of a given + /// + /// The in question + /// Returns the volume in kilograms. + [PublicAPI] + public abstract float GetMass(GasMixture mix); + + /// + [PublicAPI] + public abstract float GetMass(float[] moles); + + /// + /// Calculates the amount of volume transferred from one gas mixture to another over time based on flow rate. + /// + /// + /// A + /// Another + /// The area of transfer, in square meters. One tile of movement is about one square meter. + /// delta time, or how much time is passing/has passed. + /// Discharge coefficient. An abstract modifier for friction and turbulence. + /// + /// The volume of gas being moved over dt in Litres. + /// If the value is positive it's in the direction of mix1->mix2, + /// If it's negative it's in the direction of mix2 -> mix1 + /// + /// I'm assuming C is always 1 because I'm lazy, you can precalculate it and pass it with the area if you really care. + [PublicAPI] + public double GetFlowVolume(GasMixture mix1, GasMixture mix2, float area, float dt, float c = 1f) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dt); + return dt * GetFlowRate(mix1, mix2, area, c); + } + + /// + [PublicAPI] + public double GetFlowVolume(GasMixture mix1, float deltaP, float area, float dt, float c = 1f) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dt); + return dt * GetFlowRate(mix1, deltaP, area, c); + } + + /// + /// Calculates the volumetric flow rate between two gas mixtures. + /// + /// A + /// Another + /// The area of transfer, in square meters. One tile of movement is about one square meter. + /// Discharge coefficient. An abstract modifier for friction and turbulence. + /// + /// The volume of gas being moved in Litres / Second. + /// If the value is positive it's in the direction of mix1->mix2, + /// If it's negative it's in the direction of mix2 -> mix1 + /// + /// I'm assuming C is always 1 because I'm lazy, you can precalculate it and pass it with the area if you really care. + [PublicAPI] + public double GetFlowRate(GasMixture mix1, GasMixture mix2, float area, float c = 1f) + { + /* + Q = C × A × √(2 × ΔP / ρ) + Q is the volumetric airflow rate + C is the discharge coefficient + A is the cross-sectional area + ΔP is the measured pressure difference + ρ is the air density, adjusted for environmental conditions. + We can break this up into Q = A × V where V is the velocity of the gas. + */ + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(area); + return area * GetFlowVelocity(mix1, mix2, c); + } + + /// + [PublicAPI] + public double GetFlowRate(GasMixture mix1, float deltaP, float area, float c = 1f) + { + /* + Q = C × A × √(2 × ΔP / ρ) + Q is the volumetric airflow rate + C is the discharge coefficient + A is the cross-sectional area + ΔP is the measured pressure difference + ρ is the air density, adjusted for environmental conditions. + We can break this up into Q = A × V where V is the velocity of the gas. + */ + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(area); + return area * GetFlowVelocity(mix1, deltaP, c); + } + + /// + /// Calculates the flow velocity between two gas mixtures using Q = C × A × √(2 × ΔP / ρ) but without the A (area) + /// Useful for determining flow rate, or how fast a gas is moving. + /// + /// A + /// Another + /// Discharge coefficient. An abstract modifier for friction and turbulence. + /// + /// The velocity of gas movement between two mixtures in Meters / Second. + /// If the value is positive it's in the direction of mix1->mix2, + /// If it's negative it's in the direction of mix2 -> mix1 + /// + [PublicAPI] + public double GetFlowVelocity(GasMixture mix1, GasMixture mix2, float c = 1f) + { + if (mix1.Pressure > mix2.Pressure) + return GetFlowVelocity(mix1, mix1.Pressure - mix2.Pressure, c); + + return -GetFlowVelocity(mix2, mix2.Pressure - mix1.Pressure, c); + } + + /// + /// Calculates the flow velocity between a gas mixture given a pressure differential. + /// + /// The mixture which is being allowed to flow + /// The difference in pressure between this mixture and where it's flowing to + /// Discharge coefficient. An abstract modifier for friction and turbulence. + /// + /// The velocity of the gas leaving our mixture in Meters / Second. + /// + [PublicAPI] + public double GetFlowVelocity(GasMixture mix1, float deltaP, float c = 1f) + { + /* + V = C × √(2 × ΔP / ρ) + V is the velocity of our gas + C is the discharge coefficient + ΔP is the measured pressure difference + ρ is the air density, adjusted for environmental conditions. + Density is equivalent to Mass / Volume, so we invert that to divide by density. + */ + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(deltaP); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(c); + return c * Math.Sqrt(2 * deltaP * mix1.Volume / GetMass(mix1)); + } + + /// + /// Lets a volume of gas flow throw a constrained area into another volume of gas over a period of time. + /// + /// Gas volume that is discharging some of its gas. + /// Gas volume that is receiving the discharge. + /// Time that the discharge occurs in seconds, should be as small as possible since it doesn't use calculus + /// Area that our gas is traveling through in m^2, the larger the area the bigger the transfer. + /// Default of 2m^2 since that's the area of a single face of an atmos tile. + [PublicAPI] + public void FlowGas(GasMixture mixture, GasMixture? output, float dt, float area) + { + FlowGas(mixture, output, mixture.Pressure, dt, area); + } + + /// + [PublicAPI] + public void FlowGas(GasMixture mixture, GasMixture? output, float pressure, float dt, float area) + { + if (output == null) + { + FlowGas(mixture, pressure, dt, area); + return; + } + + pressure = Math.Min(pressure, mixture.Pressure - output.Pressure); + var removed = FlowGas(mixture, pressure, dt, area); + + if (removed == null) + return; + + Merge(output, removed); + } + + /// + /// Lets a volume of gas flow through constrained area at a constrained pressure delta. + /// + /// Mixture of gas that is currently flowing + /// Pressure our gas is able to flow at. + /// Time that the discharge occurs in seconds, should be as small as possible since it doesn't use calculus + /// Area that our gas is traveling through in m^2, the larger the area the bigger the transfer. + /// Default of 2m^2 since that's the area of a single face of an atmos tile. + /// + [PublicAPI] + public GasMixture? FlowGas(GasMixture mixture, float deltaP, float dt, float area = 2f) + { + if (deltaP <= 0) + return null; + + return ReleaseGasAt(mixture, (float)GetFlowVolume(mixture, deltaP, area, dt), mixture.Pressure); + } + + /// + /// Releases some volume of a gas mixture at a specified pressure. + /// + /// Mixture which is releasing gas. + /// Optional Mixture to receive gas + /// Volume we are releasing + /// Pressure of the released volume. + [PublicAPI] + public void ReleaseGasAt(GasMixture mixture, GasMixture? output, float volume, float targetPressure) + { + if (output == null) + { + ReleaseGasAt(mixture, volume, targetPressure); + return; + } + + targetPressure = Math.Min(targetPressure, mixture.Pressure - output.Pressure); + + if (targetPressure <= 0) + return; + + var molesNeeded = Math.Min(targetPressure * volume / (Atmospherics.R * mixture.Temperature), + MolesToEqualizePressure(mixture, output)); + + var removed = mixture.Remove(molesNeeded); + + Merge(mixture, removed); + } + + /// + [PublicAPI] + public GasMixture? ReleaseGasAt(GasMixture mixture, float volume, float targetPressure) + { + if (targetPressure <= 0) + return null; + + targetPressure = Math.Min(targetPressure, mixture.Pressure); + + return RemoveVolumeAtPressure(mixture, volume, targetPressure); + } + + /// + /// Removes a specified volume of gas from a mixture, at a specific pressure. + /// + /// mixture of gas + /// volume we're attempting to remove + /// pressure that volume will be removed at. + public GasMixture RemoveVolumeAtPressure(GasMixture mixture, float volume, float pressure) + { + var molesNeeded = pressure * volume / (Atmospherics.R * mixture.Temperature); + return mixture.Remove(molesNeeded); + } + /// /// Gets the heat capacity for a . /// @@ -242,4 +487,219 @@ public abstract partial class SharedAtmosphereSystem /// The heat capacity of the . [MethodImpl(MethodImplOptions.AggressiveInlining)] protected abstract float GetHeatCapacityCalculation(float[] moles, bool space); + + + /// + /// Calculates the moles that must be transferred from + /// to to equalize pressure. + /// + public float MolesToEqualizePressure(GasMixture gasMixture1, GasMixture gasMixture2) + { + return gasMixture1.TotalMoles * FractionToEqualizePressure(gasMixture1, gasMixture2); + } + + /// + /// Calculates the dimensionless fraction of gas required to equalize pressure between two gas mixtures. + /// + /// The first gas mixture involved in the pressure equalization. + /// This mixture should be the one you always expect to be the highest pressure. + /// The second gas mixture involved in the pressure equalization. + /// A float (from 0 to 1) representing the dimensionless fraction of gas that needs to be transferred from the + /// mixture of higher pressure to the mixture of lower pressure. + /// + /// + /// This properly takes into account the effect + /// of gas merging from inlet to outlet affecting the temperature + /// (and possibly increasing the pressure) in the outlet. + /// + /// + /// The gas is assumed to expand freely, + /// so the temperature of the gas with the greater pressure is not changing. + /// + /// + /// + /// If you want to calculate the moles required to equalize pressure between an inlet and an outlet, + /// multiply the fraction returned by the source moles. + /// + public float FractionToEqualizePressure(GasMixture gasMixture1, GasMixture gasMixture2) + { + /* + Problem: the gas being merged from the inlet to the outlet could affect the + temp. of the gas and cause a pressure rise. + We want the pressure to be equalized, so we have to account for this. + + For clarity, let's assume that gasMixture1 is the inlet and gasMixture2 is the outlet. + + We require mechanical equilibrium, so \( P_1' = P_2' \) + + Before the transfer, we have: + \( P_1 = \frac{n_1 R T_1}{V_1} \) + \( P_2 = \frac{n_2 R T_2}{V_2} \) + + After removing fraction \( x \) moles from the inlet, we have: + \( P_1' = \frac{(1 - x) n_1 R T_1}{V_1} \) + + The outlet will gain the same \( x n_1 \) moles of gas. + So \( n_2' = n_2 + x n_1 \) + + After mixing, the outlet temperature will be changed. + Denote the new mixture temperature as \( T_2' \). + Volume is constant. + So we have: + \( P_2' = \frac{(n_2 + x n_1) R T_2}{V_2} \) + + The total energy of the incoming inlet to outlet gas at \( T_1 \) plus the existing energy of the outlet gas at \( T_2 \) + will be equal to the energy of the new outlet gas at \( T_2' \). + This leads to the following derivation: + \( x n_1 C_1 T_1 + n_2 C_2 T_2 = (x n_1 C_1 + n_2 C_2) T_2' \) + + Where \( C_1 \) and \( C_2 \) are the heat capacities of the inlet and outlet gases, respectively. + + Solving for \( T_2' \) gives us: + \( T_2' = \frac{x n_1 C_1 T_1 + n_2 C_2 T_2}{x n_1 C_1 + n_2 C_2} \) + + Once again, we require mechanical equilibrium (\( P_1' = P_2' \)), + so we can substitute \( T_2' \) into the pressure equation: + + \( \frac{(1 - x) n_1 R T_1}{V_1} = + \frac{(n_2 + x n_1) R}{V_2} \cdot + \frac{x n_1 C_1 T_1 + n_2 C_2 T_2} + {x n_1 C_1 + n_2 C_2} \) + + Now it's a matter of solving for \( x \). + Not going to show the full derivation here, just steps. + 1. Cancel common factor \( R \). + 2. Multiply both sides by \( x n_1 C_1 + n_2 C_2 \), so that everything + becomes a polynomial in terms of \( x \). + 3. Expand both sides. + 4. Collect like powers of \( x \). + 5. After collecting, you should end up with a polynomial of the form: + + \( (-n_1 C_1 T_1 (1 + \frac{V_2}{V_1})) x^2 + + (n_1 T_1 \frac{V_2}{V_1} (C_1 - C_2) - n_2 C_1 T_1 - n_1 C_2 T_2) x + + (n_1 T_1 \frac{V_2}{V_1} C_2 - n_2 C_2 T_2) = 0 \) + + Divide through by \( n_1 C_1 T_1 \) and replace each ratio with a symbol for clarity: + \( k_V = \frac{V_2}{V_1} \) + \( k_n = \frac{n_2}{n_1} \) + \( k_T = \frac{T_2}{T_1} \) + \( k_C = \frac{C_2}{C_1} \) + */ + + // Ensure that P_1 > P_2 so the quadratic works out. + if (gasMixture1.Pressure < gasMixture2.Pressure) + { + (gasMixture1, gasMixture2) = (gasMixture2, gasMixture1); + } + + // Establish the dimensionless ratios. + var volumeRatio = gasMixture2.Volume / gasMixture1.Volume; + var molesRatio = gasMixture2.TotalMoles / gasMixture1.TotalMoles; + var temperatureRatio = gasMixture2.Temperature / gasMixture1.Temperature; + var heatCapacityRatio = GetHeatCapacity(gasMixture2) / GetHeatCapacity(gasMixture1); + + // The quadratic equation is solved for the transfer fraction. + var quadraticA = 1 + volumeRatio; + var quadraticB = molesRatio - volumeRatio + heatCapacityRatio * (temperatureRatio + volumeRatio); + var quadraticC = heatCapacityRatio * (molesRatio * temperatureRatio - volumeRatio); + + return (-quadraticB + MathF.Sqrt(quadraticB * quadraticB - 4 * quadraticA * quadraticC)) / (2 * quadraticA); + } + + /// + /// Determines the fraction of gas to be removed and transferred from a source + /// to a target to reach a target pressure + /// in the target . + /// + /// The source that gas will be removed from. + /// This should always be of higher pressure than the second . + /// The target that will increase in pressure + /// to the target pressure. + /// The target mixture's desired pressure to target. + /// A float representing the dimensionless fraction of gas to transfer from the source + /// to the target. This may return negative if you have your mixtures swapped. + /// Note that this method doesn't take into account the heat capacity of the + /// transferred volume causing a pressure rise in the target . + [PublicAPI] + public static float FractionToMaxPressure(GasMixture mix1, GasMixture mix2, float targetPressure) + { + var molesToTransfer = MolesToMaxPressure(mix1, mix2, targetPressure); + return molesToTransfer / mix1.TotalMoles; + } + + /// + /// Determines the number of moles to be removed and transferred from a source + /// to a target to reach a target pressure + /// in the target . + /// + /// The source that gas will be removed from. + /// This should always be of higher pressure than the second . + /// The target that will increase in pressure + /// to the target pressure. + /// The target mixture's desired pressure to target. + /// The difference in moles required to reach the target pressure. + /// Note that this method doesn't take into account the heat capacity of the + /// transferred volume causing a pressure rise in the target . + [PublicAPI] + public static float MolesToMaxPressure(GasMixture mix1, GasMixture mix2, float targetPressure) + { + /* + Calculate the moles required to reach the target pressure. + The formula is derived from the ideal gas law and the + general Richman's law, under the simplification that all the specific heat capacities are equal. + Derivation can also be seen at + https://github.com/space-wizards/space-station-14/pull/35211/files/a0ae787fe07a4e792570f55b49d9dd8038eb6e4d#r1961183456 + TODO ATMOS Make this properly obey the heat capacity change on the target mixture. + + Derivation is as follows. + Assume A is mix1, B is mix2, C is the combined mixture after transfer. + We can express the number of moles in C: + n_C = n_A + n_B + + We can then determine the temperature of C: + T_C = \frac{T_A n_A c_A + T_B n_B c_B}{n_A c_A + n_B c_B} + + Where c_A and c_B are the specific heats of mixtures A and B, respectively. + We can then express the pressure of C: + P_C = \frac{n_C R T_C}{V_C} + + Using the above equations, we can express P_C as follows: + P_C = \frac{(n_A + n_B) R (\frac{T_a n_A + T_B n_B}{n_A + n_B}}{V_C} + + Which can be reduced to: + P_C = \frac{R (T_A n_A + T_B n_B)}{V_C} + + Solving for n_A gives: + n_A = \frac{P_C V_C - R T_B n_B}{R T_A} + + Using the ideal gas law to substitute: + n_A = \frac{P_C V_C - P_B V_B}{R T_A} + + The output volume doesn't change: + V_B = V_C + + So: + n_A = \frac{(P_C - P_B) V_B}{R T_A} + */ + + var delta = targetPressure - mix2.Pressure; + var requiredMoles = (delta * mix2.Volume) / (mix1.Temperature * Atmospherics.R); + + // Return the fraction of moles to transfer. + return requiredMoles; + } + + /// + /// Determines the number of moles that need to be removed from a to reach a target pressure threshold. + /// + /// The gas mixture whose moles and properties will be used in the calculation. + /// The target pressure threshold to calculate against. + /// The difference in moles required to reach the target pressure threshold. + /// The temperature of the gas is assumed to be not changing due to a free expansion. + public static float MolesToPressureThreshold(GasMixture gasMixture, float targetPressure) + { + // Kid named PV = nRT. + return gasMixture.TotalMoles - + targetPressure * gasMixture.Volume / (Atmospherics.R * gasMixture.Temperature); + } } diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.cs index 04f0221994..47e3219efc 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.cs @@ -14,7 +14,7 @@ public abstract partial class SharedAtmosphereSystem : EntitySystem [Dependency] private readonly SharedInternalsSystem _internals = default!; [Dependency] protected readonly SharedTransformSystem XformSystem = default!; - private EntityQuery _internalsQuery; + [Dependency] private readonly EntityQuery _internalsQuery = default!; /// /// The length to pre-allocate list/dicts of delta pressure entities on a . @@ -25,8 +25,6 @@ public abstract partial class SharedAtmosphereSystem : EntitySystem { base.Initialize(); - _internalsQuery = GetEntityQuery(); - InitializeBreathTool(); InitializeGases(); InitializeCVars(); diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs index 4158c687bd..ffb7d9f865 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs @@ -6,16 +6,14 @@ using Content.Shared.Timing; using Content.Shared.Toggleable; using Content.Shared.UserInterface; using Content.Shared.Verbs; -using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using InternalsComponent = Content.Shared.Body.Components.InternalsComponent; namespace Content.Shared.Atmos.EntitySystems; -public abstract class SharedGasTankSystem : EntitySystem +public abstract class SharedGasTankSystem : GasMaxPressureSystem { [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly SharedInternalsSystem _internals = default!; [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; @@ -48,9 +46,9 @@ public abstract class SharedGasTankSystem : EntitySystem private void OnGasTankSetPressure(Entity ent, ref GasTankSetPressureMessage args) { - var pressure = Math.Clamp(args.Pressure, 0f, ent.Comp.MaxOutputPressure); + var pressure = Math.Clamp(args.Pressure, 0f, ent.Comp.MaxReleasePressure); - ent.Comp.OutputPressure = pressure; + ent.Comp.ReleasePressure = pressure; Dirty(ent); UpdateUserInterface(ent); } @@ -81,7 +79,7 @@ public abstract class SharedGasTankSystem : EntitySystem if (component.IsConnected) args.PushMarkup(Loc.GetString("comp-gas-tank-connected")); - args.PushMarkup(Loc.GetString(component.IsValveOpen ? "comp-gas-tank-examine-open-valve" : "comp-gas-tank-examine-closed-valve")); + args.PushMarkup(Loc.GetString(component.ReleaseValveOpen ? "comp-gas-tank-examine-open-valve" : "comp-gas-tank-examine-closed-valve")); } private void OnActionToggle(Entity gasTank, ref ToggleActionEvent args) @@ -93,28 +91,46 @@ public abstract class SharedGasTankSystem : EntitySystem args.Handled = true; } - private void OnGetAlternativeVerb(EntityUid uid, GasTankComponent component, GetVerbsEvent args) + private void OnGetAlternativeVerb(Entity entity, ref GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; + var user = args.User; args.Verbs.Add(new AlternativeVerb() { - Text = component.IsValveOpen ? Loc.GetString("comp-gas-tank-close-valve") : Loc.GetString("comp-gas-tank-open-valve"), + Text = entity.Comp.ReleaseValveOpen ? Loc.GetString("comp-gas-tank-close-valve") : Loc.GetString("comp-gas-tank-open-valve"), Act = () => { - component.IsValveOpen = !component.IsValveOpen; - _audio.PlayPredicted(component.ValveSound, uid, args.User); - Dirty(uid, component); + ToggleValve(entity, user: user); }, - Disabled = component.IsConnected, + Disabled = entity.Comp.IsConnected, }); } + /// > + public void ToggleValve(Entity entity, EntityUid? user = null) + { + ToggleValve(entity, !entity.Comp.ReleaseValveOpen, user); + } + + /// + /// Toggles the release valve for this open or closed + /// + /// Entity whose valve we're toggling + /// Whether we're opening or closing the valve + /// Optional user who is performing the action. + public void ToggleValve(Entity entity, bool open, EntityUid? user = null) + { + entity.Comp.ReleaseValveOpen = open; + Audio.PlayPredicted(entity.Comp.ValveSound, entity, user); + Dirty(entity); + } + public bool CanConnectToInternals(Entity ent) { TryGetInternalsComp(ent, out _, out var internalsComp, ent.Comp.User); - return internalsComp != null && internalsComp.BreathTools.Count != 0 && !ent.Comp.IsValveOpen; + return internalsComp != null && internalsComp.BreathTools.Count != 0 && !ent.Comp.ReleaseValveOpen; } public bool ConnectToInternals(Entity ent, EntityUid? user = null) @@ -141,8 +157,8 @@ public abstract class SharedGasTankSystem : EntitySystem if (!component.IsConnected) return false; - component.DisconnectStream = _audio.Stop(component.DisconnectStream); - component.ConnectStream = _audio.PlayPredicted(component.ConnectSound, owner, user)?.Entity; + component.DisconnectStream = Audio.Stop(component.DisconnectStream); + component.ConnectStream = Audio.PlayPredicted(component.ConnectSound, owner, user)?.Entity; UpdateUserInterface(ent); return true; } @@ -210,8 +226,8 @@ public abstract class SharedGasTankSystem : EntitySystem if (internalsUid != null && internalsComp != null) _internals.DisconnectTank((internalsUid.Value, internalsComp), forced: forced); - component.ConnectStream = _audio.Stop(component.ConnectStream); - component.DisconnectStream = _audio.PlayPredicted(component.DisconnectSound, owner, user)?.Entity; + component.ConnectStream = Audio.Stop(component.ConnectStream); + component.DisconnectStream = Audio.PlayPredicted(component.DisconnectSound, owner, user)?.Entity; UpdateUserInterface(ent); return true; } diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs index 23ea8fa8fa..e9c697c53c 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs @@ -31,8 +31,7 @@ public abstract class SharedGasTileOverlaySystem : EntitySystem for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) { var gasPrototype = _atmosphere.GetGas(i); - if (!string.IsNullOrEmpty(gasPrototype.GasOverlayTexture) || - (!string.IsNullOrEmpty(gasPrototype.GasOverlaySprite) && !string.IsNullOrEmpty(gasPrototype.GasOverlayState))) + if (gasPrototype.GasOverlaySprite != null) visibleGases.Add(i); } VisibleGasId = visibleGases.ToArray(); diff --git a/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs b/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs index 40d76684ee..2c19638ee0 100644 --- a/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs +++ b/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs @@ -1,13 +1,12 @@ -using Content.Shared.Atmos.Piping.Binary.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Guidebook; -using Robust.Shared.Audio; using Robust.Shared.GameStates; namespace Content.Shared.Atmos.Piping.Unary.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] -public sealed partial class GasCanisterComponent : Component, IGasMixtureHolder +public sealed partial class GasCanisterComponent : GasMaxPressureHolderComponent { [DataField("port")] public string PortName { get; set; } = "port"; @@ -21,38 +20,18 @@ public sealed partial class GasCanisterComponent : Component, IGasMixtureHolder [DataField] public ItemSlot GasTankSlot = new(); - [DataField("gasMixture")] - public GasMixture Air { get; set; } = new(); + /// + /// The safety release valve on this gas canister. Automatically opens + /// when is reached. + /// + [DataField] + public bool SafetyValveOpen; /// /// Last recorded pressure, for appearance-updating purposes. /// public float LastPressure = 0f; - /// - /// Minimum release pressure possible for the release valve. - /// - [DataField, AutoNetworkedField] - public float MinReleasePressure = Atmospherics.OneAtmosphere / 10; - - /// - /// Maximum release pressure possible for the release valve. - /// - [DataField, AutoNetworkedField] - public float MaxReleasePressure = Atmospherics.OneAtmosphere * 10; - - /// - /// Valve release pressure. - /// - [DataField, AutoNetworkedField] - public float ReleasePressure = Atmospherics.OneAtmosphere; - - /// - /// Whether the release valve is open on the canister. - /// - [DataField, AutoNetworkedField] - public bool ReleaseValve = false; - [GuidebookData] public float Volume => Air.Volume; } diff --git a/Content.Shared/Atmos/Piping/Unary/Systems/SharedGasCanisterSystem.cs b/Content.Shared/Atmos/Piping/Unary/Systems/SharedGasCanisterSystem.cs index a7562689ca..8fed3f0eb7 100644 --- a/Content.Shared/Atmos/Piping/Unary/Systems/SharedGasCanisterSystem.cs +++ b/Content.Shared/Atmos/Piping/Unary/Systems/SharedGasCanisterSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Administration.Logs; using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; @@ -9,7 +10,7 @@ using GasCanisterComponent = Content.Shared.Atmos.Piping.Unary.Components.GasCan namespace Content.Shared.Atmos.Piping.Unary.Systems; -public abstract class SharedGasCanisterSystem : EntitySystem +public abstract class SharedGasCanisterSystem : GasMaxPressureSystem { [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; [Dependency] private readonly ItemSlotsSystem _slots = default!; @@ -73,7 +74,7 @@ public abstract class SharedGasCanisterSystem : EntitySystem var item = canister.GasTankSlot.Item; _slots.TryEjectToHands(uid, canister.GasTankSlot, args.Actor, excludeUserAudio: true); - if (canister.ReleaseValve) + if (canister.ReleaseValveOpen) { AdminLogger.Add(LogType.CanisterTankEjected, LogImpact.High, $"Player {ToPrettyString(args.Actor):player} ejected tank {ToPrettyString(item):tank} from {ToPrettyString(uid):canister} while the valve was open, releasing [{GetContainedGasesString((uid, canister))}] to atmosphere"); } @@ -103,24 +104,35 @@ public abstract class SharedGasCanisterSystem : EntitySystem DirtyUI(uid, canister); } - private void OnCanisterChangeReleaseValve(EntityUid uid, GasCanisterComponent canister, GasCanisterChangeReleaseValveMessage args) + private void OnCanisterChangeReleaseValve(Entity entity, ref GasCanisterChangeReleaseValveMessage args) { // filling a jetpack with plasma is less important than filling a room with it - var impact = canister.GasTankSlot.HasItem ? LogImpact.Medium : LogImpact.High; + var impact = entity.Comp.GasTankSlot.HasItem ? LogImpact.Medium : LogImpact.High; var containedGasDict = new Dictionary(); var containedGasArray = Enum.GetValues(typeof(Gas)); for (var i = 0; i < containedGasArray.Length; i++) { - containedGasDict.Add((Gas)i, canister.Air[i]); + containedGasDict.Add((Gas)i, entity.Comp.Air[i]); } - AdminLogger.Add(LogType.CanisterValve, impact, $"{ToPrettyString(args.Actor):player} set the valve on {ToPrettyString(uid):canister} to {args.Valve:valveState} while it contained [{string.Join(", ", containedGasDict)}]"); + AdminLogger.Add(LogType.CanisterValve, impact, $"{ToPrettyString(args.Actor):player} set the valve on {ToPrettyString(entity):canister} to {args.Valve:valveState} while it contained [{string.Join(", ", containedGasDict)}]"); - canister.ReleaseValve = args.Valve; - Dirty(uid, canister); - DirtyUI(uid, canister); + ToggleValve(entity, args.Valve, args.Actor); + DirtyUI(entity); + } + + protected void ToggleValve(Entity entity, EntityUid? user = null) + { + ToggleValve(entity, !entity.Comp.ReleaseValveOpen, user); + } + + protected void ToggleValve(Entity entity, bool open, EntityUid? user = null) + { + entity.Comp.ReleaseValveOpen = open; + Audio.PlayPredicted(entity.Comp.ValveSound, entity, user); + Dirty(entity); } private void OnCanisterInsertAttempt(EntityUid uid, GasCanisterComponent component, ref ItemSlotInsertAttemptEvent args) @@ -129,7 +141,7 @@ public abstract class SharedGasCanisterSystem : EntitySystem return; // Could whitelist but we want to check if it's open so. - if (!TryComp(args.Item, out var gasTank) || gasTank.IsValveOpen) + if (!TryComp(args.Item, out var gasTank) || gasTank.ReleaseValveOpen) { args.Cancelled = true; } diff --git a/Content.Shared/Atmos/Prototypes/GasPrototype.cs b/Content.Shared/Atmos/Prototypes/GasPrototype.cs index 86304dfdda..bb92d0307d 100644 --- a/Content.Shared/Atmos/Prototypes/GasPrototype.cs +++ b/Content.Shared/Atmos/Prototypes/GasPrototype.cs @@ -1,103 +1,122 @@ -using Content.Shared.Chemistry.Reagent; +using Content.Shared.CCVar; +using Content.Shared.Chemistry.Reagent; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Utility; -namespace Content.Shared.Atmos.Prototypes +namespace Content.Shared.Atmos.Prototypes; + +/// +/// Prototype defining a gas for atmospherics. +/// +/// +/// The total number of gases is hardcoded in a bunch of places. +/// If you add any new ones, make sure to also adjust the constants in accordingly. +/// +[Prototype] +public sealed partial class GasPrototype : IPrototype { - [Prototype] - public sealed partial class GasPrototype : IPrototype - { - [DataField("name")] public string Name { get; set; } = ""; + // TODO: Add interfaces for gas behaviours e.g. breathing, burning - // TODO: Control gas amount necessary for overlay to appear - // TODO: Add interfaces for gas behaviours e.g. breathing, burning + /// + [IdDataField] + public string ID { get; private set; } = default!; - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; + /// + /// The name of the gas as shown to the player. + /// + [DataField(required: true)] + public LocId Name; - /// - /// Specific heat for gas. - /// - [DataField("specificHeat")] - public float SpecificHeat { get; private set; } + /// + /// The abbreviation of the name. For example O₂ for Oxygen. + /// Used for UI purposes. + /// + [DataField(required: true)] + public LocId Abbreviation; - /// - /// Heat capacity ratio for gas - /// - [DataField("heatCapacityRatio")] - public float HeatCapacityRatio { get; private set; } = 1.4f; + /// + /// The molar heat capacity of this gas, in J/(K * mol). + /// Describes how much heat energy is needed to heat up this gas by one Kelvin. + /// Or in other words, the higher this number is the more energy this gas can store. + /// + /// + /// This will be divided by the cvar. + /// + [DataField] + public float MolarHeatCapacity; - /// - /// Molar mass of gas - /// - [DataField("molarMass")] - public float MolarMass { get; set; } = 1f; + /// + /// Heat capacity ratio for gas. + /// TODO: Make gas pumps do proper adiabatic compression so that this is actually used. + /// + [DataField] + public float HeatCapacityRatio = 1.4f; + + /// + /// Molar mass of the gas. + /// TODO: This is not used anywhere, do we even need this? + /// + [DataField] + public float MolarMass = 1f; - /// - /// Minimum amount of moles for this gas to be visible. - /// - [DataField("gasMolesVisible")] - public float GasMolesVisible { get; private set; } = 0.25f; + /// + /// Minimum amount of moles for this gas to be visible. + /// + [DataField] + public float GasMolesVisible = 0.25f; - /// - /// Visibility for this gas will be max after this value. - /// - public float GasMolesVisibleMax => GasMolesVisible * GasVisibilityFactor; + /// + /// Visibility for this gas will be max after this value. + /// + [ViewVariables] + public float GasMolesVisibleMax => GasMolesVisible * GasVisibilityFactor; - [DataField("gasVisbilityFactor")] - public float GasVisibilityFactor = Atmospherics.FactorGasVisibleMax; + /// + /// Multiplier that decides when a gas will be at maximum visibility. + /// + [DataField] + public float GasVisibilityFactor = Atmospherics.FactorGasVisibleMax; - /// - /// If this reagent is in gas form, this is the path to the overlay that will be used to make the gas visible. - /// - [DataField("gasOverlayTexture")] - public string GasOverlayTexture { get; private set; } = string.Empty; + /// + /// Sprite to show in the gas overlay if this gas is present on a tile. + /// If null the gas will be invisible. + /// + [DataField] + public SpriteSpecifier? GasOverlaySprite; - /// - /// If this reagent is in gas form, this will be the path to the RSI sprite that will be used to make the gas visible. - /// - [DataField("gasOverlayState")] - public string GasOverlayState { get; set; } = string.Empty; + /// + /// The reagent that this gas will turn into when inhaled or condensed. + /// + [DataField] + public ProtoId? Reagent; - /// - /// State for the gas RSI overlay. - /// - [DataField("gasOverlaySprite")] - public string GasOverlaySprite { get; set; } = string.Empty; + /// + /// The color of the gas used for UI purposes. + /// + [DataField] + public Color Color = Color.White; - /// - /// Path to the tile overlay used when this gas appears visible. - /// - [DataField("overlayPath")] - public string OverlayPath { get; private set; } = string.Empty; + /// + /// The price per mole when this gas is sold at cargo. + /// The final price will also depend on the purity of the gas mixture. + /// + [DataField] + public float PricePerMole = 0; - /// - /// The reagent that this gas will turn into when inhaled. - /// - [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string? Reagent { get; private set; } = default!; + /// + /// Whether the gas is considered to be flammable. + /// This is used generically across Atmospherics to determine + /// if things like hotspots are allowed to ignite if an + /// oxidizer is present. + /// + [DataField] + public bool IsFuel; - [DataField("color")] public string Color { get; private set; } = string.Empty; - - [DataField("pricePerMole")] - public float PricePerMole { get; set; } = 0; - - /// - /// Whether the gas is considered to be flammable. - /// This is used generically across Atmospherics to determine - /// if things like hotspots are allowed to ignite if an - /// oxidizer is present. - /// - [DataField] - public bool IsFuel; - - /// - /// Whether the gas is considered to be an oxidizer. - /// Same reasoning as but for oxidizers. - /// - [DataField] - public bool IsOxidizer; - } + /// + /// Whether the gas is considered to be an oxidizer. + /// Same reasoning as but for oxidizers. + /// + [DataField] + public bool IsOxidizer; } diff --git a/Content.Shared/Audio/SharedAmbientSoundSystem.cs b/Content.Shared/Audio/SharedAmbientSoundSystem.cs index 0a52b7c58e..fd5a474a70 100644 --- a/Content.Shared/Audio/SharedAmbientSoundSystem.cs +++ b/Content.Shared/Audio/SharedAmbientSoundSystem.cs @@ -6,15 +6,13 @@ namespace Content.Shared.Audio; public abstract class SharedAmbientSoundSystem : EntitySystem { - private EntityQuery _query; + [Dependency] private readonly EntityQuery _query = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(GetCompState); SubscribeLocalEvent(HandleCompState); - - _query = GetEntityQuery(); } public virtual void SetAmbience(EntityUid uid, bool value, AmbientSoundComponent? ambience = null) diff --git a/Content.Shared/Bed/BedSystem.cs b/Content.Shared/Bed/BedSystem.cs index 83abdbd7a8..a20156a79e 100644 --- a/Content.Shared/Bed/BedSystem.cs +++ b/Content.Shared/Bed/BedSystem.cs @@ -31,7 +31,7 @@ public sealed class BedSystem : EntitySystem [Dependency] private readonly SleepingSystem _sleepingSystem = default!; [Dependency] private readonly StandingStateSystem _standing = default!; // WL-Changes: fix: lying down after sleep on bed - private EntityQuery _sleepingQuery; + [Dependency] private readonly EntityQuery _sleepingQuery = default!; public override void Initialize() { @@ -47,8 +47,6 @@ public sealed class BedSystem : EntitySystem SubscribeLocalEvent(OnStasisEmagged); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnStasisGetMetabolicMultiplier); - - _sleepingQuery = GetEntityQuery(); } private void OnHealMapInit(Entity ent, ref MapInitEvent args) diff --git a/Content.Shared/Blocking/BlockingSystem.cs b/Content.Shared/Blocking/BlockingSystem.cs index 2e17485bde..d0e3bef52b 100644 --- a/Content.Shared/Blocking/BlockingSystem.cs +++ b/Content.Shared/Blocking/BlockingSystem.cs @@ -33,6 +33,11 @@ public sealed partial class BlockingSystem : EntitySystem [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly TurfSystem _turf = default!; + [Dependency] private readonly EntityQuery _blockQuery = default!; + [Dependency] private readonly EntityQuery _handQuery = default!; + [Dependency] private readonly EntityQuery _mobQuery = default!; + [Dependency] private readonly EntityQuery _userQuery = default!; + public override void Initialize() { base.Initialize(); @@ -91,10 +96,7 @@ public sealed partial class BlockingSystem : EntitySystem if (args.Handled) return; - var blockQuery = GetEntityQuery(); - var handQuery = GetEntityQuery(); - - if (!handQuery.TryGetComponent(args.Performer, out var hands)) + if (!_handQuery.TryGetComponent(args.Performer, out var hands)) return; var shields = _handsSystem.EnumerateHeld((args.Performer, hands)).ToArray(); @@ -104,7 +106,7 @@ public sealed partial class BlockingSystem : EntitySystem if (shield == uid) continue; - if (blockQuery.TryGetComponent(shield, out var otherBlockComp) && otherBlockComp.IsBlocking) + if (_blockQuery.TryGetComponent(shield, out var otherBlockComp) && otherBlockComp.IsBlocking) { CantBlockError(args.Performer); return; @@ -170,10 +172,9 @@ public sealed partial class BlockingSystem : EntitySystem if (playerTileRef != null) { var intersecting = _lookup.GetLocalEntitiesIntersecting(playerTileRef.Value, 0f); - var mobQuery = GetEntityQuery(); foreach (var uid in intersecting) { - if (uid != user && mobQuery.HasComponent(uid)) + if (uid != user && _mobQuery.HasComponent(uid)) { TooCloseError(user); return false; @@ -271,17 +272,14 @@ public sealed partial class BlockingSystem : EntitySystem if (component.IsBlocking) StopBlocking(uid, component, user); - var userQuery = GetEntityQuery(); - var handQuery = GetEntityQuery(); - - if (!handQuery.TryGetComponent(user, out var hands)) + if (!_handQuery.TryGetComponent(user, out var hands)) return; var shields = _handsSystem.EnumerateHeld((user, hands)).ToArray(); foreach (var shield in shields) { - if (HasComp(shield) && userQuery.TryGetComponent(user, out var blockingUserComponent)) + if (HasComp(shield) && _userQuery.TryGetComponent(user, out var blockingUserComponent)) { blockingUserComponent.BlockingItem = shield; return; diff --git a/Content.Shared/Body/BodySystem.cs b/Content.Shared/Body/BodySystem.cs index 4cd5a4e792..3f01b4e2ba 100644 --- a/Content.Shared/Body/BodySystem.cs +++ b/Content.Shared/Body/BodySystem.cs @@ -18,8 +18,8 @@ public sealed partial class BodySystem : EntitySystem { [Dependency] private readonly SharedContainerSystem _container = default!; - private EntityQuery _bodyQuery; - private EntityQuery _organQuery; + [Dependency] private readonly EntityQuery _bodyQuery = default!; + [Dependency] private readonly EntityQuery _organQuery = default!; public override void Initialize() { @@ -33,9 +33,6 @@ public sealed partial class BodySystem : EntitySystem SubscribeLocalEvent(OnBodyEntInserted); SubscribeLocalEvent(OnBodyEntRemoved); - _bodyQuery = GetEntityQuery(); - _organQuery = GetEntityQuery(); - InitializeRelay(); } diff --git a/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs b/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs index 6273b0640b..a7f5163110 100644 --- a/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs +++ b/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs @@ -82,7 +82,7 @@ public abstract partial class SharedVisualBodySystem [NotNullWhen(true)] out Dictionary, OrganMarkingData>? markings, [NotNullWhen(true)] out Dictionary, Dictionary>>? applied) { - if (!Resolve(ent, ref ent.Comp)) + if (!Resolve(ent, ref ent.Comp, logMissing: false)) { profiles = null; markings = null; diff --git a/Content.Shared/Body/Systems/LungSystem.cs b/Content.Shared/Body/Systems/LungSystem.cs index 705a931102..8a7a00001a 100644 --- a/Content.Shared/Body/Systems/LungSystem.cs +++ b/Content.Shared/Body/Systems/LungSystem.cs @@ -37,10 +37,10 @@ public sealed class LungSystem : EntitySystem return; } - if (TryComp(args.Equipee, out InternalsComponent? internals)) + if (TryComp(args.EquipTarget, out InternalsComponent? internals)) { - ent.Comp.ConnectedInternalsEntity = args.Equipee; - _internals.ConnectBreathTool((args.Equipee, internals), ent); + ent.Comp.ConnectedInternalsEntity = args.EquipTarget; + _internals.ConnectBreathTool((args.EquipTarget, internals), ent); } } diff --git a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs index ec0521b7b6..bde65b99fe 100644 --- a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs +++ b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs @@ -278,7 +278,8 @@ public abstract class SharedBloodstreamSystem : EntitySystem if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution)) { SolutionContainer.RemoveAllSolution(ent.Comp.BloodSolution.Value); - TryModifyBloodLevel(ent.AsNullable(), ent.Comp.BloodReferenceSolution.Volume); + // TODO: Use Solutions API for this when it exists + TryRegulateBloodLevel(ent.AsNullable(), ent.Comp.BloodReferenceSolution.Volume); } } @@ -403,12 +404,15 @@ public abstract class SharedBloodstreamSystem : EntitySystem || amount == 0) return false; + // TODO: Either make this percentage based regeneration and pre-pass the percentage. + // TODO: Solution regulation API that doesn't result in very minor FixedPoint2 errors (Currently gingerbreadman only regenerates 0.99u instead of 1.00u) referenceFactor = Math.Clamp(referenceFactor, 0f, ent.Comp.MaxVolumeModifier); + var ratio = (float)amount / (float)ent.Comp.BloodReferenceSolution.Volume; foreach (var (referenceReagent, referenceQuantity) in ent.Comp.BloodReferenceSolution) { var error = referenceQuantity * referenceFactor - bloodSolution.GetTotalPrototypeQuantity(referenceReagent.Prototype); - var adjustedAmount = amount * referenceQuantity / ent.Comp.BloodReferenceSolution.Volume; + var adjustedAmount = referenceQuantity * ratio; if (error > 0) { diff --git a/Content.Shared/Body/VisualOrganMarkingsComponent.cs b/Content.Shared/Body/VisualOrganMarkingsComponent.cs index af79fd1830..31c34a42f9 100644 --- a/Content.Shared/Body/VisualOrganMarkingsComponent.cs +++ b/Content.Shared/Body/VisualOrganMarkingsComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.DisplacementMap; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Markings; using Robust.Shared.GameStates; @@ -37,6 +38,13 @@ public sealed partial class VisualOrganMarkingsComponent : Component [DataField, AutoNetworkedField] public Dictionary> DependentHidingLayers = new(); + /// + /// Optional displacement data for this organ to apply to markings. + /// Only applies to markings which support displacement data. + /// + [DataField, AutoNetworkedField] + public Dictionary MarkingsDisplacement = new(); + /// /// Client only - the last markings applied by this component /// diff --git a/Content.Shared/CCVar/CCVars.Atmos.cs b/Content.Shared/CCVar/CCVars.Atmos.cs index 833938b765..eccee01151 100644 --- a/Content.Shared/CCVar/CCVars.Atmos.cs +++ b/Content.Shared/CCVar/CCVars.Atmos.cs @@ -154,11 +154,11 @@ public sealed partial class CCVars CVarDef.Create("atmos.heat_scale", 8f, CVar.REPLICATED | CVar.SERVER); /// - /// Maximum explosion radius for explosions caused by bursting a gas tank ("max caps"). - /// Setting this to zero disables the explosion but still allows the tank to burst and leak. + /// Maximum explosion intensity for explosions caused by bursting a gas tank ("max caps"). + /// Setting this to zero disables the limits. /// public static readonly CVarDef AtmosTankFragment = - CVarDef.Create("atmos.max_explosion_range", 26f, CVar.SERVERONLY); + CVarDef.Create("atmos.max_explosion_range", 0f, CVar.SERVER); /// /// Whether atmospherics will process delta-pressure damage on entities with a DeltaPressureComponent. diff --git a/Content.Shared/CCVar/CCVars.Worldgen.cs b/Content.Shared/CCVar/CCVars.Worldgen.cs deleted file mode 100644 index da165ce74a..0000000000 --- a/Content.Shared/CCVar/CCVars.Worldgen.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Robust.Shared.Configuration; - -namespace Content.Shared.CCVar; - -public sealed partial class CCVars -{ - /// - /// Whether or not world generation is enabled. - /// - public static readonly CVarDef WorldgenEnabled = - CVarDef.Create("worldgen.enabled", false, CVar.SERVERONLY); - - /// - /// The worldgen config to use. - /// - public static readonly CVarDef WorldgenConfig = - CVarDef.Create("worldgen.worldgen_config", "Default", CVar.SERVERONLY); -} diff --git a/Content.Shared/Cargo/Components/TradeStationComponent.cs b/Content.Shared/Cargo/Components/TradeStationComponent.cs new file mode 100644 index 0000000000..ec0cba5386 --- /dev/null +++ b/Content.Shared/Cargo/Components/TradeStationComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.HijackBeacon; +using Robust.Shared.GameStates; + +namespace Content.Shared.Cargo.Components; + +/// +/// Target for approved orders to spawn at. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class TradeStationComponent : Component +{ + /// + /// The Trade Station's current hijack state. Modified by HijackBeaconSystem. + /// + [DataField, AutoNetworkedField] + public bool Hacked = false; +} diff --git a/Content.Shared/Cargo/SharedCargoSystem.cs b/Content.Shared/Cargo/SharedCargoSystem.cs index 9d044d1850..291d3abbc3 100644 --- a/Content.Shared/Cargo/SharedCargoSystem.cs +++ b/Content.Shared/Cargo/SharedCargoSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Prototypes; +using Content.Shared.HijackBeacon; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -17,6 +18,7 @@ public abstract class SharedCargoSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnHijackSuccess); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -25,6 +27,23 @@ public abstract class SharedCargoSystem : EntitySystem Dirty(ent); } + private void OnHijackSuccess(ref HijackBeaconSuccessEvent args) + { + var stationQuery = EntityQueryEnumerator(); + while (stationQuery.MoveNext(out var uid, out var comp)) + { + foreach (var (account, cash) in comp.Accounts) + { + comp.Accounts[account] = cash - args.Fine; + args.Total += args.Fine; + } + + var ev = new BankBalanceUpdatedEvent(uid, comp.Accounts); + RaiseLocalEvent(uid, ref ev, true); + Dirty(uid, comp); + } + } + /// /// For a given station, retrieves the balance in a specific account. /// diff --git a/Content.Shared/Changeling/ChangelingTransformEvents.cs b/Content.Shared/Changeling/ChangelingTransformEvents.cs index 9940a60705..9a4ff4454e 100644 --- a/Content.Shared/Changeling/ChangelingTransformEvents.cs +++ b/Content.Shared/Changeling/ChangelingTransformEvents.cs @@ -14,3 +14,26 @@ public sealed partial class ChangelingTransformActionEvent : InstantActionEvent; /// [Serializable, NetSerializable] public sealed partial class ChangelingTransformDoAfterEvent : SimpleDoAfterEvent; + +/// +/// Raised on a changeling before they transform into a stored identity. +/// This is raised after the DoAfter finished. +/// +public readonly record struct BeforeChangelingTransformEvent(EntityUid StoredIdentity) +{ + /// + /// The stored identity the changeling will transform into. + /// + public readonly EntityUid StoredIdentity = StoredIdentity; +}; + +/// +/// Raised on a changeling after they successfully transformed into a stored identity. +/// +public readonly record struct AfterChangelingTransformEvent(EntityUid StoredIdentity) +{ + /// + /// The stored identity the changeling transformed into. + /// + public readonly EntityUid StoredIdentity = StoredIdentity; +}; diff --git a/Content.Shared/Changeling/Components/ChangelingDevourComponent.cs b/Content.Shared/Changeling/Components/ChangelingDevourComponent.cs index 3648f1198e..0b3fdda5b6 100644 --- a/Content.Shared/Changeling/Components/ChangelingDevourComponent.cs +++ b/Content.Shared/Changeling/Components/ChangelingDevourComponent.cs @@ -1,12 +1,10 @@ using Content.Shared.Changeling.Systems; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; -using Content.Shared.FixedPoint; using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Changeling.Components; @@ -14,24 +12,24 @@ namespace Content.Shared.Changeling.Components; /// Component responsible for Changelings Devour attack. Including the amount of damage /// and how long it takes to devour someone /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [Access(typeof(ChangelingDevourSystem))] public sealed partial class ChangelingDevourComponent : Component { /// - /// The Action for devouring + /// The action for devouring. /// [DataField] public EntProtoId? ChangelingDevourAction = "ActionChangelingDevour"; /// - /// The action entity associated with devouring + /// The action entity associated with devouring. /// [DataField, AutoNetworkedField] public EntityUid? ChangelingDevourActionEntity; /// - /// The whitelist of targets for devouring + /// The whitelist of targets for devouring. /// [DataField, AutoNetworkedField] public EntityWhitelist? Whitelist = new() @@ -44,7 +42,7 @@ public sealed partial class ChangelingDevourComponent : Component }; /// - /// The Sound to use during consumption of a victim + /// The sound to use during consumption of a victim. /// /// /// 6 distance due to the default 15 being hearable all the way across PVS. Changeling is meant to be stealthy. @@ -54,7 +52,7 @@ public sealed partial class ChangelingDevourComponent : Component public SoundSpecifier? ConsumeNoise = new SoundCollectionSpecifier("ChangelingDevourConsume", AudioParams.Default.WithMaxDistance(6)); /// - /// The Sound to use during the windup before consuming a victim + /// The sound to use during the windup before consuming a victim. /// /// /// 6 distance due to the default 15 being hearable all the way across PVS. Changeling is meant to be stealthy. @@ -64,36 +62,31 @@ public sealed partial class ChangelingDevourComponent : Component public SoundSpecifier? DevourWindupNoise = new SoundCollectionSpecifier("ChangelingDevourWindup", AudioParams.Default.WithMaxDistance(6)); /// - /// The time between damage ticks - /// - [DataField, AutoNetworkedField] - public TimeSpan DamageTimeBetweenTicks = TimeSpan.FromSeconds(1); - - /// - /// The windup time before the changeling begins to engage in devouring the identity of a target + /// The windup time before the changeling begins to engage in devouring the identity of a target. /// [DataField, AutoNetworkedField] public TimeSpan DevourWindupTime = TimeSpan.FromSeconds(2); /// - /// The time it takes to FULLY consume someones identity. + /// The time it takes to consume someones identity. + /// Starts after the windup. /// [DataField, AutoNetworkedField] public TimeSpan DevourConsumeTime = TimeSpan.FromSeconds(10); /// - /// The Currently active devour sound in the world + /// The currently active devour sound in the world. /// [DataField] public EntityUid? CurrentDevourSound; /// - /// The damage profile for a single tick of devour damage + /// The damage dealt after the windup finished and devouring started. /// [DataField, AutoNetworkedField] - public DamageSpecifier DamagePerTick = new() + public DamageSpecifier WindupDamage = new() { - DamageDict = new () + DamageDict = new() { { "Slash", 10}, { "Piercing", 10 }, @@ -102,7 +95,21 @@ public sealed partial class ChangelingDevourComponent : Component }; /// - /// The list of protective damage types capable of preventing a devour if over the threshold + /// The damage dealt after the devouring is fully finished. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier DevourDamage = new() + { + DamageDict = new() + { + { "Slash", 20}, + { "Piercing", 20 }, + { "Blunt", 10 }, + }, + }; + + /// + /// The list of protective damage types capable of preventing a devour if over the threshold. /// [DataField, AutoNetworkedField] public List> ProtectiveDamageTypes = new() @@ -113,13 +120,7 @@ public sealed partial class ChangelingDevourComponent : Component }; /// - /// The next Tick to deal damage on (utilized during the consumption "do-during" (a do after with an attempt event)) - /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField] - public TimeSpan NextTick = TimeSpan.Zero; - - /// - /// The percentage of ANY brute damage resistance that will prevent devouring + /// The percentage of ANY brute damage resistance that will prevent devouring. /// [DataField, AutoNetworkedField] public float DevourPreventionPercentageThreshold = 0.1f; diff --git a/Content.Shared/Changeling/Components/ChangelingDevouredComponent.cs b/Content.Shared/Changeling/Components/ChangelingDevouredComponent.cs new file mode 100644 index 0000000000..c235ea071a --- /dev/null +++ b/Content.Shared/Changeling/Components/ChangelingDevouredComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Changeling.Components; + +/// +/// Component used for marking entities devoured by a changeling. +/// Used to prevent granting the identity several times. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ChangelingDevouredComponent : Component +{ + /// + /// HashSet of all changelings that have devoured this entity. + /// + // TODO: This should be using some sort of relation system in the future. + [DataField, AutoNetworkedField] + public HashSet DevouredBy = new(); +} diff --git a/Content.Shared/Changeling/Components/ChangelingFleshClothingAbilityComponent.cs b/Content.Shared/Changeling/Components/ChangelingFleshClothingAbilityComponent.cs new file mode 100644 index 0000000000..40df5b2bad --- /dev/null +++ b/Content.Shared/Changeling/Components/ChangelingFleshClothingAbilityComponent.cs @@ -0,0 +1,33 @@ +using Content.Shared.Alert; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Changeling.Components; + +/// +/// Allows the changeling to spawn dummy chameleon clothing items that will transform with them, +/// mimicing the equipment of the stored disguise. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ChangelingFleshClothingAbilityComponent : Component +{ + /// + /// Is the ability currently enabled? + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; + + /// + /// The alert for showing if the ability is active and for toggling it. + /// + [DataField] + public ProtoId AlertId = "ChangelingFleshClothing"; + + /// + /// The chameleon clothing items to spawn into any empty slots if the changeling transformed. + /// These need so that they will change their visuals according to the identity we transformed into. + /// The key of the dictionary is the corresponding inventory slot. + /// + [DataField] + public Dictionary ClothingPrototypes = new(); +} diff --git a/Content.Shared/Changeling/Components/ChangelingFleshClothingComponent.cs b/Content.Shared/Changeling/Components/ChangelingFleshClothingComponent.cs new file mode 100644 index 0000000000..affd1f3a8f --- /dev/null +++ b/Content.Shared/Changeling/Components/ChangelingFleshClothingComponent.cs @@ -0,0 +1,11 @@ +using Content.Shared.Clothing.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Changeling.Components; + +/// +/// Allows this clothing item to transform along with the changeling so that it looks like whatever the mob we transform into is wearing in that slot. +/// Requires and . +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ChangelingFleshClothingComponent : Component; diff --git a/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs b/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs index 8e74f83537..bef822b720 100644 --- a/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs +++ b/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs @@ -13,11 +13,12 @@ public sealed partial class ChangelingIdentityComponent : Component { /// /// The list of entities that exist on a paused map. They are paused clones of the victims that the ling has consumed, with all relevant components copied from the original. + /// The key is the EntityUid of the stored identity, the value is the original entity the identity came from. + /// The value will be set to null if that entity is deleted. /// - // TODO: Store a reference to the original entity as well so you cannot infinitely devour somebody. Currently very tricky due the inability to send over EntityUid if the original is ever deleted. Can be fixed by something like WeakEntityReference. + // TODO: This should be handled via a relation system in the future. [DataField, AutoNetworkedField] - public List ConsumedIdentities = new(); - + public Dictionary ConsumedIdentities = new(); /// /// The currently assumed identity. @@ -26,8 +27,8 @@ public sealed partial class ChangelingIdentityComponent : Component public EntityUid? CurrentIdentity; /// - /// The cloning settings passed to the CloningSystem, contains a list of all components to copy or have handled by their - /// respective systems. + /// The cloning settings to use when cloning a devoured identity to the paused map. + /// This contains a whitelist of all components that need to be backed up so that the changeling can transform into them later. /// [DataField] public ProtoId IdentityCloningSettings = "ChangelingCloningSettings"; diff --git a/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs b/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs index 385ed5c9e9..63a0a67f9b 100644 --- a/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs @@ -209,14 +209,14 @@ public sealed class ChangelingClonerSystem : EntitySystem if (!HasComp(target)) return; // cloning only works for humanoids at the moment - if (!_prototype.Resolve(ent.Comp.Settings, out var settings)) - return; - _adminLogger.Add(LogType.Identity, $"{user} is using {ent.Owner} to draw DNA from {target}."); // Make a copy of the target on a paused map, so that we can apply their components later. - ent.Comp.ClonedBackup = _changelingIdentity.CloneToPausedMap(settings, target); + ent.Comp.ClonedBackup = _changelingIdentity.CloneToPausedMap(ent.Comp.Settings, target); + if (ent.Comp.ClonedBackup == null) + return; + ent.Comp.State = ChangelingClonerState.Filled; _appearance.SetData(ent.Owner, ChangelingClonerVisuals.State, ChangelingClonerState.Filled); Dirty(ent); diff --git a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs index 5b70b7388f..e0cb17f51f 100644 --- a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs @@ -2,9 +2,7 @@ using Content.Shared.Actions; using Content.Shared.Administration.Logs; using Content.Shared.Armor; using Content.Shared.Atmos.Rotting; -using Content.Shared.Body; using Content.Shared.Changeling.Components; -using Content.Shared.Damage.Components; using Content.Shared.Damage.Systems; using Content.Shared.Database; using Content.Shared.DoAfter; @@ -12,20 +10,15 @@ using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; using Content.Shared.Inventory; using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; using Content.Shared.Popups; -using Content.Shared.Storage; using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; -using Robust.Shared.Random; -using Robust.Shared.Timing; namespace Content.Shared.Changeling.Systems; public sealed class ChangelingDevourSystem : EntitySystem { - [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly INetManager _net = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; @@ -34,10 +27,8 @@ public sealed class ChangelingDevourSystem : EntitySystem [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedChangelingIdentitySystem _changelingIdentitySystem = default!; - [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; public override void Initialize() { @@ -47,7 +38,6 @@ public sealed class ChangelingDevourSystem : EntitySystem SubscribeLocalEvent(OnDevourAction); SubscribeLocalEvent(OnDevourWindup); SubscribeLocalEvent(OnDevourConsume); - SubscribeLocalEvent>(OnConsumeAttemptTick); SubscribeLocalEvent(OnShutdown); } @@ -64,31 +54,193 @@ public sealed class ChangelingDevourSystem : EntitySystem } } - //TODO: Allow doafters to have proper update loop support. Attempt events should not be doing state changes. - private void OnConsumeAttemptTick(Entity ent, - ref DoAfterAttemptEvent eventData) + // The action was used. + // Start the first doafter for the windup. + private void OnDevourAction(Entity ent, ref ChangelingDevourActionEvent args) { - - var curTime = _timing.CurTime; - - if (curTime < ent.Comp.NextTick) + if (args.Handled + || _whitelistSystem.IsWhitelistFailOrNull(ent.Comp.Whitelist, args.Target) + || !HasComp(ent)) return; - ConsumeDamageTick(eventData.Event.Target, ent.Comp, eventData.Event.User); - ent.Comp.NextTick += ent.Comp.DamageTimeBetweenTicks; - Dirty(ent, ent.Comp); + args.Handled = true; + var target = args.Target; + + if (!CanDevour(ent.AsNullable(), target)) + return; + + if (_net.IsServer) + { + ent.Comp.CurrentDevourSound = _audio.Stop(ent.Comp.CurrentDevourSound); + ent.Comp.CurrentDevourSound = _audio.PlayPvs(ent.Comp.DevourWindupNoise, ent)?.Entity; + } + + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ent:player} started changeling devour windup against {target:player}"); + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, ent, ent.Comp.DevourWindupTime, new ChangelingDevourWindupDoAfterEvent(), ent, target: target, used: ent) + { + BreakOnMove = true, + CancelDuplicate = true, + DuplicateCondition = DuplicateConditions.None, + }); + + var selfMessage = Loc.GetString("changeling-devour-begin-windup-self", ("user", Identity.Entity(ent.Owner, EntityManager))); + var othersMessage = Loc.GetString("changeling-devour-begin-windup-others", ("user", Identity.Entity(ent.Owner, EntityManager))); + _popupSystem.PopupPredicted( + selfMessage, + othersMessage, + args.Performer, + args.Performer, + PopupType.MediumCaution); } - private void ConsumeDamageTick(EntityUid? target, ChangelingDevourComponent comp, EntityUid? user) + // First doafter finished. + // Start the second doafter for the actual consumption and deal a small amount of damage. + private void OnDevourWindup(Entity ent, ref ChangelingDevourWindupDoAfterEvent args) { - if (target == null) + args.Handled = true; + ent.Comp.CurrentDevourSound = _audio.Stop(ent.Comp.CurrentDevourSound); + + if (args.Cancelled) return; - _damageable.ChangeDamage(target.Value, comp.DamagePerTick, true, true, user); + if (args.Target is not { } target) + return; + + _damageable.ChangeDamage(target, ent.Comp.WindupDamage, true, true, ent.Owner); + + var selfMessage = Loc.GetString("changeling-devour-begin-consume-self", ("user", Identity.Entity(ent.Owner, EntityManager))); + var othersMessage = Loc.GetString("changeling-devour-begin-consume-others", ("user", Identity.Entity(ent.Owner, EntityManager))); + _popupSystem.PopupPredicted( + selfMessage, + othersMessage, + ent.Owner, + ent.Owner, + PopupType.LargeCaution); + + if (_net.IsServer) + ent.Comp.CurrentDevourSound = _audio.PlayPvs(ent.Comp.ConsumeNoise, ent)?.Entity; + + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} began to devour {ToPrettyString(target):player}'s identity"); + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, + ent, + ent.Comp.DevourConsumeTime, + new ChangelingDevourConsumeDoAfterEvent(), + ent, + target: target, + used: ent) + { + BreakOnMove = true, + CancelDuplicate = true, + DuplicateCondition = DuplicateConditions.None, + }); + } + + // Second doafter finished. + // Save the identity and deal more damage. + private void OnDevourConsume(Entity ent, ref ChangelingDevourConsumeDoAfterEvent args) + { + args.Handled = true; + ent.Comp.CurrentDevourSound = _audio.Stop(ent.Comp.CurrentDevourSound); + + if (args.Cancelled) + return; + + if (args.Target is not { } target) + return; + + // Damage first before the CanDevour check to make sure they don't gib in-between and to kill them again in case they somehow revived. + _damageable.ChangeDamage(target, ent.Comp.DevourDamage, true, true, ent.Owner); + + if (!CanDevour(ent.AsNullable(), target)) // Check again if the conditions are still met. + return; + + var selfMessage = Loc.GetString("changeling-devour-consume-complete-self", ("user", Identity.Entity(ent.Owner, EntityManager))); + var othersMessage = Loc.GetString("changeling-devour-consume-complete-others", ("user", Identity.Entity(ent.Owner, EntityManager))); + _popupSystem.PopupPredicted( + selfMessage, + othersMessage, + ent.Owner, + ent.Owner, + PopupType.LargeCaution); + + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} successfully devoured {ToPrettyString(target):player}'s identity"); + + if (!TryComp(ent.Owner, out var identityStorage)) + return; + + _changelingIdentitySystem.CloneToPausedMap((ent, identityStorage), target); + + // We add a reference to ourselves to prevent repeated identity gain. + var targetDevoured = EnsureComp(target); + targetDevoured.DevouredBy.Add(ent.Owner); + Dirty(target, targetDevoured); + Dirty(ent); } /// - /// Checkes if the targets outerclothing is beyond a DamageCoefficientThreshold to protect them from being devoured. + /// Has the given victim been devoured by the given changeling before? + /// + public bool HasDevoured(Entity changeling, EntityUid devoured) + { + if (!Resolve(changeling, ref changeling.Comp, false)) + return false; + + return changeling.Comp.ConsumedIdentities.ContainsValue(devoured); + } + + /// + /// Can the given changeling devour the given victim? + /// + public bool CanDevour(Entity changeling, EntityUid victim, bool showPopup = true) + { + if (!Resolve(changeling, ref changeling.Comp)) + return false; + + if (changeling.Owner == victim) + return false; // Can't devour yourself. + + if (!HasComp(victim)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-cannot-devour"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + if (HasDevoured(changeling.Owner, victim)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-already-devoured"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + if (!_mobState.IsDead(victim)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-not-dead"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + if (HasComp(victim)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-rotting"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + if (IsTargetProtected(victim, changeling!)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-protected"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + return true; + } + + /// + /// Checks if the target's outerclothing is beyond a DamageCoefficientThreshold to protect them from being devoured. /// /// The Targeted entity /// Changelings Devour Component @@ -109,171 +261,4 @@ public sealed class ChangelingDevourSystem : EntitySystem return false; } - - private void OnDevourAction(Entity ent, ref ChangelingDevourActionEvent args) - { - if (args.Handled || _whitelistSystem.IsWhitelistFailOrNull(ent.Comp.Whitelist, args.Target) - || !HasComp(ent)) - return; - - args.Handled = true; - var target = args.Target; - - if (target == ent.Owner) - return; // don't eat yourself - - if (HasComp(target)) - { - //WL-Changes: Devour custom popups start - _popupSystem.PopupClient(Loc.GetString(ent.Comp.AttemptFailedRottingPopup), args.Performer, args.Performer, PopupType.Medium); - return; - } - - if (IsTargetProtected(target, ent)) - { - _popupSystem.PopupClient(Loc.GetString(ent.Comp.AttemptFailedProtectedPopup), ent, ent, PopupType.Medium); - //WL-Changes: Devour custom popups end - return; - } - - if (_net.IsServer) - { - var pvsSound = _audio.PlayPvs(ent.Comp.DevourWindupNoise, ent); - if (pvsSound != null) - ent.Comp.CurrentDevourSound = pvsSound.Value.Entity; - } - - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ent:player} started changeling devour windup against {target:player}"); - - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, ent, ent.Comp.DevourWindupTime, new ChangelingDevourWindupDoAfterEvent(), ent, target: target, used: ent) - { - BreakOnMove = true, - BlockDuplicate = true, - DuplicateCondition = DuplicateConditions.None, - }); - - //WL-Changes: Devour custom popups start - var selfMessage = Loc.GetString(ent.Comp.BeginWindupSelfPopup, ("user", Identity.Entity(ent.Owner, EntityManager))); - var othersMessage = Loc.GetString(ent.Comp.BeginWindupOthersPopup, ("user", Identity.Entity(ent.Owner, EntityManager))); - //WL-Changes: Devour custom popups end - _popupSystem.PopupPredicted( - selfMessage, - othersMessage, - args.Performer, - args.Performer, - PopupType.MediumCaution); - } - - private void OnDevourWindup(Entity ent, ref ChangelingDevourWindupDoAfterEvent args) - { - var curTime = _timing.CurTime; - args.Handled = true; - - if (!Exists(ent.Comp.CurrentDevourSound)) - _audio.Stop(ent.Comp.CurrentDevourSound!); - - if (args.Cancelled) - return; - - //WL-Changes: Devour custom popups start - var selfMessage = Loc.GetString(ent.Comp.BeginConsumeSelfPopup, ("user", Identity.Entity(ent.Owner, EntityManager))); - var othersMessage = Loc.GetString(ent.Comp.BeginConsumeOthersPopup, ("user", Identity.Entity(ent.Owner, EntityManager))); - //WL-Changes: Devour custom popups end - _popupSystem.PopupPredicted( - selfMessage, - othersMessage, - args.User, - args.User, - PopupType.LargeCaution); - - if (_net.IsServer) - { - var pvsSound = _audio.PlayPvs(ent.Comp.ConsumeNoise, ent); - - if (pvsSound != null) - ent.Comp.CurrentDevourSound = pvsSound.Value.Entity; - } - - - ent.Comp.NextTick = curTime + ent.Comp.DamageTimeBetweenTicks; - - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} began to devour {ToPrettyString(args.Target):player} identity"); - - _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, - ent, - ent.Comp.DevourConsumeTime, - new ChangelingDevourConsumeDoAfterEvent(), - ent, - target: args.Target, - used: ent) - { - AttemptFrequency = AttemptFrequency.EveryTick, - BreakOnMove = true, - BlockDuplicate = true, - DuplicateCondition = DuplicateConditions.None, - }); - } - - private void OnDevourConsume(Entity ent, ref ChangelingDevourConsumeDoAfterEvent args) - { - args.Handled = true; - var target = args.Target; - - if (target == null) - return; - - if (Exists(ent.Comp.CurrentDevourSound)) - _audio.Stop(ent.Comp.CurrentDevourSound!); - - if (args.Cancelled) - return; - - if (!_mobState.IsDead((EntityUid)target)) - { - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} unsuccessfully devoured {ToPrettyString(args.Target):player}'s identity"); - //WL-Changes: Devour custom popups start - _popupSystem.PopupClient(Loc.GetString(ent.Comp.ConsumeFailedNotDeadPopup), args.User, args.User, PopupType.Medium); - //WL-Changes: Devour custom popups end - return; - } - - //WL-Changes: Devour custom popups start - var selfMessage = Loc.GetString(ent.Comp.ConsumeCompleteSelfPopup, ("user", Identity.Entity(args.User, EntityManager))); - var othersMessage = Loc.GetString(ent.Comp.ConsumeCompleteOthersPopup, ("user", Identity.Entity(args.User, EntityManager))); - //WL-Changes: Devour custom popups end - _popupSystem.PopupPredicted( - selfMessage, - othersMessage, - args.User, - args.User, - PopupType.LargeCaution); - - if (_mobState.IsDead(target.Value) - && TryComp(target, out var body) - && HasComp(target) - && TryComp(args.User, out var identityStorage)) - { - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} successfully devoured {ToPrettyString(args.Target):player}'s identity"); - _changelingIdentitySystem.CloneToPausedMap((ent, identityStorage), target.Value); - - if (_inventorySystem.TryGetSlotEntity(target.Value, "jumpsuit", out var item) - && TryComp(item, out var butcherable)) - RipClothing(target.Value, (item.Value, butcherable)); - } - - Dirty(ent); - } - - private void RipClothing(EntityUid victim, Entity item) - { - var spawnEntities = EntitySpawnCollection.GetSpawns(item.Comp.SpawnedEntities, _robustRandom); - - foreach (var proto in spawnEntities) - { - // TODO: once predictedRandom is in, make this a Coordinate offset of 0.25f from the victims position - PredictedSpawnNextToOrDrop(proto, victim); - } - - PredictedQueueDel(item.Owner); - } } diff --git a/Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs b/Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs new file mode 100644 index 0000000000..8082c3bb87 --- /dev/null +++ b/Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs @@ -0,0 +1,137 @@ +using Content.Shared.Alert; +using Content.Shared.Changeling.Components; +using Content.Shared.Clothing.Components; +using Content.Shared.Clothing.EntitySystems; +using Content.Shared.Inventory; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Changeling.Systems; + +public sealed class ChangelingFleshClothingSystem : EntitySystem +{ + [Dependency] private readonly SharedChameleonClothingSystem _chameleonClothing = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnToggle); + SubscribeLocalEvent(OnBeforeChangelingTransform); + SubscribeLocalEvent(OnAfterChangelingTransform); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + _alerts.ShowAlert(ent.Owner, ent.Comp.AlertId, 1); + } + + private void OnToggle(Entity ent, ref ToggleFleshClothingEvent args) + { + if (args.Handled) + return; + + ent.Comp.Enabled = !ent.Comp.Enabled; + Dirty(ent); + _alerts.ShowAlert(ent.Owner, ent.Comp.AlertId, (short)(ent.Comp.Enabled ? 1 : 0)); + + args.Handled = true; + } + + // If we transform into another species and loose an inventory item then it will get dropped as a result. + // But we don't want fleeting clothing to do a sound and popup in that case, + // so we have to delete any flesh clothing slots that would drop before transforming. + private void OnBeforeChangelingTransform(Entity ent, ref BeforeChangelingTransformEvent args) + { + // We always remove slots that are no longer supported by the transformation, even if the component is disabled. + RemoveRedundantFleshClothing(ent.Owner, args.StoredIdentity); + } + + private void OnAfterChangelingTransform(Entity ent, ref AfterChangelingTransformEvent args) + { + if (ent.Comp.Enabled) + SpawnAndTransformClothing(ent.Owner, args.StoredIdentity, ent.Comp.ClothingPrototypes); + } + + /// + /// Removes any flesh clothing items the target is wearing in slots the original does not have. + /// + public void RemoveRedundantFleshClothing(Entity target, Entity original) + { + // TODO: Not predicted yet because equipping is not predicted either and we don't want to be nude for a few frames. + if (_net.IsClient) + return; + + if (!Resolve(target, ref target.Comp)) + return; + + Resolve(original, ref original.Comp, false); // They might now have an inventory at all. + + var slots = _inventory.GetSlotEnumerator(target, SlotFlags.WITHOUT_POCKET); + while (slots.NextItem(out var targetItem, out var slotDefinition)) + { + if (!HasComp(targetItem)) + continue; // Do nothing for normal items. + + // If the original does not have that slot or it contains an invalid prototype then we remove any flesh clothing item in our corresponding slot. + if (original.Comp == null + || !_inventory.TryGetSlotEntity(original, slotDefinition.Name, out var originalItem, inventoryComponent: original.Comp) + || MetaData(originalItem.Value).EntityPrototype?.ID == null) + { + _inventory.TryUnequip(target, slotDefinition.Name, silent: true, force: true, inventory: target.Comp); + QueueDel(targetItem); + } + } + } + + /// + /// Spawns the given clothing items into empty inventory slots. + /// If the item has and then + /// it will have its visuals changed to match the item another given player is wearing in the same slot. + /// + public void SpawnAndTransformClothing(Entity target, Entity original, Dictionary clothingPrototypes) + { + // TODO: Remove this guard when chameleon clothing is properly predicted. + // Otherwise we will see the default item sprite for a moment after spawn until it's updated. + if (_net.IsClient) + return; + + if (!Resolve(target, ref target.Comp) || !Resolve(original, ref original.Comp, false)) // Don't log because the original might be outside PVS range since it's on another map. + return; + + var slots = _inventory.GetSlotEnumerator(target, SlotFlags.WITHOUT_POCKET); + var coords = Transform(target).Coordinates; + while (slots.MoveNext(out var containerSlot, out var slotDefinition)) + { + var targetItem = containerSlot.ContainedEntity; + if (!_inventory.TryGetSlotEntity(original, slotDefinition.Name, out var originalItem, inventoryComponent: original.Comp) + || MetaData(originalItem.Value).EntityPrototype?.ID is not { } chameleonProtoId) + continue; + + // If our slot is empty then spawn a new chameleon clothing item inside. + if (targetItem == null && clothingPrototypes.TryGetValue(slotDefinition.Name, out var fleshProtoId)) + { + targetItem = SpawnAtPosition(fleshProtoId, coords); + _inventory.TryEquip(target, targetItem.Value, slotDefinition.Name, silent: true, force: true, inventory: target.Comp); + } + + // If the item in our slot is flesh clothing then set the chameleon prototype to mirror the target. + if (HasComp(targetItem)) + { + if (TryComp(originalItem, out var originalChameleonComp)) + _chameleonClothing.SetSelectedPrototype(targetItem.Value, originalChameleonComp.Default, validate: false); // If it is also a chameleon item then use whatever that is mimicing. + else + _chameleonClothing.SetSelectedPrototype(targetItem.Value, chameleonProtoId, validate: false); // We don't validate because a lot of clothing is not chameleon whitelisted, like warden's beret. + } + } + } +} + +/// +/// Event raised to toggle the . +/// +public sealed partial class ToggleFleshClothingEvent : BaseAlertEvent; diff --git a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs index e555147352..50f5ceb547 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs @@ -3,13 +3,25 @@ namespace Content.Shared.Changeling.Systems; /// -/// Send when a player selects an intentity to transform into in the radial menu. +/// Send when a player selects an identity to transform into in the radial menu. /// [Serializable, NetSerializable] public sealed class ChangelingTransformIdentitySelectMessage(NetEntity targetIdentity) : BoundUserInterfaceMessage { /// - /// The uid of the cloned identity. + /// The uid of the stored identity. + /// + public readonly NetEntity TargetIdentity = targetIdentity; +} + +/// +/// Send when a player selects an identity to drop from their storage. +/// +[Serializable, NetSerializable] +public sealed class ChangelingTransformIdentityDropMessage(NetEntity targetIdentity) : BoundUserInterfaceMessage +{ + /// + /// The uid of the stored identity. /// public readonly NetEntity TargetIdentity = targetIdentity; } diff --git a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs index 9e4b1d4d03..8ec2d36cd8 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs @@ -5,10 +5,11 @@ using Content.Shared.Changeling.Components; using Content.Shared.Cloning; using Content.Shared.Database; using Content.Shared.DoAfter; -using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; using Content.Shared.Popups; +using Content.Shared.Storage; using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; using Robust.Shared.Network; using Robust.Shared.Prototypes; @@ -17,16 +18,19 @@ namespace Content.Shared.Changeling.Systems; public sealed partial class ChangelingTransformSystem : EntitySystem { [Dependency] private readonly INetManager _net = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly MetaDataSystem _metaSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedCloningSystem _cloningSystem = default!; + [Dependency] private readonly SharedCloningSystem _cloning = default!; [Dependency] private readonly SharedVisualBodySystem _visualBody = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly IdentitySystem _identity = default!; + [Dependency] private readonly SharedChangelingIdentitySystem _changelingIdentity = default!; private const string ChangelingBuiXmlGeneratedName = "ChangelingTransformBoundUserInterface"; public override void Initialize() @@ -35,24 +39,28 @@ public sealed partial class ChangelingTransformSystem : EntitySystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnTransformAction); - SubscribeLocalEvent(OnSuccessfulTransform); SubscribeLocalEvent(OnTransformSelected); + SubscribeLocalEvent(OnTransformDrop); + SubscribeLocalEvent(OnSuccessfulTransform); SubscribeLocalEvent(OnShutdown); + + // Components that need special handling outside of cloning. + SubscribeLocalEvent(StorageBeforeTransform); } private void OnMapInit(Entity ent, ref MapInitEvent init) { - _actionsSystem.AddAction(ent, ref ent.Comp.ChangelingTransformActionEntity, ent.Comp.ChangelingTransformAction); + _actions.AddAction(ent, ref ent.Comp.ChangelingTransformActionEntity, ent.Comp.ChangelingTransformAction); var userInterfaceComp = EnsureComp(ent); - _uiSystem.SetUi((ent, userInterfaceComp), ChangelingTransformUiKey.Key, new InterfaceData(ChangelingBuiXmlGeneratedName)); + _ui.SetUi((ent, userInterfaceComp), ChangelingTransformUiKey.Key, new InterfaceData(ChangelingBuiXmlGeneratedName)); } private void OnShutdown(Entity ent, ref ComponentShutdown args) { if (ent.Comp.ChangelingTransformActionEntity != null) { - _actionsSystem.RemoveAction(ent.Owner, ent.Comp.ChangelingTransformActionEntity); + _actions.RemoveAction(ent.Owner, ent.Comp.ChangelingTransformActionEntity); } } @@ -65,17 +73,54 @@ public sealed partial class ChangelingTransformSystem : EntitySystem if (!TryComp(ent, out var userIdentity)) return; - if (!_uiSystem.IsUiOpen((ent, userInterfaceComp), ChangelingTransformUiKey.Key, args.Performer)) + if (!_ui.IsUiOpen((ent, userInterfaceComp), ChangelingTransformUiKey.Key, args.Performer)) { - _uiSystem.OpenUi((ent, userInterfaceComp), ChangelingTransformUiKey.Key, args.Performer); + _ui.OpenUi((ent, userInterfaceComp), ChangelingTransformUiKey.Key, args.Performer); } //TODO: Can add a Else here with TransformInto and CloseUI to make a quick switch, // issue right now is that Radials cover the Action buttons so clicking the action closes the UI (due to clicking off a radial causing it to close, even with UI) // but pressing the number does. } + private void OnTransformSelected(Entity ent, + ref ChangelingTransformIdentitySelectMessage args) + { + if (!TryGetEntity(args.TargetIdentity, out var targetIdentity)) + return; + + if (!TryComp(ent, out var identity)) + return; + + if (identity.CurrentIdentity == targetIdentity) + return; // don't transform into ourselves + + if (!identity.ConsumedIdentities.ContainsKey(targetIdentity.Value)) + return; // this identity does not belong to this player + + TransformInto(ent.AsNullable(), targetIdentity.Value); + } + + private void OnTransformDrop(Entity ent, + ref ChangelingTransformIdentityDropMessage args) + { + if (!TryGetEntity(args.TargetIdentity, out var targetIdentity)) + return; + + if (!TryComp(ent, out var identity)) + return; + + if (identity.CurrentIdentity == targetIdentity) + return; // don't drop our current identity + + if (!identity.ConsumedIdentities.ContainsKey(targetIdentity.Value)) + return; // this identity does not belong to this player + + _popup.PopupClient(Loc.GetString("changeling-transform-bui-drop-identity-entity-popup", ("entity", targetIdentity.Value)), ent.Owner, PopupType.Large); + _changelingIdentity.DropStoredIdentity(ent.Owner, targetIdentity.Value); + } + /// /// Transform the changeling into another identity. - /// This can be any cloneable humanoid and doesn't have to be stored in the ChangelingIdentiyComponent, + /// This can be any cloneable humanoid and doesn't have to be stored in the ChangelingIdentityComponent, /// so make sure to validate the target before. /// public void TransformInto(Entity ent, EntityUid targetIdentity) @@ -85,7 +130,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem var selfMessage = Loc.GetString("changeling-transform-attempt-self", ("user", Identity.Entity(ent.Owner, EntityManager))); var othersMessage = Loc.GetString("changeling-transform-attempt-others", ("user", Identity.Entity(ent.Owner, EntityManager))); - _popupSystem.PopupPredicted( + _popup.PopupPredicted( selfMessage, othersMessage, ent, @@ -93,14 +138,17 @@ public sealed partial class ChangelingTransformSystem : EntitySystem PopupType.MediumCaution); if (_net.IsServer) + { + ent.Comp.CurrentTransformSound = _audio.Stop(ent.Comp.CurrentTransformSound); // cancel any previous sounds first ent.Comp.CurrentTransformSound = _audio.PlayPvs(ent.Comp.TransformAttemptNoise, ent)?.Entity; + } if (TryComp(targetIdentity, out var storedIdentity) && storedIdentity.OriginalSession != null) _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} begun an attempt to transform into \"{Name(targetIdentity)}\" ({storedIdentity.OriginalSession:player}) "); else _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} begun an attempt to transform into \"{Name(targetIdentity)}\""); - _doAfterSystem.TryStartDoAfter(new DoAfterArgs( + _doAfter.TryStartDoAfter(new DoAfterArgs( EntityManager, ent, ent.Comp.TransformWindup, @@ -116,33 +164,11 @@ public sealed partial class ChangelingTransformSystem : EntitySystem }); } - private void OnTransformSelected(Entity ent, - ref ChangelingTransformIdentitySelectMessage args) - { - _uiSystem.CloseUi(ent.Owner, ChangelingTransformUiKey.Key, ent); - - if (!TryGetEntity(args.TargetIdentity, out var targetIdentity)) - return; - - if (!TryComp(ent, out var identity)) - return; - - if (identity.CurrentIdentity == targetIdentity) - return; // don't transform into ourselves - - if (!identity.ConsumedIdentities.Contains(targetIdentity.Value)) - return; // this identity does not belong to this player - - TransformInto(ent.AsNullable(), targetIdentity.Value); - } - private void OnSuccessfulTransform(Entity ent, ref ChangelingTransformDoAfterEvent args) { args.Handled = true; - - if (Exists(ent.Comp.CurrentTransformSound)) - _audio.Stop(ent.Comp.CurrentTransformSound); + ent.Comp.CurrentTransformSound = _audio.Stop(ent.Comp.CurrentTransformSound); if (args.Cancelled) return; @@ -153,14 +179,19 @@ public sealed partial class ChangelingTransformSystem : EntitySystem if (args.Target is not { } targetIdentity) return; + var beforeTransformEvent = new BeforeChangelingTransformEvent(targetIdentity); + RaiseLocalEvent(args.User, beforeTransformEvent); + _visualBody.CopyAppearanceFrom(targetIdentity, args.User); - _cloningSystem.CloneComponents(targetIdentity, args.User, settings); + _cloning.CloneComponents(targetIdentity, args.User, settings); if (TryComp(targetIdentity, out var storedIdentity) && storedIdentity.OriginalSession != null) _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(ent.Owner):player} successfully transformed into \"{Name(targetIdentity)}\" ({storedIdentity.OriginalSession:player})"); else _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(ent.Owner):player} successfully transformed into \"{Name(targetIdentity)}\""); - _metaSystem.SetEntityName(ent, Name(targetIdentity), raiseEvents: false); + + _metaData.SetEntityName(ent, Name(targetIdentity), raiseEvents: false); // Don't raise events because we don't want to rename the ID card. + _identity.QueueIdentityUpdate(ent); // We have to manually refresh the identity because we did not raise events. Dirty(ent); @@ -169,5 +200,17 @@ public sealed partial class ChangelingTransformSystem : EntitySystem identity.CurrentIdentity = targetIdentity; Dirty(ent.Owner, identity); } + + var afterTransformEvent = new AfterChangelingTransformEvent(targetIdentity); + RaiseLocalEvent(args.User, afterTransformEvent); + } + + private void StorageBeforeTransform(Entity ent, ref BeforeChangelingTransformEvent args) + { + if (HasComp(args.StoredIdentity)) + return; // If we have a storage component and the target has one as well, then do nothing. + + // If the target identity does not have a storage anymore, drop all items inside our storage so that they don't become unreachable. + _container.EmptyContainer(ent.Comp.Container); } } diff --git a/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs b/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs index a3b620d68a..8e28cd48ad 100644 --- a/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs +++ b/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs @@ -1,9 +1,6 @@ -using System.Numerics; -using Content.Shared.Body; +using System.Linq; using Content.Shared.Changeling.Components; using Content.Shared.Cloning; -using Content.Shared.Humanoid; -using Content.Shared.NameModifier.EntitySystems; using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Network; @@ -15,12 +12,9 @@ namespace Content.Shared.Changeling.Systems; public abstract class SharedChangelingIdentitySystem : EntitySystem { [Dependency] private readonly INetManager _net = default!; - [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly MetaDataSystem _metaSystem = default!; - [Dependency] private readonly NameModifierSystem _nameMod = default!; [Dependency] private readonly SharedCloningSystem _cloningSystem = default!; [Dependency] private readonly SharedMapSystem _map = default!; - [Dependency] private readonly SharedVisualBodySystem _visualBody = default!; [Dependency] private readonly SharedPvsOverrideSystem _pvsOverrideSystem = default!; public MapId? PausedMapId; @@ -34,6 +28,8 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); SubscribeLocalEvent(OnStoredRemove); + + SubscribeLocalEvent(OnDevouredShutdown); } private void OnPlayerAttached(Entity ent, ref PlayerAttachedEvent args) @@ -57,7 +53,32 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem { if (TryComp(ent, out var actor)) CleanupPvsOverride(ent, actor.PlayerSession); + CleanupChangelingNullspaceIdentities(ent); + CleanupDevouredReferences(ent); + } + + // Set all references to this entity to null to prevent PVS errors when networking. + private void OnDevouredShutdown(Entity ent, ref ComponentShutdown args) + { + foreach (var ling in ent.Comp.DevouredBy) + { + if (!TryComp(ling, out var identityComp)) + continue; + + var keysToUpdate = identityComp.ConsumedIdentities + .Where(kvp => kvp.Value == ent.Owner) + .Select(kvp => kvp.Key) + .ToList(); + + if (keysToUpdate.Count == 0) + continue; // No need to dirty. + + foreach (var key in keysToUpdate) + identityComp.ConsumedIdentities[key] = null; + + Dirty(ling, identityComp); + } } private void OnStoredRemove(Entity ent, ref ComponentRemove args) @@ -78,7 +99,23 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem foreach (var consumedIdentity in ent.Comp.ConsumedIdentities) { - QueueDel(consumedIdentity); + QueueDel(consumedIdentity.Key); + } + } + + /// + /// Removes all references to the owning changeling from ChangelingDevouredComponents. + /// + /// The changeling entity + private void CleanupDevouredReferences(Entity ent) + { + foreach (var devouredUid in ent.Comp.ConsumedIdentities.Values) + { + if (!TryComp(devouredUid, out var devouredComp)) + continue; + + if (devouredComp.DevouredBy.Remove(ent.Owner)) + Dirty(devouredUid.Value, devouredComp); } } @@ -88,31 +125,26 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem /// /// The settings to use for cloning. /// The target to clone. - public EntityUid? CloneToPausedMap(CloningSettingsPrototype settings, EntityUid target) + public EntityUid? CloneToPausedMap(ProtoId settings, EntityUid target) { // Don't create client side duplicate clones or a clientside map. if (_net.IsClient) return null; - if (!TryComp(target, out var humanoid) - || !_prototype.Resolve(humanoid.Species, out var speciesPrototype)) + EnsurePausedMap(); + if (PausedMapId == null) return null; - EnsurePausedMap(); - var clone = Spawn(speciesPrototype.Prototype, new MapCoordinates(Vector2.Zero, PausedMapId!.Value)); + var mapCoords = new MapCoordinates(0, 0, PausedMapId.Value); + if (!_cloningSystem.TryCloning(target, mapCoords, settings, out var clone)) + return null; - var storedIdentity = EnsureComp(clone); - storedIdentity.OriginalEntity = target; // TODO: network this once we have WeakEntityReference or the autonetworking source gen is fixed + var storedIdentity = EnsureComp(clone.Value); + storedIdentity.OriginalEntity = target; // TODO: network this once we have a relations system so that this does not cause PVS errors. if (TryComp(target, out var actor)) storedIdentity.OriginalSession = actor.PlayerSession; - _visualBody.CopyAppearanceFrom(target, clone); - _cloningSystem.CloneComponents(target, clone, settings); - - var targetName = _nameMod.GetBaseName(target); - _metaSystem.SetEntityName(clone, targetName); - return clone; } @@ -124,15 +156,12 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem /// The target to clone. public EntityUid? CloneToPausedMap(Entity ent, EntityUid target) { - if (!_prototype.Resolve(ent.Comp.IdentityCloningSettings, out var settings)) - return null; - - var clone = CloneToPausedMap(settings, target); + var clone = CloneToPausedMap(ent.Comp.IdentityCloningSettings, target); if (clone == null) return null; - ent.Comp.ConsumedIdentities.Add(clone.Value); + ent.Comp.ConsumedIdentities.Add(clone.Value, target); Dirty(ent); HandlePvsOverride(ent, clone.Value); @@ -140,6 +169,22 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem return clone; } + /// + /// Drop a stored identity from the changeling's storage. + /// + public void DropStoredIdentity(Entity ent, EntityUid identity) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + if (!HasComp(identity)) + return; // Not a stored identity. + + PredictedQueueDel(identity); + if (ent.Comp.ConsumedIdentities.Remove(identity)) + Dirty(ent); + } + /// /// Simple helper to add a PVS override to a nullspace identity. /// @@ -157,12 +202,12 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem /// Cleanup all PVS overrides for the owner of the ChangelingIdentity /// /// The changeling storing the identities. - /// + /// The session you wish to remove the overrides from. private void CleanupPvsOverride(Entity ent, ICommonSession session) { foreach (var identity in ent.Comp.ConsumedIdentities) { - _pvsOverrideSystem.RemoveSessionOverride(identity, session); + _pvsOverrideSystem.RemoveSessionOverride(identity.Key, session); } } @@ -175,7 +220,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem { foreach (var identity in ent.Comp.ConsumedIdentities) { - _pvsOverrideSystem.AddSessionOverride(identity, session); + _pvsOverrideSystem.AddSessionOverride(identity.Key, session); } } diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs index 8fe17368cc..3928ff7179 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs @@ -23,8 +23,8 @@ public sealed class SolutionTransferSystem : EntitySystem [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - private EntityQuery _refillableQuery; - private EntityQuery _drainableQuery; + [Dependency] private readonly EntityQuery _refillableQuery = default!; + [Dependency] private readonly EntityQuery _drainableQuery = default!; /// /// Default transfer amounts for the set-transfer verb. @@ -40,9 +40,6 @@ public sealed class SolutionTransferSystem : EntitySystem SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnSolutionDrainTransferDoAfter); SubscribeLocalEvent(OnSolutionFillTransferDoAfter); - - _refillableQuery = GetEntityQuery(); - _drainableQuery = GetEntityQuery(); } private void AddSetTransferVerbs(Entity ent, ref GetVerbsEvent args) diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index 9cc0a55ce1..257817394e 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -42,21 +42,18 @@ public sealed partial class ClimbSystem : VirtualController [Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + [Dependency] private readonly EntityQuery _climbableQuery = default!; + [Dependency] private readonly EntityQuery _fixturesQuery = default!; + [Dependency] private readonly EntityQuery _xformQuery = default!; + private const string ClimbingFixtureName = "climb"; private const int ClimbingCollisionGroup = (int) (CollisionGroup.TableLayer | CollisionGroup.LowImpassable); - private EntityQuery _climbableQuery; - private EntityQuery _fixturesQuery; - private EntityQuery _xformQuery; public override void Initialize() { base.Initialize(); - _climbableQuery = GetEntityQuery(); - _fixturesQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - SubscribeLocalEvent(OnMoveAttempt); SubscribeLocalEvent(OnParentChange); SubscribeLocalEvent(OnDoAfter); diff --git a/Content.Shared/Cloning/CloningSettingsPrototype.cs b/Content.Shared/Cloning/CloningSettingsPrototype.cs index 0b531561ca..ffc4dd06dd 100644 --- a/Content.Shared/Cloning/CloningSettingsPrototype.cs +++ b/Content.Shared/Cloning/CloningSettingsPrototype.cs @@ -1,15 +1,13 @@ using Content.Shared.Inventory; using Content.Shared.Whitelist; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; namespace Content.Shared.Cloning; /// -/// Settings for cloning a humanoid. -/// Used to decide which components should be copied. +/// Settings for cloning a humanoid. +/// Used to decide which components should be copied. /// [Prototype] public sealed partial class CloningSettingsPrototype : IPrototype, IInheritingPrototype @@ -18,70 +16,79 @@ public sealed partial class CloningSettingsPrototype : IPrototype, IInheritingPr [IdDataField] public string ID { get; private set; } = default!; + /// [ParentDataField(typeof(PrototypeIdArraySerializer))] public string[]? Parents { get; private set; } + /// [AbstractDataField] [NeverPushInheritance] public bool Abstract { get; private set; } /// - /// Determines if cloning can be prevented by traits etc. + /// Determines if cloning can be prevented by traits etc. /// [DataField] public bool ForceCloning = true; /// - /// Which inventory slots will receive a copy of the original's clothing. - /// Disabled when null. + /// Which inventory slots will receive a copy of the original's clothing. + /// Disabled when null. /// [DataField] public SlotFlags? CopyEquipment = SlotFlags.All; /// - /// Whether or not to copy slime storage and storage implant contents. + /// Whether or not to copy slime storage and storage implant contents. /// [DataField] public bool CopyInternalStorage = true; /// - /// Whether or not to copy implants. + /// Whether or not to copy implants. /// [DataField] public bool CopyImplants = true; /// - /// Should infinite status effects applied to an entity be copied or not? + /// Should infinite status effects (which are used for some traits) applied to an entity be copied or not? /// [DataField] public bool CopyStatusEffects = true; /// - /// Whitelist for the equipment allowed to be copied. + /// Should the event for renaming the clone be raised? + /// This will also set the clone's id card and PDA name if they have one equipped. + /// + [DataField] + public bool RaiseEntityRenamedEvent = true; + + /// + /// Whitelist for the equipment allowed to be copied. /// [DataField] public EntityWhitelist? Whitelist; /// - /// Blacklist for the equipment allowed to be copied. + /// Blacklist for the equipment allowed to be copied. /// [DataField] public EntityWhitelist? Blacklist; /// TODO: Make this not a string https://github.com/space-wizards/RobustToolbox/issues/5709 /// - /// Components to copy from the original to the clone using CopyComp. - /// This makes a deepcopy of all datafields, including information the clone might not own! - /// If you need to exclude data or do additional component initialization, then subscribe to CloningEvent instead! - /// Components in this list that the orginal does not have will be removed from the clone. + /// Components to copy from the original to the clone using CopyComp. + /// This makes a deepcopy of all datafields, including information the clone might not own! + /// If you need to exclude data or do additional component initialization, then subscribe to CloningEvent instead! + /// Components in this list that the orginal does not have will be removed from the clone. /// [DataField] [AlwaysPushInheritance] public HashSet Components = new(); /// - /// Components to remove from the clone and copy over manually using a CloneEvent raised on the original. - /// Use this when the component cannot be copied using CopyComp, for example when having an Uid as a datafield. + /// Components to remove from the clone and copy over manually using a CloneEvent raised on the original. + /// Use this when the component cannot be copied using CopyComp, for example when having an EntityUid as a datafield. /// [DataField] [AlwaysPushInheritance] diff --git a/Content.Shared/Cloning/SharedCloningSystem.cs b/Content.Shared/Cloning/SharedCloningSystem.cs index e44264fb41..ca21d686cf 100644 --- a/Content.Shared/Cloning/SharedCloningSystem.cs +++ b/Content.Shared/Cloning/SharedCloningSystem.cs @@ -1,17 +1,30 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Implants.Components; +using Content.Shared.Inventory; +using Content.Shared.StatusEffectNew.Components; +using Content.Shared.Storage; +using Content.Shared.Whitelist; +using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.Shared.Cloning; public abstract partial class SharedCloningSystem : EntitySystem { + [Dependency] private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!; //TODO: This system has to support both the old and new status effect systems, until the old is able to be fully removed. + [Dependency] private readonly EntityQuery _cloneableEffectQuery = default!; + /// - /// Copy components from one entity to another based on a CloningSettingsPrototype. + /// Spawns a clone of the given humanoid mob at the specified location or in nullspace. /// - /// The orignal Entity to clone components from. - /// The target Entity to clone components to. - /// The clone settings prototype containing the list of components to clone. - public virtual void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings) + public virtual bool TryCloning( + EntityUid original, + MapCoordinates? coords, + ProtoId settingsId, + [NotNullWhen(true)] out EntityUid? clone) { + clone = null; + return false; } /// @@ -20,7 +33,105 @@ public abstract partial class SharedCloningSystem : EntitySystem /// The orignal Entity to clone components from. /// The target Entity to clone components to. /// The clone settings prototype id containing the list of components to clone. - public virtual void CloneComponents(EntityUid original, EntityUid clone, ProtoId settings) + public virtual void CloneComponents( + EntityUid original, + EntityUid clone, + ProtoId settings) { } + + /// + /// Copy components from one entity to another based on a CloningSettingsPrototype. + /// + /// The orignal Entity to clone components from. + /// The target Entity to clone components to. + /// The clone settings prototype containing the list of components to clone. + public virtual void CloneComponents( + EntityUid original, + EntityUid clone, + CloningSettingsPrototype settings) + { + } + + /// + /// Copies the equipment the original has to the clone. + /// This uses the original prototype of the items, so any changes to components that are done after spawning are lost! + /// + public virtual void CopyEquipment( + Entity original, + Entity clone, + SlotFlags slotFlags, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) + { + } + + /// + /// Copies an item and its storage recursively, placing all items at the same position in grid storage. + /// This uses the original prototype of the items, so any changes to components that are done after spawning are lost! + /// + /// + /// This is not perfect and only considers item in storage containers. + /// Some components have their own additional spawn logic on map init, so we cannot just copy all containers. + /// + public virtual EntityUid? CopyItem( + EntityUid original, + EntityCoordinates coords, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) + { + return null; + } + + /// + /// Copies an item's storage recursively to another storage. + /// The storage grids should have the same shape or it will drop on the floor. + /// Basically the same as CopyItem, but we don't copy the outermost container. + /// + public virtual void CopyStorage( + Entity original, + Entity target, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) + { + } + + /// + /// Copies all implants from one mob to another. + /// Might result in duplicates if the target already has them. + /// Can copy the storage inside a storage implant according to a whitelist and blacklist. + /// + /// Entity to copy implants from. + /// Entity to copy implants to. + /// If true will copy storage of the implants (E.g storage implant) + /// Whitelist for the storage copy (If copyStorage is true) + /// Blacklist for the storage copy (If copyStorage is true) + public virtual void CopyImplants( + Entity original, + EntityUid target, + bool copyStorage = false, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) + { + } + + /// + /// Scans all permanent status effects applied to the original entity and transfers them to the clone. + /// + public void CopyStatusEffects(Entity original, Entity target) + { + foreach (var effect in _statusEffects.EnumerateStatusEffects(original, _cloneableEffectQuery)) + { + // We are not interested in temporary effects, only permanent ones. + if (effect.Comp1.EndEffectTime is not null) + continue; + + var effectProto = Prototype(effect); + + if (effectProto is null) + continue; + + _statusEffects.TrySetStatusEffectDuration(target, effectProto); + } + } } diff --git a/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs b/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs index 5caddda18b..4c61c67c6d 100644 --- a/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs +++ b/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs @@ -21,15 +21,19 @@ public sealed partial class ChameleonClothingComponent : Component public SlotFlags Slot; /// - /// EntityPrototype id that chameleon item is trying to mimic. + /// The currently selected EntityPrototype ID that chameleon item is trying to mimic. /// + /// + /// TODO: Rename this, the name "Default" is misleading. + /// Also should not be required, just make null use its original sprites. + /// [DataField(required: true), AutoNetworkedField] public EntProtoId? Default; /// /// Current user that wears chameleon clothing. /// - [ViewVariables] + [DataField, AutoNetworkedField] public EntityUid? User; /// @@ -38,6 +42,18 @@ public sealed partial class ChameleonClothingComponent : Component [DataField] public string? RequireTag; + /// + /// Can this item have its prototype changed by a ? + /// + [DataField] + public bool CanBeSetByController = true; + + /// + /// Show a verb for toggling the UI? + /// + [DataField] + public bool ShowVerb = true; + /// /// Will component owner be affected by EMP pulses? /// diff --git a/Content.Shared/Clothing/Components/FleetingClothingComponent.cs b/Content.Shared/Clothing/Components/FleetingClothingComponent.cs new file mode 100644 index 0000000000..0790d47672 --- /dev/null +++ b/Content.Shared/Clothing/Components/FleetingClothingComponent.cs @@ -0,0 +1,64 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Clothing.Components; + +/// +/// Makes a clothing item despawn when unequipped, stripped or removed in any other way. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class FleetingClothingComponent : Component +{ + /// + /// The sound to play when unequipping. + /// + [DataField] + public SoundSpecifier? RemovedSound; + + /// + /// Should the sound also be played when the player wearing the item unequips it themselves? + /// + [DataField] + public bool PlaySoundOnSelfUnequip = true; + + /// + /// The popup to show to the wearer if they unequip this item themselves. + /// + [DataField] + public LocId? SelfUnquipPopupWearer = "fleeting-clothing-component-default-popup"; + + /// + /// The popup to show to others if the wearer unequips this item themselves. + /// + [DataField] + public LocId? SelfUnquipPopupOthers = "fleeting-clothing-component-default-popup"; + + /// + /// The popup to show to everone if this item was removed by any other means. + /// + /// + /// We can't split this one up into wearer/others popups because EntGotRemovedFromContainerEvent does not have the user passed in. + /// + [DataField] + public LocId? RemovedPopup = "fleeting-clothing-component-default-popup"; + + /// + /// Examine text shown to the wearer. + /// + [DataField] + public LocId? ExamineWearer = "fleeting-clothing-component-default-examine"; + + /// + /// Examine text shown to others. + /// + [DataField] + public LocId? ExamineOthers = "fleeting-clothing-component-default-examine"; + + /// + /// If true this entity will use rather than simply be deleted. + /// Use this if you need to do stuff before deleting it, for example emptying storage containers so that the contents don't get deleted with with. + /// If false the clothing item will just be deleted instead. + /// + [DataField] + public bool DestroyOnUnequip = true; +} diff --git a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs index 6ebaa94e2e..6c7b024b29 100644 --- a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs @@ -88,22 +88,22 @@ public abstract class ClothingSystem : EntitySystem if ((component.Slots & args.SlotFlags) == SlotFlags.NONE) return; - var gotEquippedEvent = new ClothingGotEquippedEvent(args.Equipee, component); + var gotEquippedEvent = new ClothingGotEquippedEvent(args.EquipTarget, component); RaiseLocalEvent(uid, ref gotEquippedEvent); var didEquippedEvent = new ClothingDidEquippedEvent((uid, component)); - RaiseLocalEvent(args.Equipee, ref didEquippedEvent); + RaiseLocalEvent(args.EquipTarget, ref didEquippedEvent); } protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args) { if ((component.Slots & args.SlotFlags) != SlotFlags.NONE) { - var gotUnequippedEvent = new ClothingGotUnequippedEvent(args.Equipee, component); + var gotUnequippedEvent = new ClothingGotUnequippedEvent(args.EquipTarget, component); RaiseLocalEvent(uid, ref gotUnequippedEvent); var didUnequippedEvent = new ClothingDidUnequippedEvent((uid, component)); - RaiseLocalEvent(args.Equipee, ref didUnequippedEvent); + RaiseLocalEvent(args.EquipTarget, ref didUnequippedEvent); } component.InSlot = null; @@ -139,6 +139,21 @@ public abstract class ClothingSystem : EntitySystem #region Public API + /// + /// Returns true if this clothing item is currently inside an inventory slot AND that slot is considered valid for equipping. + /// For example putting shoes into your pockets does not count as being equipped. + /// + public bool IsEquipped(Entity item) + { + if (!Resolve(item, ref item.Comp, false)) + return false; + + if ((item.Comp.Slots & item.Comp.InSlotFlag) != SlotFlags.NONE) + return true; + + return false; + } + public void SetEquippedPrefix(EntityUid uid, string? prefix, ClothingComponent? clothing = null) { if (!Resolve(uid, ref clothing, false)) diff --git a/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs index 76b7b9aa66..5c0e936bdf 100644 --- a/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs @@ -22,8 +22,8 @@ public sealed class FactionClothingSystem : EntitySystem private void OnEquipped(Entity ent, ref GotEquippedEvent args) { - TryComp(args.Equipee, out var factionComp); - var faction = (args.Equipee, factionComp); + TryComp(args.EquipTarget, out var factionComp); + var faction = (args.EquipTarget, factionComp); ent.Comp.AlreadyMember = _faction.IsMember(faction, ent.Comp.Faction); _faction.AddFaction(faction, ent.Comp.Faction); @@ -37,6 +37,6 @@ public sealed class FactionClothingSystem : EntitySystem return; } - _faction.RemoveFaction(args.Equipee, ent.Comp.Faction); + _faction.RemoveFaction(args.EquipTarget, ent.Comp.Faction); } } diff --git a/Content.Shared/Clothing/EntitySystems/FleetingClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/FleetingClothingSystem.cs new file mode 100644 index 0000000000..e9a61dd8b3 --- /dev/null +++ b/Content.Shared/Clothing/EntitySystems/FleetingClothingSystem.cs @@ -0,0 +1,117 @@ +using Content.Shared.Clothing.Components; +using Content.Shared.Destructible; +using Content.Shared.Examine; +using Content.Shared.Inventory.Events; +using Content.Shared.Popups; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; +using Robust.Shared.Timing; + +namespace Content.Shared.Clothing.EntitySystems; + +public sealed class FleetingClothingSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly ClothingSystem _clothing = default!; + [Dependency] private readonly SharedDestructibleSystem _destructibleSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnBeforeGettingUnequipped); + SubscribeLocalEvent(OnGotUnequipped); + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + string? examineText = null; + // check if the item is clothing, is currently equipped and if the wearer is the examiner + if (_clothing.IsEquipped(ent.Owner) && Transform(ent.Owner).ParentUid == args.Examiner) + { + // text to show to the wearer only + if (ent.Comp.ExamineWearer != null) + examineText = Loc.GetString(ent.Comp.ExamineWearer); + } + else + { + // text to show to everyone else + if (ent.Comp.ExamineOthers != null) + examineText = Loc.GetString(ent.Comp.ExamineOthers); + } + + if (!string.IsNullOrEmpty(examineText)) + args.PushMarkup(examineText); + } + + // Raised before the item is being unequipped. + // We have to use QueueDel instead of Del because directly deleting the entity while an event is being raised on it will cause errors. + // We can't do this in.GotUnequippedEvent because container events don't include the user. + private void OnBeforeGettingUnequipped(Entity ent, ref BeforeGettingUnequippedEvent args) + { + if (ent.Comp.DestroyOnUnequip) + _destructibleSystem.DestroyEntity(ent.Owner); // Empty containers first. + else + PredictedQueueDel(ent.Owner); // Poof! + + // Use coords because the entity will be deleted. + var coords = Transform(ent).Coordinates; + if (ent.Comp.PlaySoundOnSelfUnequip || args.User != args.EquipTarget) + _audio.PlayPredicted(ent.Comp.RemovedSound, coords, args.User); + + if (args.User == args.EquipTarget) + { + // If the wearer removes the item themselves show specific messages to them and others. + string? selfMessage = null; + string? othersMessage = null; + if (ent.Comp.SelfUnquipPopupWearer != null) + selfMessage = Loc.GetString(ent.Comp.SelfUnquipPopupWearer, ("item", ent.Owner)); + if (ent.Comp.SelfUnquipPopupOthers != null) + othersMessage = Loc.GetString(ent.Comp.SelfUnquipPopupOthers, ("item", ent.Owner)); + + // Use the wearer for the popup location because the item item itself will get deleted. + _popup.PopupPredicted(selfMessage, othersMessage, args.EquipTarget, args.User, PopupType.LargeCaution); + } + else + { + // Show the same popup message for everyone. + if (ent.Comp.RemovedPopup != null) + _popup.PopupPredicted(Loc.GetString(ent.Comp.RemovedPopup, ("item", ent.Owner)), args.EquipTarget, args.User, PopupType.LargeCaution); + } + } + + // In case the item was somehow removed by any other means not using TryUnequip. + private void OnGotUnequipped(Entity ent, ref GotUnequippedEvent args) + { + if (_timing.ApplyingState) + return; // The results of the container change were already networked as part of the same game state. + + if (Terminating(ent.Owner) || EntityManager.IsQueuedForDeletion(ent.Owner)) + return; // Don't do the popups or sound twice. + + if (Terminating(args.EquipTarget)) + return; // Don't do anything if the item is removed due to the wearer getting deleted. + + if (ent.Comp.DestroyOnUnequip) + _destructibleSystem.DestroyEntity(ent.Owner); // Empty containers first. + else + PredictedQueueDel(ent.Owner); // Poof! + + // Can't predict the popup or sound without a user. + // TODO: Make the popup and sound API sane and remove this guard. + if (_net.IsClient) + return; + + // Use the wearer for the popup location because the item item itself will get deleted. + _audio.PlayPvs(ent.Comp.RemovedSound, args.EquipTarget); + if (ent.Comp.RemovedPopup != null) + _popup.PopupEntity(Loc.GetString(ent.Comp.RemovedPopup, ("item", ent.Owner)), args.EquipTarget, PopupType.LargeCaution); + } +} diff --git a/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs index 49df7aee94..9890832ee7 100644 --- a/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs @@ -63,7 +63,7 @@ public sealed partial class PilotedClothingSystem : EntitySystem if (!isCorrectSlot) return; - entity.Comp.Wearer = args.Equipee; + entity.Comp.Wearer = args.EquipTarget; Dirty(entity); // Attempt to setup control link, if Pilot and Wearer are both present. diff --git a/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs index ab0c41c5c7..e722735267 100644 --- a/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs @@ -23,7 +23,7 @@ public sealed class SelfUnremovableClothingSystem : EntitySystem if (TryComp(selfUnremovableClothing, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) return; - if (args.UnEquipTarget == args.Unequipee) + if (args.UnEquipTarget == args.User) { args.Cancel(); } diff --git a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs index 07aae61f83..ccd0e04953 100644 --- a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs @@ -66,12 +66,20 @@ public abstract class SharedChameleonClothingSystem : EntitySystem private void OnGotEquipped(EntityUid uid, ChameleonClothingComponent component, GotEquippedEvent args) { - component.User = args.Equipee; + if (Timing.ApplyingState) + return; // Already networked as part of the same gamestate + + component.User = args.EquipTarget; + Dirty(uid, component); } private void OnGotUnequipped(EntityUid uid, ChameleonClothingComponent component, GotUnequippedEvent args) { + if (Timing.ApplyingState) + return; // Already networked as part of the same gamestate + component.User = null; + Dirty(uid, component); } // Updates chameleon visuals and meta information. @@ -134,6 +142,9 @@ public abstract class SharedChameleonClothingSystem : EntitySystem if (!args.CanAccess || !args.CanInteract || _lock.IsLocked(ent.Owner)) return; + if (!ent.Comp.ShowVerb) + return; + // Can't pass args from a ref event inside of lambdas var user = args.User; @@ -184,6 +195,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem // check if it's valid clothing if (!proto.TryGetComponent(out ClothingComponent? clothing, Factory)) return false; + if (!clothing.Slots.HasFlag(chameleonSlot)) return false; @@ -250,7 +262,15 @@ public abstract class SharedChameleonClothingSystem : EntitySystem } // TODO: Predict and use component states for the UI - public virtual void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false, + /// + /// Change chameleon items name, description and sprite to mimic other entity prototype. + /// + /// The entity who's appearance to swap. + /// The target protoId of the target appearance. + /// Whether to force update appearance, even if the same one was selected. + /// Whether to validate if the target prototype is a valid chameleon target. + /// The of the entity we are updating. + public virtual void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false, bool validate = true, ChameleonClothingComponent? component = null) { } } diff --git a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs index a2b7d01641..ce167e06f4 100644 --- a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs @@ -160,7 +160,7 @@ public sealed class ToggleableClothingSystem : EntitySystem // This should maybe double check that the entity currently in the slot is actually the attached clothing, but // if its not, then something else has gone wrong already... if (component.Container != null && component.Container.ContainedEntity == null && component.ClothingUid != null) - _inventorySystem.TryUnequip(args.Equipee, component.Slot, force: true, triggerHandContact: true); + _inventorySystem.TryUnequip(args.EquipTarget, component.Slot, force: true, triggerHandContact: true); } private void OnRemoveToggleable(EntityUid uid, ToggleableClothingComponent component, ComponentRemove args) diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index 3985bd3051..495dddfe69 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -34,7 +34,7 @@ public sealed partial class AnchorableSystem : EntitySystem [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - private EntityQuery _physicsQuery; + [Dependency] private readonly EntityQuery _physicsQuery = default!; public readonly ProtoId Unstackable = "Unstackable"; @@ -42,8 +42,6 @@ public sealed partial class AnchorableSystem : EntitySystem { base.Initialize(); - _physicsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnInteractUsing, before: new[] { typeof(ItemSlotsSystem) }, after: new[] { typeof(SharedConstructionSystem) }); SubscribeLocalEvent(OnAnchorComplete); @@ -235,12 +233,8 @@ public sealed partial class AnchorableSystem : EntitySystem // Log anchor attempt (server only) _adminLogger.Add(LogType.Anchor, LogImpact.Low, $"{ToPrettyString(userUid):user} is trying to anchor {ToPrettyString(uid):entity} to {transform.Coordinates:targetlocation}"); - if (TryComp(uid, out var anchorBody) && - !TileFree(transform.Coordinates, anchorBody)) - { - _popup.PopupClient(Loc.GetString("anchorable-occupied"), uid, userUid); + if (!CanAnchorAt(uid, transform.Coordinates, userUid)) return; - } if (AnyUnstackable(uid, transform.Coordinates)) { @@ -285,6 +279,23 @@ public sealed partial class AnchorableSystem : EntitySystem return !attempt.Cancelled; } + public bool CanAnchorAt(Entity entity, EntityUid? user = null) + { + return CanAnchorAt(entity, Transform(entity).Coordinates, user); + } + + public bool CanAnchorAt(Entity entity, EntityCoordinates coordinates, EntityUid? user = null) + { + if (!Resolve(entity, ref entity.Comp)) + return true; + + if (TileFree(coordinates, entity.Comp)) + return true; + + _popup.PopupClient(Loc.GetString("anchorable-occupied"), entity, user); + return false; + } + /// /// Returns true if no hard anchored entities exist on the coordinate tile that would collide with the provided physics body. /// diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index f8efa20afa..577b7dfe2c 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -795,14 +795,14 @@ namespace Content.Shared.Cuffs private void OnEquipAttempt(EntityUid uid, CuffableComponent component, IsEquippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Equipee == uid) + if (args.User == uid) CheckAct(uid, component, args); } private void OnUnequipAttempt(EntityUid uid, CuffableComponent component, IsUnequippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Unequipee == uid) + if (args.User == uid) CheckAct(uid, component, args); } diff --git a/Content.Shared/Damage/Components/DamageModifierStatusEffectComponent.cs b/Content.Shared/Damage/Components/DamageModifierStatusEffectComponent.cs new file mode 100644 index 0000000000..51a80a12fe --- /dev/null +++ b/Content.Shared/Damage/Components/DamageModifierStatusEffectComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Damage.Components; + +/// +/// Component used on a status effect entity to modify damage taken. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class DamageModifierStatusEffectComponent : Component +{ + /// + /// The modifier prototype to apply. + /// + [DataField(required: true)] + public DamageModifierSet Modifiers; +} diff --git a/Content.Shared/Damage/Components/DamageProtectionBuffComponent.cs b/Content.Shared/Damage/Components/DamageProtectionBuffComponent.cs deleted file mode 100644 index 99b055a645..0000000000 --- a/Content.Shared/Damage/Components/DamageProtectionBuffComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Shared.Damage.Prototypes; -using Robust.Shared.GameStates; - -namespace Content.Shared.Damage.Components; - -/// -/// Applies the specified DamageModifierSets when the entity takes damage. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class DamageProtectionBuffComponent : Component -{ - /// - /// The damage modifiers for entities with this component. - /// - [DataField] - public Dictionary Modifiers = new(); -} diff --git a/Content.Shared/Damage/Systems/DamageContactsSystem.cs b/Content.Shared/Damage/Systems/DamageContactsSystem.cs index 9a53d83eb2..820fc3c90b 100644 --- a/Content.Shared/Damage/Systems/DamageContactsSystem.cs +++ b/Content.Shared/Damage/Systems/DamageContactsSystem.cs @@ -14,6 +14,8 @@ public sealed class DamageContactsSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly EntityQuery _damageQuery = default!; + public override void Initialize() { base.Initialize(); @@ -45,13 +47,12 @@ public sealed class DamageContactsSystem : EntitySystem if (!TryComp(otherUid, out var body)) return; - var damageQuery = GetEntityQuery(); foreach (var ent in _physics.GetContactingEntities(otherUid, body)) { if (ent == uid) continue; - if (damageQuery.HasComponent(ent)) + if (_damageQuery.HasComponent(ent)) return; } diff --git a/Content.Shared/Damage/Systems/DamageModifierStatusEffect.cs b/Content.Shared/Damage/Systems/DamageModifierStatusEffect.cs new file mode 100644 index 0000000000..a47b239330 --- /dev/null +++ b/Content.Shared/Damage/Systems/DamageModifierStatusEffect.cs @@ -0,0 +1,19 @@ +using Content.Shared.Damage.Components; +using Content.Shared.StatusEffectNew; + +namespace Content.Shared.Damage.Systems; + +public sealed class DamageModifierStatusEffectSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnDamageModifyStatus); + } + + private void OnDamageModifyStatus(Entity status, ref StatusEffectRelayedEvent args) + { + args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, status.Comp.Modifiers); + } +} diff --git a/Content.Shared/Damage/Systems/DamageProtectionBuffSystem.cs b/Content.Shared/Damage/Systems/DamageProtectionBuffSystem.cs deleted file mode 100644 index bbb7bfda32..0000000000 --- a/Content.Shared/Damage/Systems/DamageProtectionBuffSystem.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Shared.Damage.Components; - -namespace Content.Shared.Damage.Systems; - -public sealed class DamageProtectionBuffSystem : EntitySystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnDamageModify); - } - - private void OnDamageModify(EntityUid uid, DamageProtectionBuffComponent component, DamageModifyEvent args) - { - foreach (var modifier in component.Modifiers.Values) - args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modifier); - } -} diff --git a/Content.Shared/Damage/Systems/DamageableSystem.Events.cs b/Content.Shared/Damage/Systems/DamageableSystem.Events.cs index c9cf625cbc..36f997c5a9 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.Events.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.Events.cs @@ -23,9 +23,6 @@ public sealed partial class DamageableSystem SubscribeLocalEvent(DamageableHandleState); SubscribeLocalEvent(DamageableGetState); - _appearanceQuery = GetEntityQuery(); - _damageableQuery = GetEntityQuery(); - // Damage modifier CVars are updated and stored here to be queried in other systems. // Note that certain modifiers requires reloading the guidebook. Subs.CVar( diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 2d707568f0..fd8564cf3c 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -23,8 +23,8 @@ public sealed partial class DamageableSystem : EntitySystem [Dependency] private readonly SharedChemistryGuideDataSystem _chemistryGuideData = default!; [Dependency] private readonly SharedExplosionSystem _explosion = default!; - private EntityQuery _appearanceQuery; - private EntityQuery _damageableQuery; + [Dependency] private readonly EntityQuery _appearanceQuery = default!; + [Dependency] private readonly EntityQuery _damageableQuery = default!; public float UniversalAllDamageModifier { get; private set; } = 1f; public float UniversalAllHealModifier { get; private set; } = 1f; diff --git a/Content.Shared/Damage/Systems/SharedStaminaSystem.cs b/Content.Shared/Damage/Systems/SharedStaminaSystem.cs index aaca59620b..5b5018b4f9 100644 --- a/Content.Shared/Damage/Systems/SharedStaminaSystem.cs +++ b/Content.Shared/Damage/Systems/SharedStaminaSystem.cs @@ -55,6 +55,8 @@ public abstract partial class SharedStaminaSystem : EntitySystem [Dependency] private readonly SharedBuckleSystem _buckle = default!; // WL Golem species end + [Dependency] private readonly EntityQuery _stamQuery = default!; + /// /// How much of a buffer is there between the stun duration and when stuns can be re-applied. /// @@ -172,13 +174,12 @@ public abstract partial class SharedStaminaSystem : EntitySystem if (ev.Cancelled) return; - var stamQuery = GetEntityQuery(); var toHit = new List<(EntityUid Entity, StaminaComponent Component)>(); // Split stamina damage between all eligible targets. foreach (var ent in args.HitEntities) { - if (!stamQuery.TryGetComponent(ent, out var stam)) + if (!_stamQuery.TryGetComponent(ent, out var stam)) continue; toHit.Add((ent, stam)); @@ -361,14 +362,13 @@ public abstract partial class SharedStaminaSystem : EntitySystem { base.Update(frameTime); - var stamQuery = GetEntityQuery(); var query = EntityQueryEnumerator(); var curTime = Timing.CurTime; while (query.MoveNext(out var uid, out _)) { // Just in case we have active but not stamina we'll check and account for it. - if (!stamQuery.TryGetComponent(uid, out var comp) || + if (!_stamQuery.TryComp(uid, out var comp) || comp.StaminaDamage <= 0f && !comp.Critical) { RemComp(uid); @@ -407,8 +407,7 @@ public abstract partial class SharedStaminaSystem : EntitySystem component.Critical = true; component.StaminaDamage = component.CritThreshold; - if (StunSystem.TryUpdateParalyzeDuration(uid, component.StunTime)) - StunSystem.TrySeeingStars(uid); + StunSystem.TryUpdateParalyzeDuration(uid, component.StunTime); // WL Golem species start if (!_buckle.IsBuckled(uid)) diff --git a/Content.Shared/Destructible/SharedDestructibleSystem.cs b/Content.Shared/Destructible/SharedDestructibleSystem.cs index 4917ded326..076125bc8a 100644 --- a/Content.Shared/Destructible/SharedDestructibleSystem.cs +++ b/Content.Shared/Destructible/SharedDestructibleSystem.cs @@ -10,7 +10,7 @@ public abstract class SharedDestructibleSystem : EntitySystem /// /// Force entity to be destroyed and deleted. /// - public bool DestroyEntity(Entity owner) + public bool DestroyEntity(EntityUid owner) { var ev = new DestructionAttemptEvent(); RaiseLocalEvent(owner, ev); diff --git a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs index 74bbc259c6..9e4e964f0a 100644 --- a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs +++ b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs @@ -18,6 +18,8 @@ public abstract class SharedDeviceLinkSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly EntityQuery _deviceLinkSinkQuery = default!; + public const string InvokedPort = "link_port"; /// @@ -82,10 +84,9 @@ public abstract class SharedDeviceLinkSystem : EntitySystem /// private void OnSourceRemoved(Entity source, ref ComponentRemove args) { - var query = GetEntityQuery(); foreach (var sinkUid in source.Comp.LinkedPorts.Keys) { - if (query.TryGetComponent(sinkUid, out var sink)) + if (_deviceLinkSinkQuery.TryGetComponent(sinkUid, out var sink)) RemoveSinkFromSourceInternal(source, sinkUid, source, sink); else Log.Error($"Device source {ToPrettyString(source)} links to invalid entity: {ToPrettyString(sinkUid)}"); diff --git a/Content.Shared/DisplacementMap/DisplacementData.cs b/Content.Shared/DisplacementMap/DisplacementData.cs index 79f89a1d25..8bc922708f 100644 --- a/Content.Shared/DisplacementMap/DisplacementData.cs +++ b/Content.Shared/DisplacementMap/DisplacementData.cs @@ -13,4 +13,7 @@ public sealed partial class DisplacementData [DataField] public string? ShaderOverride = "DisplacedDraw"; + + [DataField] + public string ShaderOverrideUnshaded = "DisplacedDrawUnshaded"; } diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index 14c2ae67d3..5e4b742e10 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -18,6 +18,8 @@ public abstract partial class SharedDoAfterSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly EntityQuery _handsQuery = default!; + private DoAfter[] _doAfters = Array.Empty(); public override void Update(float frameTime) @@ -25,8 +27,6 @@ public abstract partial class SharedDoAfterSystem : EntitySystem base.Update(frameTime); var time = GameTiming.CurTime; - var xformQuery = GetEntityQuery(); - var handsQuery = GetEntityQuery(); var enumerator = EntityQueryEnumerator(); while (enumerator.MoveNext(out var uid, out var active, out var comp)) @@ -34,7 +34,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem try { - Update(uid, active, comp, time, xformQuery, handsQuery); + Update(uid, active, comp, time); } // ReSharper disable once RedundantCatchClause #if EXCEPTION_TOLERANCE @@ -87,9 +87,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem EntityUid uid, ActiveDoAfterComponent active, DoAfterComponent comp, - TimeSpan time, - EntityQuery xformQuery, - EntityQuery handsQuery) + TimeSpan time) { var dirty = false; @@ -122,7 +120,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem continue; } - if (ShouldCancel(doAfter, xformQuery, handsQuery)) + if (ShouldCancel(doAfter)) { InternalCancel(doAfter, comp); dirty = true; @@ -196,27 +194,24 @@ public abstract partial class SharedDoAfterSystem : EntitySystem } } - private bool ShouldCancel(DoAfter doAfter, - EntityQuery xformQuery, - EntityQuery handsQuery) + private bool ShouldCancel(DoAfter doAfter) { var args = doAfter.Args; - //re-using xformQuery for Exists() checks. - if (args.Used is { } used && !xformQuery.HasComponent(used)) + if (args.Used is { } used && !Exists(used)) return true; - if (args.EventTarget is { Valid: true } eventTarget && !xformQuery.HasComponent(eventTarget)) + if (args.EventTarget is { Valid: true } eventTarget && !Exists(eventTarget)) return true; - if (!xformQuery.TryGetComponent(args.User, out var userXform)) + if (!TryComp(args.User, out TransformComponent? userXform)) return true; TransformComponent? targetXform = null; - if (args.Target is { } target && !xformQuery.TryGetComponent(target, out targetXform)) + if (args.Target is { } target && !TryComp(target, out targetXform)) return true; - if (args.Used is { } @using && !xformQuery.HasComp(@using)) + if (args.Used is { } @using && !Exists(@using)) return true; // TODO: Re-use existing xform query for these calculations. @@ -265,7 +260,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem // This does not mean their hand needs to be empty. if (args.NeedHand) { - if (!handsQuery.TryGetComponent(args.User, out var hands) || hands.Count == 0) + if (!_handsQuery.TryGetComponent(args.User, out var hands) || hands.Count == 0) return true; // If an item was in the user's hand to begin with, diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs index 0b72692ea0..8841d8dd26 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -251,7 +251,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem doAfter.NetInitialItem = GetNetEntity(doAfter.InitialItem); // Initial checks - if (ShouldCancel(doAfter, GetEntityQuery(), GetEntityQuery())) + if (ShouldCancel(doAfter)) return false; if (args.AttemptFrequency == AttemptFrequency.StartAndEnd && !TryAttemptEvent(doAfter)) diff --git a/Content.Shared/Doors/Components/AirlockComponent.cs b/Content.Shared/Doors/Components/AirlockComponent.cs index 6b3fcfad7e..136f49b4fc 100644 --- a/Content.Shared/Doors/Components/AirlockComponent.cs +++ b/Content.Shared/Doors/Components/AirlockComponent.cs @@ -24,7 +24,7 @@ public sealed partial class AirlockComponent : Component [ViewVariables(VVAccess.ReadWrite)] [DataField, AutoNetworkedField] public bool EmergencyAccess = false; - + /// /// Sound to play when the airlock emergency access is turned on. /// @@ -83,6 +83,9 @@ public sealed partial class AirlockComponent : Component [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] public string AutoClosePort = "AutoClose"; + [DataField] + public LocId PryFailedPopup = "airlock-component-cannot-pry-is-powered-message"; + #region Graphics /// @@ -115,6 +118,13 @@ public sealed partial class AirlockComponent : Component [DataField] public string OpeningPanelSpriteState = "panel_opening"; + /// + /// The sprite state to use for the wire panel when the airlock is open. The + /// first frame will be used for when the airlock is closed. + /// + [DataField] + public string OpenPanelSpriteState = "panel_open"; + /// /// The sprite state used to animate the airlock frame when the airlock closes. /// diff --git a/Content.Shared/Doors/Components/DoorComponent.cs b/Content.Shared/Doors/Components/DoorComponent.cs index e66dff2611..057fd5982d 100644 --- a/Content.Shared/Doors/Components/DoorComponent.cs +++ b/Content.Shared/Doors/Components/DoorComponent.cs @@ -140,7 +140,17 @@ public sealed partial class DoorComponent : Component /// /// The key used when playing door opening/closing/emagging/deny animations. /// - public const string AnimationKey = "door_animation"; + public const string OpenCloseKey = "door_animation_openclose"; + + /// + /// The key used when playing door deny animations. + /// + public const string DenyKey = "door_animation_deny"; + + /// + /// The key used when playing door emag animations. + /// + public const string EmagKey = "door_animation_emag"; /// /// The sprite state used for the door when it's open. @@ -153,7 +163,7 @@ public sealed partial class DoorComponent : Component /// The sprite states used for the door while it's open. /// [ViewVariables(VVAccess.ReadOnly)] - public List<(DoorVisualLayers, string)> OpenSpriteStates = default!; + public List<(Enum, string)> OpenSpriteStates = default!; /// /// The sprite state used for the door when it's closed. @@ -166,7 +176,7 @@ public sealed partial class DoorComponent : Component /// The sprite states used for the door while it's closed. /// [ViewVariables(VVAccess.ReadOnly)] - public List<(DoorVisualLayers, string)> ClosedSpriteStates = default!; + public List<(Enum, string)> ClosedSpriteStates = default!; /// /// The sprite state used for the door when it's opening. diff --git a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs index 5bbde04aed..23c8c463e6 100644 --- a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs +++ b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs @@ -1,6 +1,9 @@ +using Content.Shared.DeviceLinking.Events; using Content.Shared.Doors.Components; +using Content.Shared.Interaction; using Robust.Shared.Audio.Systems; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Prying.Components; using Content.Shared.Wires; using Robust.Shared.Timing; @@ -27,66 +30,69 @@ public abstract class SharedAirlockSystem : EntitySystem SubscribeLocalEvent(OnBeforeDoorDenied); SubscribeLocalEvent(OnGetPryMod); SubscribeLocalEvent(OnBeforePry); + SubscribeLocalEvent(OnSignalReceived); + SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnActivate, before: new[] { typeof(SharedDoorSystem) }); } - private void OnBeforeDoorClosed(EntityUid uid, AirlockComponent airlock, BeforeDoorClosedEvent args) + private void OnBeforeDoorClosed(Entity ent, ref BeforeDoorClosedEvent args) { if (args.Cancelled) return; - if (!airlock.Safety) + if (!ent.Comp.Safety) args.PerformCollisionCheck = false; // only block based on bolts / power status when initially closing the door, not when its already // mid-transition. Particularly relevant for when the door was pried-closed with a crowbar, which bypasses // the initial power-check. - if (TryComp(uid, out DoorComponent? door) + if (HasComp(ent) && !args.Partial - && !CanChangeState(uid, airlock)) + && !CanChangeState(ent)) { args.Cancel(); } } - private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args) + private void OnStateChanged(Entity ent, ref DoorStateChangedEvent args) { // This is here so we don't accidentally bulldoze state values and mispredict. if (_timing.ApplyingState) return; // Only show the maintenance panel if the airlock is closed - if (TryComp(uid, out var wiresPanel)) + if (TryComp(ent, out var wiresPanel)) { - _wiresSystem.ChangePanelVisibility(uid, wiresPanel, component.OpenPanelVisible || args.State != DoorState.Open); + _wiresSystem.ChangePanelVisibility(ent, wiresPanel, ent.Comp.OpenPanelVisible || args.State != DoorState.Open); } // If the door is closed, we should look if the bolt was locked while closing - UpdateAutoClose(uid, component); + UpdateAutoClose((ent, ent.Comp)); // Make sure the airlock auto closes again next time it is opened if (args.State == DoorState.Closed) { - component.AutoClose = true; - Dirty(uid, component); + ent.Comp.AutoClose = true; + Dirty(ent); } } - private void OnBoltsChanged(EntityUid uid, AirlockComponent component, DoorBoltsChangedEvent args) + private void OnBoltsChanged(Entity ent, ref DoorBoltsChangedEvent args) { // If unbolted, reset the auto close timer if (!args.BoltsDown) - UpdateAutoClose(uid, component); + UpdateAutoClose((ent, ent.Comp)); } - private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args) + private void OnBeforeDoorOpened(Entity ent, ref BeforeDoorOpenedEvent args) { - if (!CanChangeState(uid, component)) + if (!CanChangeState(ent)) args.Cancel(); } - private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args) + private void OnBeforeDoorDenied(Entity ent, ref BeforeDoorDeniedEvent args) { - if (!CanChangeState(uid, component)) + if (!CanChangeState(ent)) args.Cancel(); } @@ -102,44 +108,86 @@ public abstract class SharedAirlockSystem : EntitySystem /// /// Updates the auto close timer. /// - public void UpdateAutoClose(EntityUid uid, AirlockComponent? airlock = null, DoorComponent? door = null) + public void UpdateAutoClose(Entity ent) { - if (!Resolve(uid, ref airlock, ref door)) + if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2)) return; - if (door.State != DoorState.Open) + if (ent.Comp2.State != DoorState.Open) return; - if (!airlock.AutoClose) + if (!ent.Comp1.AutoClose) return; - if (!CanChangeState(uid, airlock)) + if (!CanChangeState((ent.Owner, ent.Comp1))) return; var autoev = new BeforeDoorAutoCloseEvent(); - RaiseLocalEvent(uid, autoev); + RaiseLocalEvent(ent, autoev); if (autoev.Cancelled) return; - DoorSystem.SetNextStateChange(uid, airlock.AutoCloseDelay * airlock.AutoCloseDelayModifier); + DoorSystem.SetNextStateChange(ent, ent.Comp1.AutoCloseDelay * ent.Comp1.AutoCloseDelayModifier); } - private void OnBeforePry(EntityUid uid, AirlockComponent component, ref BeforePryEvent args) + private void OnBeforePry(Entity ent, ref BeforePryEvent args) { if (args.Cancelled) return; - if (!component.Powered || args.PryPowered) + if (!ent.Comp.Powered || args.PryPowered) return; - args.Message = "airlock-component-cannot-pry-is-powered-message"; + args.Message = ent.Comp.PryFailedPopup; args.Cancelled = true; } - public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component) + private void OnSignalReceived(Entity ent, ref SignalReceivedEvent args) { - Appearance.SetData(uid, DoorVisuals.EmergencyLights, component.EmergencyAccess); + if (args.Port == ent.Comp.AutoClosePort && ent.Comp.AutoClose) + { + ent.Comp.AutoClose = false; + Dirty(ent); + } + } + + private void OnPowerChanged(Entity ent, ref PowerChangedEvent args) + { + ent.Comp.Powered = args.Powered; + Dirty(ent); + + if (!TryComp(ent, out DoorComponent? door)) + return; + + if (!args.Powered) + { + // stop any scheduled auto-closing + if (door.State == DoorState.Open) + DoorSystem.SetNextStateChange(ent, null); + } + else + { + UpdateAutoClose((ent, ent.Comp, door)); + } + } + + private void OnActivate(Entity ent, ref ActivateInWorldEvent args) + { + if (args.Handled || !args.Complex) + return; + + if (ent.Comp.KeepOpenIfClicked && ent.Comp.AutoClose) + { + // Disable auto close + ent.Comp.AutoClose = false; + Dirty(ent); + } + } + + public void UpdateEmergencyLightStatus(Entity ent) + { + Appearance.SetData(ent, DoorVisuals.EmergencyLights, ent.Comp.EmergencyAccess); } public void SetEmergencyAccess(Entity ent, bool value, EntityUid? user = null, bool predicted = false) @@ -152,7 +200,7 @@ public abstract class SharedAirlockSystem : EntitySystem ent.Comp.EmergencyAccess = value; Dirty(ent, ent.Comp); // This only runs on the server apparently so we need this. - UpdateEmergencyLightStatus(ent, ent.Comp); + UpdateEmergencyLightStatus(ent); var sound = ent.Comp.EmergencyAccess ? ent.Comp.EmergencyOnSound : ent.Comp.EmergencyOffSound; if (predicted) @@ -174,8 +222,8 @@ public abstract class SharedAirlockSystem : EntitySystem component.Safety = value; } - public bool CanChangeState(EntityUid uid, AirlockComponent component) + public bool CanChangeState(Entity ent) { - return component.Powered && !DoorSystem.IsBolted(uid); + return ent.Comp.Powered && !DoorSystem.IsBolted(ent); } } diff --git a/Content.Shared/Emp/SharedEmpSystem.cs b/Content.Shared/Emp/SharedEmpSystem.cs index e1ef05c8ce..0c902798b1 100644 --- a/Content.Shared/Emp/SharedEmpSystem.cs +++ b/Content.Shared/Emp/SharedEmpSystem.cs @@ -18,8 +18,9 @@ public abstract class SharedEmpSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EntityQuery _resistanceQuery = default!; + private HashSet _entSet = new(); - private EntityQuery _resistanceQuery; public override void Initialize() { @@ -30,8 +31,6 @@ public abstract class SharedEmpSystem : EntitySystem SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnResistEmpAttempt); - - _resistanceQuery = GetEntityQuery(); } public static readonly EntProtoId EmpPulseEffectPrototype = "EffectEmpPulse"; diff --git a/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs b/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs index aa5132e596..9614933247 100644 --- a/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs +++ b/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs @@ -30,6 +30,6 @@ public sealed partial class CreateGas : EntityEffectBase return Loc.GetString("entity-effect-guidebook-create-gas", ("chance", Probability), ("moles", Moles), - ("gas", gasProto.Name)); + ("gas", Loc.GetString(gasProto.Name))); } } diff --git a/Content.Shared/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzu.cs b/Content.Shared/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzu.cs new file mode 100644 index 0000000000..2ab68708cc --- /dev/null +++ b/Content.Shared/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzu.cs @@ -0,0 +1,10 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; + +public sealed partial class PlantRemoveKudzu : EntityEffectBase +{ + /// + public override string EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => + Loc.GetString("entity-effect-guidebook-plant-remove-kudzu", ("chance", Probability)); +} diff --git a/Content.Shared/EntityEffects/Effects/WashCreamPieEntityEffectSystem.cs b/Content.Shared/EntityEffects/Effects/WashCreamPieEntityEffectSystem.cs index c54912191e..9b3d731fcc 100644 --- a/Content.Shared/EntityEffects/Effects/WashCreamPieEntityEffectSystem.cs +++ b/Content.Shared/EntityEffects/Effects/WashCreamPieEntityEffectSystem.cs @@ -15,7 +15,7 @@ public sealed partial class WashCreamPieEntityEffectSystem : EntityEffectSystem< protected override void Effect(Entity entity, ref EntityEffectEvent args) { - _creamPie.SetCreamPied(entity, entity.Comp, false); + _creamPie.SetCreamPied((entity, entity.Comp), false); } } diff --git a/Content.Shared/Examine/ExamineSystemShared.Group.cs b/Content.Shared/Examine/ExamineSystemShared.Group.cs index e220d3ef77..7c231fe157 100644 --- a/Content.Shared/Examine/ExamineSystemShared.Group.cs +++ b/Content.Shared/Examine/ExamineSystemShared.Group.cs @@ -13,8 +13,6 @@ namespace Content.Shared.Examine base.Initialize(); SubscribeLocalEvent>(OnGroupExamineVerb); - - _ghostQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 7c063f6ce9..8af9a91e5c 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -21,6 +21,8 @@ namespace Content.Shared.Examine [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] protected readonly MobStateSystem MobStateSystem = default!; + [Dependency] private readonly EntityQuery _ghostQuery = default!; + public const float MaxRaycastRange = 100; /// @@ -43,8 +45,6 @@ namespace Content.Shared.Examine protected const float ExamineBlurrinessMult = 2.5f; - private EntityQuery _ghostQuery; - /// /// Creates a new examine tooltip with arbitrary info. /// diff --git a/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs index a2ecb4d034..ca0b8694a7 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs @@ -24,11 +24,11 @@ public sealed class BlindfoldSystem : EntitySystem private void OnEquipped(Entity blindfold, ref GotEquippedEvent args) { - _blindableSystem.UpdateIsBlind(args.Equipee); + _blindableSystem.UpdateIsBlind(args.EquipTarget); } private void OnUnequipped(Entity blindfold, ref GotUnequippedEvent args) { - _blindableSystem.UpdateIsBlind(args.Equipee); + _blindableSystem.UpdateIsBlind(args.EquipTarget); } } diff --git a/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs index 099753d51e..aa0a5a1574 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs @@ -44,12 +44,12 @@ public sealed class BlurryVisionSystem : EntitySystem private void OnGlassesEquipped(Entity glasses, ref GotEquippedEvent args) { - UpdateBlurMagnitude(args.Equipee); + UpdateBlurMagnitude(args.EquipTarget); } private void OnGlassesUnequipped(Entity glasses, ref GotUnequippedEvent args) { - UpdateBlurMagnitude(args.Equipee); + UpdateBlurMagnitude(args.EquipTarget); } } diff --git a/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs b/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs index 73b06cac9b..4ae08ed95b 100644 --- a/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs +++ b/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs @@ -46,10 +46,11 @@ public sealed class FingerprintReaderSystem : EntitySystem /// User trying to gain access. /// Whether to display a popup with the reason you are not allowed to access this. /// The reason why access was denied. + /// Allows bypassing glove check regardless of the component's IgnoreGloves param. /// True if access was granted, otherwise false. // TODO: Remove showPopup, just keeping it here for backwards compatibility while I refactor mail [PublicAPI] - public bool IsAllowed(Entity target, EntityUid user, [NotNullWhen(false)] out string? denyReason, bool showPopup = true) + public bool IsAllowed(Entity target, EntityUid user, [NotNullWhen(false)] out string? denyReason, bool showPopup = true, bool checkGloves = true) { denyReason = null; if (!Resolve(target, ref target.Comp, false)) @@ -59,7 +60,7 @@ public sealed class FingerprintReaderSystem : EntitySystem return true; // Check for gloves first - if (!target.Comp.IgnoreGloves && TryGetBlockingGloves(user, out var gloves)) + if (checkGloves && !target.Comp.IgnoreGloves && TryGetBlockingGloves(user, out var gloves)) { denyReason = Loc.GetString("fingerprint-reader-fail-gloves", ("blocker", gloves)); diff --git a/Content.Shared/Flash/Components/FlashComponent.cs b/Content.Shared/Flash/Components/FlashComponent.cs index d1a8b882d9..9484add5ef 100644 --- a/Content.Shared/Flash/Components/FlashComponent.cs +++ b/Content.Shared/Flash/Components/FlashComponent.cs @@ -16,6 +16,12 @@ public sealed partial class FlashComponent : Component [DataField, AutoNetworkedField] public bool FlashOnUse = true; + /// + /// Flash the area around the entity when the flash is used with ranged interaction? + /// + [DataField, AutoNetworkedField] + public bool FlashOnRangedInteract = false; + /// /// Flash the target when melee attacking them? /// diff --git a/Content.Shared/Flash/FlashEvents.cs b/Content.Shared/Flash/FlashEvents.cs index 1c18ca1676..70a61765ff 100644 --- a/Content.Shared/Flash/FlashEvents.cs +++ b/Content.Shared/Flash/FlashEvents.cs @@ -13,9 +13,15 @@ public record struct FlashAttemptEvent(EntityUid Target, EntityUid? User, Entity } /// -/// Called when a player is successfully flashed. +/// Called when a player is successfully flashed, once for each flashed player. /// Raised on the target hit by the flash, the user of the flash and the flash used. /// The Melee parameter is used to check for rev conversion. /// [ByRefEvent] public record struct AfterFlashedEvent(EntityUid Target, EntityUid? User, EntityUid? Used, bool Melee); + +/// +/// Raised once on the flash entity when it was used, regardless of the flashed status being applied or not. +/// +[ByRefEvent] +public record struct AfterFlashActivatedEvent(EntityUid? Target, EntityUid? User); diff --git a/Content.Shared/Flash/SharedFlashSystem.cs b/Content.Shared/Flash/SharedFlashSystem.cs index 4b8ff3ab7b..30f11efdeb 100644 --- a/Content.Shared/Flash/SharedFlashSystem.cs +++ b/Content.Shared/Flash/SharedFlashSystem.cs @@ -1,13 +1,18 @@ +using System.Linq; using Content.Shared.Charges.Components; using Content.Shared.Charges.Systems; +using Content.Shared.Clothing.Components; using Content.Shared.Examine; using Content.Shared.Eye.Blinding.Components; using Content.Shared.Flash.Components; using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; using Content.Shared.Light; +using Content.Shared.Movement.Systems; using Content.Shared.Popups; +using Content.Shared.Random.Helpers; using Content.Shared.StatusEffect; using Content.Shared.Stunnable; using Content.Shared.Tag; @@ -17,12 +22,7 @@ using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; -using Robust.Shared.Random; using Robust.Shared.Timing; -using System.Linq; -using Content.Shared.Movement.Systems; -using Content.Shared.Random.Helpers; -using Content.Shared.Clothing.Components; namespace Content.Shared.Flash; @@ -42,8 +42,9 @@ public abstract class SharedFlashSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - private EntityQuery _statusEffectsQuery; - private EntityQuery _damagedByFlashingQuery; + [Dependency] private readonly EntityQuery _statusEffectsQuery = default!; + [Dependency] private readonly EntityQuery _damagedByFlashingQuery = default!; + private HashSet _entSet = new(); // The tag to add when a flash has no charges left. @@ -57,14 +58,12 @@ public abstract class SharedFlashSystem : EntitySystem SubscribeLocalEvent(OnFlashMeleeHit); SubscribeLocalEvent(OnFlashUseInHand); + SubscribeLocalEvent(OnRangedInteract); SubscribeLocalEvent(OnLightToggle); SubscribeLocalEvent(OnPermanentBlindnessFlashAttempt); SubscribeLocalEvent(OnTemporaryBlindnessFlashAttempt); Subs.SubscribeWithRelay(OnFlashImmunityFlashAttempt, held: false); SubscribeLocalEvent(OnExamine); - - _statusEffectsQuery = GetEntityQuery(); - _damagedByFlashingQuery = GetEntityQuery(); } private void OnFlashMeleeHit(Entity ent, ref MeleeHitEvent args) @@ -72,7 +71,7 @@ public abstract class SharedFlashSystem : EntitySystem if (!ent.Comp.FlashOnMelee || !args.IsHit || !args.HitEntities.Any() || - !UseFlash(ent, args.User)) + !TryUseFlashItem(ent.AsNullable(), args.User)) { return; } @@ -82,41 +81,66 @@ public abstract class SharedFlashSystem : EntitySystem { Flash(target, args.User, ent.Owner, ent.Comp.MeleeDuration, ent.Comp.SlowTo, melee: true, stunDuration: ent.Comp.MeleeStunDuration); } + + EntityUid? firstTarget = args.HitEntities.Count > 0 ? args.HitEntities[0] : null; // Just pick the first hit entity. + var ev = new AfterFlashActivatedEvent(firstTarget, args.User); + RaiseLocalEvent(ent, ref ev); } private void OnFlashUseInHand(Entity ent, ref UseInHandEvent args) { - if (!ent.Comp.FlashOnUse || args.Handled || !UseFlash(ent, args.User)) + if (!ent.Comp.FlashOnUse || args.Handled || !TryUseFlashItem(ent.AsNullable(), args.User)) return; args.Handled = true; FlashArea(ent.Owner, args.User, ent.Comp.Range, ent.Comp.AoeFlashDuration, ent.Comp.SlowTo, true, ent.Comp.Probability); + var ev = new AfterFlashActivatedEvent(null, args.User); // No direct target. + RaiseLocalEvent(ent, ref ev); + } + + // TODO: This or most of the other systems subscribing to BeforeRangedInteractEvent shouldn't be using this event as handling it stops contact interaction with the used tool, + // but this will need some cleanup of how SharedInteractionSystem handles the code flow. Also the event is raised for both in-range and out of range interactions, which + // is what the subscribers are using it for, but does not seem originally intended from the naming convention. + private void OnRangedInteract(Entity ent, ref BeforeRangedInteractEvent args) + { + if (!ent.Comp.FlashOnRangedInteract || args.Handled || !TryUseFlashItem(ent.AsNullable(), args.User)) + return; + + args.Handled = true; + FlashArea(ent.Owner, args.User, ent.Comp.Range, ent.Comp.AoeFlashDuration, ent.Comp.SlowTo, true, ent.Comp.Probability); + var ev = new AfterFlashActivatedEvent(args.Target, args.User); + RaiseLocalEvent(ent, ref ev); } // needed for the flash lantern and interrogator lamp // TODO: This is awful and all the different components for toggleable lights need to be unified and changed to use Itemtoggle private void OnLightToggle(Entity ent, ref LightToggleEvent args) { - if (!args.IsOn || !UseFlash(ent, null)) + if (!args.IsOn || !TryUseFlashItem(ent.AsNullable(), null)) return; FlashArea(ent.Owner, null, ent.Comp.Range, ent.Comp.AoeFlashDuration, ent.Comp.SlowTo, true, ent.Comp.Probability); + var ev = new AfterFlashActivatedEvent(null, null); // TODO: Add user once someone made toggleable lights not a total mess. + RaiseLocalEvent(ent, ref ev); } /// - /// Use charges and set the visuals. + /// Try to use charges, play the sound and set the visuals of a flash item. + /// This does not actually cause the flash status effect by itself, you will need to either call or as well. /// /// False if no charges are left or the flash is currently in use. - private bool UseFlash(Entity ent, EntityUid? user) + public bool TryUseFlashItem(Entity ent, EntityUid? user) { + if (!Resolve(ent, ref ent.Comp)) + return false; + if (_useDelay.IsDelayed(ent.Owner)) return false; if (TryComp(ent.Owner, out var charges) - && _sharedCharges.IsEmpty((ent.Owner, charges))) + && !_sharedCharges.TryUseCharge((ent.Owner, charges))) return false; - _sharedCharges.TryUseCharge((ent.Owner, charges)); _audio.PlayPredicted(ent.Comp.Sound, ent.Owner, user); var active = EnsureComp(ent.Owner); @@ -126,7 +150,7 @@ public abstract class SharedFlashSystem : EntitySystem if (_sharedCharges.IsEmpty((ent.Owner, charges))) { - _appearance.SetData(ent.Owner, FlashVisuals.Burnt, true); + _appearance.SetData(ent.Owner, FlashVisuals.Burnt, true); // TODO: Reset if charges are refilled. _tag.AddTag(ent.Owner, TrashTag); _popup.PopupClient(Loc.GetString("flash-component-becomes-empty"), user); } diff --git a/Content.Shared/Fluids/EntitySystems/SolutionDumpingSystem.cs b/Content.Shared/Fluids/EntitySystems/SolutionDumpingSystem.cs index a6f4a8b31f..07e2938585 100644 --- a/Content.Shared/Fluids/EntitySystems/SolutionDumpingSystem.cs +++ b/Content.Shared/Fluids/EntitySystems/SolutionDumpingSystem.cs @@ -28,7 +28,7 @@ public sealed class SolutionDumpingSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedSolutionContainerSystem _solContainer = default!; - private EntityQuery _dumpQuery; + [Dependency] private readonly EntityQuery _dumpQuery = default!; public override void Initialize() { @@ -39,9 +39,6 @@ public sealed class SolutionDumpingSystem : EntitySystem SubscribeLocalEvent(OnDrainableDragged); SubscribeLocalEvent(OnDrainedToDumpableDragged); - - // We use queries for these since CanDropDraggedEvent gets called pretty rapidly - _dumpQuery = GetEntityQuery(); } private void OnDrainableCanDrag(Entity ent, ref CanDragEvent args) diff --git a/Content.Shared/Fluids/SharedPuddleSystem.cs b/Content.Shared/Fluids/SharedPuddleSystem.cs index 56ba5e8441..18cae762f8 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.cs @@ -44,6 +44,10 @@ public abstract partial class SharedPuddleSystem : EntitySystem [Dependency] private readonly TileFrictionController _tile = default!; [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly EntityQuery _stepTriggerQuery = default!; + [Dependency] private readonly EntityQuery _reactiveQuery = default!; + [Dependency] private readonly EntityQuery _evaporationQuery = default!; + private ProtoId[] _standoutReagents = []; /// @@ -57,10 +61,6 @@ public abstract partial class SharedPuddleSystem : EntitySystem // loses & then gains reagents in a single tick. private HashSet _deletionQueue = []; - private EntityQuery _stepTriggerQuery; - private EntityQuery _reactiveQuery; - private EntityQuery _evaporationQuery; - public override void Initialize() { base.Initialize(); @@ -75,10 +75,6 @@ public abstract partial class SharedPuddleSystem : EntitySystem SubscribeLocalEvent(OnPrototypesReloaded); - _stepTriggerQuery = GetEntityQuery(); - _reactiveQuery = GetEntityQuery(); - _evaporationQuery = GetEntityQuery(); - CacheStandsout(); InitializeSpillable(); } diff --git a/Content.Shared/Friction/TileFrictionController.cs b/Content.Shared/Friction/TileFrictionController.cs index 71c51c55f1..71e269b98c 100644 --- a/Content.Shared/Friction/TileFrictionController.cs +++ b/Content.Shared/Friction/TileFrictionController.cs @@ -28,14 +28,14 @@ namespace Content.Shared.Friction [Dependency] private readonly SharedMoverController _mover = default!; [Dependency] private readonly SharedMapSystem _map = default!; - private EntityQuery _frictionQuery; - private EntityQuery _pullerQuery; - private EntityQuery _pullableQuery; - private EntityQuery _gridQuery; + [Dependency] private readonly EntityQuery _frictionQuery = default!; + [Dependency] private readonly EntityQuery _pullerQuery = default!; + [Dependency] private readonly EntityQuery _pullableQuery = default!; + [Dependency] private readonly EntityQuery _gridQuery = default!; // For debug purposes only - private EntityQuery _moverQuery; - private EntityQuery _blockMoverQuery; + [Dependency] private readonly EntityQuery _moverQuery = default!; + [Dependency] private readonly EntityQuery _blockMoverQuery = default!; private float _frictionModifier; private float _minDamping; @@ -50,12 +50,6 @@ namespace Content.Shared.Friction Subs.CVar(_configManager, CCVars.MinFriction, value => _minDamping = value, true); Subs.CVar(_configManager, CCVars.AirFriction, value => _airDamping = value, true); Subs.CVar(_configManager, CCVars.OffgridFriction, value => _offGridDamping = value, true); - _frictionQuery = GetEntityQuery(); - _pullerQuery = GetEntityQuery(); - _pullableQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - _moverQuery = GetEntityQuery(); - _blockMoverQuery = GetEntityQuery(); } public override void UpdateBeforeSolve(bool prediction, float frameTime) diff --git a/Content.Shared/Friends/Systems/PettableFriendSystem.cs b/Content.Shared/Friends/Systems/PettableFriendSystem.cs index 6e41f4bdea..104a08c7b5 100644 --- a/Content.Shared/Friends/Systems/PettableFriendSystem.cs +++ b/Content.Shared/Friends/Systems/PettableFriendSystem.cs @@ -14,16 +14,13 @@ public sealed class PettableFriendSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - private EntityQuery _exceptionQuery; - private EntityQuery _useDelayQuery; + [Dependency] private readonly EntityQuery _exceptionQuery = default!; + [Dependency] private readonly EntityQuery _useDelayQuery = default!; public override void Initialize() { base.Initialize(); - _exceptionQuery = GetEntityQuery(); - _useDelayQuery = GetEntityQuery(); - SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnRehydrated); } diff --git a/Content.Shared/Ghost/GhostComponent.cs b/Content.Shared/Ghost/GhostComponent.cs index 8eee89a0e9..ee4a2a3138 100644 --- a/Content.Shared/Ghost/GhostComponent.cs +++ b/Content.Shared/Ghost/GhostComponent.cs @@ -10,7 +10,7 @@ namespace Content.Shared.Ghost; /// Handles limiting interactions, using ghost abilities, ghost visibility, and ghost warping. /// [RegisterComponent, NetworkedComponent, Access(typeof(SharedGhostSystem))] -[AutoGenerateComponentState(true), AutoGenerateComponentPause] +[AutoGenerateComponentState(true)] public sealed partial class GhostComponent : Component { // Actions @@ -54,7 +54,7 @@ public sealed partial class GhostComponent : Component /// May not reflect actual time of death if this entity has been paused, /// but will give an accurate length of time since death. /// - [DataField, AutoPausedField] + [DataField, AutoNetworkedField] public TimeSpan TimeOfDeath = TimeSpan.Zero; /// diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index 7d3561a79f..726c20d31a 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -1,9 +1,11 @@ using Content.Shared.Emoting; +using Content.Shared.Examine; using Content.Shared.Hands; using Content.Shared.Interaction.Events; using Content.Shared.Item; using Content.Shared.Popups; using Robust.Shared.Serialization; +using Robust.Shared.Timing; namespace Content.Shared.Ghost { @@ -14,6 +16,7 @@ namespace Content.Shared.Ghost public abstract class SharedGhostSystem : EntitySystem { [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] protected readonly IGameTiming _gameTiming = default!; public override void Initialize() { @@ -23,6 +26,17 @@ namespace Content.Shared.Ghost SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnGhostExamine); + } + + private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args) + { + var timeSinceDeath = _gameTiming.RealTime.Subtract(component.TimeOfDeath); + var deathTimeInfo = timeSinceDeath.Minutes > 0 + ? Loc.GetString("comp-ghost-examine-time-minutes", ("minutes", timeSinceDeath.Minutes)) + : Loc.GetString("comp-ghost-examine-time-seconds", ("seconds", timeSinceDeath.Seconds)); + + args.PushMarkup(deathTimeInfo); } private void OnAttemptInteract(Entity ent, ref InteractionAttemptEvent args) diff --git a/Content.Shared/Gravity/SharedGravitySystem.Shake.cs b/Content.Shared/Gravity/SharedGravitySystem.Shake.cs index 41cf616cc4..a1dbed382c 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.Shake.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.Shake.cs @@ -2,20 +2,21 @@ namespace Content.Shared.Gravity; public abstract partial class SharedGravitySystem { + [Dependency] private readonly EntityQuery _gravityQuery = default!; + protected const float GravityKick = 100.0f; protected const float ShakeCooldown = 0.2f; private void UpdateShake() { var curTime = Timing.CurTime; - var gravityQuery = GetEntityQuery(); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp)) { if (comp.NextShake <= curTime) { - if (comp.ShakeTimes == 0 || !gravityQuery.TryGetComponent(uid, out var gravity)) + if (comp.ShakeTimes == 0 || !_gravityQuery.TryGetComponent(uid, out var gravity)) { RemCompDeferred(uid); continue; diff --git a/Content.Shared/Gravity/SharedGravitySystem.cs b/Content.Shared/Gravity/SharedGravitySystem.cs index d9a0a70d94..50eb674817 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.cs @@ -19,9 +19,9 @@ public abstract partial class SharedGravitySystem : EntitySystem public static readonly ProtoId WeightlessAlert = "Weightless"; - protected EntityQuery GravityQuery; - private EntityQuery _weightlessQuery; - private EntityQuery _physicsQuery; + [Dependency] protected readonly EntityQuery GravityQuery = default!; + [Dependency] private readonly EntityQuery _weightlessQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; public override void Initialize() { @@ -43,10 +43,6 @@ public abstract partial class SharedGravitySystem : EntitySystem // Impulse SubscribeLocalEvent(OnShooterImpulse); SubscribeLocalEvent(OnThrowerImpulse); - - GravityQuery = GetEntityQuery(); - _weightlessQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); } public override void Update(float frameTime) diff --git a/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs b/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs index 4b96f89d81..4146d4ec64 100644 --- a/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs +++ b/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs @@ -17,27 +17,27 @@ public sealed class ExtraHandsEquipmentSystem : EntitySystem private void OnEquipped(Entity ent, ref GotEquippedEvent args) { - if (!TryComp(args.Equipee, out var handsComp)) + if (!TryComp(args.EquipTarget, out var handsComp)) return; foreach (var (handName, hand) in ent.Comp.Hands) { // add the NetEntity id to the container name to prevent multiple items with this component from conflicting var handId = $"{GetNetEntity(ent.Owner).Id}-{handName}"; - _hands.AddHand((args.Equipee, handsComp), handId, hand.Location); + _hands.AddHand((args.EquipTarget, handsComp), handId, hand.Location); } } private void OnUnequipped(Entity ent, ref GotUnequippedEvent args) { - if (!TryComp(args.Equipee, out var handsComp)) + if (!TryComp(args.EquipTarget, out var handsComp)) return; foreach (var handName in ent.Comp.Hands.Keys) { // add the NetEntity id to the container name to prevent multiple items with this component from conflicting var handId = $"{GetNetEntity(ent.Owner).Id}-{handName}"; - _hands.RemoveHand((args.Equipee, handsComp), handId); + _hands.RemoveHand((args.EquipTarget, handsComp), handId); } } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs index c19de9629a..17794807ed 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs @@ -80,6 +80,10 @@ public abstract partial class SharedHandsSystem if (handId == null) return false; + // don't try to pick up the item if it's being deleted anyways + if (TerminatingOrDeleted(entity) || EntityManager.IsQueuedForDeletion(entity)) + return false; + if (!Resolve(entity, ref item, false)) return false; diff --git a/Content.Shared/HijackBeacon/ActiveHijackBeaconComponent.cs b/Content.Shared/HijackBeacon/ActiveHijackBeaconComponent.cs new file mode 100644 index 0000000000..b6f1d60c79 --- /dev/null +++ b/Content.Shared/HijackBeacon/ActiveHijackBeaconComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.HijackBeacon; + +/// +/// This is used for tracking a that is currently activated or on cooldown. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ActiveHijackBeaconComponent : Component +{ + /// + /// Remaining time until the hijack is completed. + /// + [DataField, AutoNetworkedField, Access(typeof(HijackBeaconSystem))] + public TimeSpan CompletionTime = TimeSpan.Zero; +} diff --git a/Content.Shared/HijackBeacon/HijackBeaconComponent.cs b/Content.Shared/HijackBeacon/HijackBeaconComponent.cs new file mode 100644 index 0000000000..ed1d3ca74b --- /dev/null +++ b/Content.Shared/HijackBeacon/HijackBeaconComponent.cs @@ -0,0 +1,53 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; + +namespace Content.Shared.HijackBeacon; + +/// +/// Component for hijack beacons, meant to be planted on the ATS to drain station funds. +/// +/// +/// Status and timer fields are private so the state machine is preserved. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class HijackBeaconComponent : Component +{ + /// + /// Current state of the beacon. + /// + [DataField, AutoNetworkedField, Access(typeof(HijackBeaconSystem))] + public HijackBeaconStatus Status = HijackBeaconStatus.AwaitActivate; + + /// + /// How long it takes to deactivate the beacon. + /// + [DataField, AutoNetworkedField, Access(typeof(HijackBeaconSystem))] + public TimeSpan DeactivationLength = TimeSpan.FromSeconds(5); + + /// + /// Remaining time until the hijack is completed. + /// + [DataField, Access(typeof(HijackBeaconSystem))] + public TimeSpan RemainingTime = TimeSpan.FromSeconds(200); + + /// + /// Default amount of time before the beacon can be re-activated, if it is disarmed. + /// + [DataField, Access(typeof(HijackBeaconSystem))] + public TimeSpan Cooldown = TimeSpan.FromSeconds(20); + + /// + /// Remaining cooldown time before the beacon can be reactivated. + /// + [DataField, AutoNetworkedField, Access(typeof(HijackBeaconSystem))] + public TimeSpan CooldownTime = TimeSpan.Zero; + + /// + /// How much cash should be withdrawn from each department account? + /// + [DataField] + public int Fine = 5000; +} diff --git a/Content.Shared/HijackBeacon/HijackBeaconSystem.cs b/Content.Shared/HijackBeacon/HijackBeaconSystem.cs new file mode 100644 index 0000000000..d3e5810fba --- /dev/null +++ b/Content.Shared/HijackBeacon/HijackBeaconSystem.cs @@ -0,0 +1,353 @@ +using Content.Shared.Cargo.Components; +using Content.Shared.Chat; +using Content.Shared.Construction.Components; +using Content.Shared.Construction.EntitySystems; +using Content.Shared.Database; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Audio; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; + +namespace Content.Shared.HijackBeacon; + +public sealed class HijackBeaconSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly AnchorableSystem _anchor = default!; + [Dependency] private readonly SharedChatSystem _chat = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public readonly SoundSpecifier AnnounceSound = new SoundPathSpecifier("/Audio/Misc/notice1.ogg"); + public readonly SoundSpecifier DeactivateSound = new SoundPathSpecifier("/Audio/Misc/notice2.ogg"); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetAltVerbs); + SubscribeLocalEvent(OnUnanchorAttempt); + SubscribeLocalEvent(OnAnchorChanged); + SubscribeLocalEvent(OnDeactivateDoAfter); + SubscribeLocalEvent(OnExaminedEvent); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var active, out var comp)) + { + switch (comp.Status) + { + case HijackBeaconStatus.Armed: + if (_gameTiming.CurTime < active.CompletionTime) + return; + + HijackFinish((uid, comp)); + Dirty(uid, comp); + break; + case HijackBeaconStatus.Cooldown: + if (comp.CooldownTime < _gameTiming.CurTime) + { + comp.Status = HijackBeaconStatus.AwaitActivate; + RemCompDeferred(uid); + Dirty(uid, comp); + } + break; + } + } + } + + #region Event Subs + + /// + /// Deactivate beacon if it gets unanchored(via a bomb or something) + /// + private void OnAnchorChanged(Entity ent, ref AnchorStateChangedEvent args) + { + // Unanchoring the beacon deactivates it. This is to prevent people from bombing the tile the beacon is on and running away with it for a free activation. + if (!args.Anchored && ent.Comp.Status == HijackBeaconStatus.Armed) + DeactivateBeacon(ent); + } + + private void OnUnanchorAttempt(Entity entity, ref UnanchorAttemptEvent args) + { + if (entity.Comp.Status == HijackBeaconStatus.Armed) + args.Cancel(); + } + + /// + /// Get the activation and deactivation verbs. + /// + private void OnGetAltVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || args.Hands is null) + return; + + if (ent.Comp.Status == HijackBeaconStatus.AwaitActivate) + { + args.Verbs.Add(new() + { + Act = () => + { + ActivateBeacon(ent); + }, + Text = Loc.GetString("hijack-beacon-verb-activate-text"), + Message = Loc.GetString("hijack-beacon-verb-activate-message"), + Disabled = ent.Comp.Status != HijackBeaconStatus.AwaitActivate || !CanActivate(ent), + TextStyleClass = "InteractionVerb", + Impact = LogImpact.High, + }); + } + + if (ent.Comp.Status == HijackBeaconStatus.Armed) + { + var user = args.User; + + args.Verbs.Add(new() + { + Act = () => + { + DeactivateBeaconDoAfter(ent, user); + }, + Text = Loc.GetString("hijack-beacon-verb-deactivate-text"), + Message = Loc.GetString("hijack-beacon-verb-deactivate-message"), + TextStyleClass = "InteractionVerb", + Impact = LogImpact.High, + }); + } + } + + /// + /// When it's examined. + /// + private void OnExaminedEvent(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + switch (ent.Comp.Status) + { + case HijackBeaconStatus.AwaitActivate: + args.PushMarkup(Loc.GetString("hijack-beacon-examine-await-activate")); + break; + case HijackBeaconStatus.Armed: + args.PushMarkup(Loc.GetString("defusable-examine-live", + ("name", ent), + ("time", GetRemainingTime(ent.Owner)))); + break; + case HijackBeaconStatus.Cooldown: + args.PushMarkup(Loc.GetString("hijack-beacon-examine-await-cooldown")); + break; + case HijackBeaconStatus.HijackComplete: + args.PushMarkup(Loc.GetString("hijack-beacon-examine-await-hijack-complete")); + break; + } + } + + /// + /// What happens when you deactivate the beacon. + /// + private void OnDeactivateDoAfter(Entity ent, ref HijackBeaconDeactivateDoAfterEvent args) + { + if (args.Handled || args.Cancelled) + return; + + DeactivateBeacon(ent); + + args.Handled = true; + } + + #endregion + + /// + /// Arming the beacon. Should only occur if on the ATS. + /// + private void ActivateBeacon(Entity ent) + { + if (ent.Comp.Status != HijackBeaconStatus.AwaitActivate || !CanActivate(ent)) + return; + + // Activate and start countdown. + // Remaining time is adjusted by current time to simplify the update loop. + EnsureComp(ent, out var activeComp); + activeComp.CompletionTime = _gameTiming.CurTime + ent.Comp.RemainingTime; + ent.Comp.Status = HijackBeaconStatus.Armed; + + //global announcement + var sender = Loc.GetString("hijack-beacon-announcement-sender"); + var message = Loc.GetString("hijack-beacon-announcement-activated", ("time", GetRemainingTime((ent.Owner, activeComp)))); + _chat.DispatchGlobalAnnouncement(message, sender, true, AnnounceSound, Color.Yellow); + + //Anchor. Anchoring is tied to activation. + Anchor(ent); + + Dirty(ent); + } + + /// + /// Deactivates the beacon. + /// + private void DeactivateBeacon(Entity ent) + { + if (ent.Comp.Status != HijackBeaconStatus.Armed) + return; + + // Put beacon on cooldown + ent.Comp.CooldownTime = ent.Comp.Cooldown + _gameTiming.CurTime; + ent.Comp.Status = HijackBeaconStatus.Cooldown; + + //global announcement + var sender = Loc.GetString("hijack-beacon-announcement-sender"); + var message = Loc.GetString("hijack-beacon-announcement-deactivated"); + _chat.DispatchGlobalAnnouncement(message, sender, true, DeactivateSound, Color.Green); + + // Unanchor. we want anchoring to be tied to activation here so we just call this. + Unanchor(ent); + + Dirty(ent); + } + + /// + /// Complete the hijack. The beacon equivalent to a nuke detonation + /// + private void HijackFinish(Entity ent) + { + if (ent.Comp.Status != HijackBeaconStatus.Armed) + return; + + // Hijack is completed and can't be reattempted + ent.Comp.Status = HijackBeaconStatus.HijackComplete; + RemCompDeferred(ent); + + var beaconXForm = Transform(ent); + + // Ensure we are on the trade station still + if (!TryComp(beaconXForm.GridUid, out var station)) + { + Log.Warning($"Trade station hijack tried to succeed on non-trade station grid {beaconXForm.GridUid}!"); + DeactivateBeacon(ent); + return; + } + + if (station.Hacked) + { + Log.Warning("Hack succeeded on an already hacked trade station!"); + return; + } + + // Mark the station as hacked. + station.Hacked = true; + Dirty(beaconXForm.GridUid.Value, station); + + // Broadcast that the ATS has been hacked. + var ev = new HijackBeaconSuccessEvent(ent.Comp.Fine); + RaiseLocalEvent(ref ev); + + //global announcement + var sender = Loc.GetString("hijack-beacon-announcement-sender"); + var message = Loc.GetString("hijack-beacon-announcement-success", ("fine", ev.Total)); + _chat.DispatchGlobalAnnouncement(message, sender, true, AnnounceSound, Color.Red); + + // Unanchoring must occur after updating the status, or it will disarm the beacon + Unanchor(ent, beaconXForm); + + Dirty(ent); + } + + /// + /// Starts the deactivation doafter. + /// + private void DeactivateBeaconDoAfter(Entity beacon, EntityUid user) + { + var doAfter = new DoAfterArgs(EntityManager, user, beacon.Comp.DeactivationLength, new HijackBeaconDeactivateDoAfterEvent(), beacon) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = true, + }; + + // (try to) start doafter + _doAfter.TryStartDoAfter(doAfter); + } + + #region Helpers + + /// + /// Check if the beacon is on the Trade Station and if the Trade Station has not been hijacked already. + /// + private bool CanActivate(Entity ent) + { + return TryComp(Transform(ent).GridUid, out TradeStationComponent? tradeStation) && !tradeStation.Hacked && _anchor.CanAnchorAt(ent.Owner); + } + + /// + /// Anchoring helper + /// + private void Anchor(Entity ent, TransformComponent? beaconXForm = null) + { + beaconXForm ??= Transform(ent); + + if (beaconXForm.Anchored || beaconXForm.GridUid == null) + return; + + _transform.AnchorEntity(ent, beaconXForm); + _popup.PopupPredicted(Loc.GetString("hijack-beacon-popup-anchor"), ent, null); + } + + /// + /// Unanchoring helper + /// + private void Unanchor(Entity ent, TransformComponent? beaconXForm = null) + { + beaconXForm ??= Transform(ent); + + if (!beaconXForm.Anchored) + return; + + _transform.Unanchor(ent, beaconXForm); + _popup.PopupPredicted(Loc.GetString("hijack-beacon-popup-unanchor"), ent, null); + } + + /// + /// Turns time values into usable values for announcements/examine messages + /// + private int GetRemainingTime(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return 69420; // Mature error code + + return (int) (ent.Comp.CompletionTime - _gameTiming.CurTime).TotalSeconds; + } + + #endregion +} + +public enum HijackBeaconStatus : byte +{ + AwaitActivate, + Armed, + Cooldown, + HijackComplete +} + +/// +/// DoAfter event raised when the hijack beacon is deactivated. +/// +[Serializable, NetSerializable] +public sealed partial class HijackBeaconDeactivateDoAfterEvent : SimpleDoAfterEvent; + +/// +/// Event raised when the hijack beacon succeeds in hijacking the ATS. +/// +[ByRefEvent] +public record struct HijackBeaconSuccessEvent(int Fine) +{ + public int Total = 0; +}; diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index 82cc4924fb..9622e814c5 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -29,6 +29,8 @@ public abstract class SharedImplanterSystem : EntitySystem [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly EntityQuery _implantCompQuery = default!; + public override void Initialize() { base.Initialize(); @@ -193,13 +195,11 @@ public abstract class SharedImplanterSystem : EntitySystem if (_container.TryGetContainer(target, ImplanterComponent.ImplantSlotId, out var implantContainer)) { - var implantCompQuery = GetEntityQuery(); - if (component.AllowDeimplantAll) { foreach (var implant in implantContainer.ContainedEntities) { - if (!implantCompQuery.TryGetComponent(implant, out var implantComp)) + if (!_implantCompQuery.TryGetComponent(implant, out var implantComp)) continue; //Don't remove a permanent implant and look for the next that can be drawn @@ -234,7 +234,7 @@ public abstract class SharedImplanterSystem : EntitySystem } } - if (implant != null && implantCompQuery.TryGetComponent(implant, out var implantComp)) + if (implant != null && _implantCompQuery.TryGetComponent(implant, out var implantComp)) { //Don't remove a permanent implant if (!_container.CanRemove(implant.Value, implantContainer)) diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs index 6595789977..52e25a0b9a 100644 --- a/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs +++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs @@ -1,9 +1,9 @@ using Content.Shared.Chat; using Content.Shared.IdentityManagement.Components; using Content.Shared.Implants.Components; -using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Mobs; +using Content.Shared.Store; using Content.Shared.Corvax.TTS; namespace Content.Shared.Implants; @@ -13,12 +13,15 @@ public abstract partial class SharedSubdermalImplantSystem public void InitializeRelay() { SubscribeLocalEvent(RelayToImplantEvent); - SubscribeLocalEvent(RelayToImplantEvent); SubscribeLocalEvent(RelayToImplantEvent); SubscribeLocalEvent(RelayToImplantEvent); SubscribeLocalEvent(RelayToImplantEvent); SubscribeLocalEvent(RelayToImplantEvent); SubscribeLocalEvent(RelayToImplantEvent); + + // Ref relays, for when you need to write to the event! + SubscribeLocalEvent(RefRelayToImplantEvent); + SubscribeLocalEvent(RefRelayToImplantEvent); } /// @@ -38,6 +41,26 @@ public abstract partial class SharedSubdermalImplantSystem RaiseLocalEvent(implant, relayEv); } } + + /// + /// Relays events from the implanted to the implant. + /// + private void RefRelayToImplantEvent(Entity entity, ref T args) where T : notnull + { + if (!_container.TryGetContainer(entity, ImplanterComponent.ImplantSlotId, out var implantContainer)) + return; + + var relayEv = new ImplantRelayEvent(args, entity); + foreach (var implant in implantContainer.ContainedEntities) + { + if (args is HandledEntityEventArgs { Handled: true }) + return; + + RaiseLocalEvent(implant, relayEv); + } + + args = relayEv.Event; + } } /// @@ -45,7 +68,7 @@ public abstract partial class SharedSubdermalImplantSystem /// public sealed class ImplantRelayEvent where T : notnull { - public readonly T Event; + public T Event; public readonly EntityUid ImplantedEntity; diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 88d42b8a28..f5deabdf7e 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -75,16 +75,16 @@ namespace Content.Shared.Interaction [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - private EntityQuery _ignoreUiRangeQuery; - private EntityQuery _fixtureQuery; - private EntityQuery _itemQuery; - private EntityQuery _physicsQuery; - private EntityQuery _handsQuery; - private EntityQuery _relayQuery; - private EntityQuery _combatQuery; - private EntityQuery _wallMountQuery; - private EntityQuery _delayQuery; - private EntityQuery _uiQuery; + [Dependency] private readonly EntityQuery _ignoreUiRangeQuery = default!; + [Dependency] private readonly EntityQuery _fixtureQuery = default!; + [Dependency] private readonly EntityQuery _itemQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + [Dependency] private readonly EntityQuery _handsQuery = default!; + [Dependency] private readonly EntityQuery _relayQuery = default!; + [Dependency] private readonly EntityQuery _combatQuery = default!; + [Dependency] private readonly EntityQuery _wallMountQuery = default!; + [Dependency] private readonly EntityQuery _delayQuery = default!; + [Dependency] private readonly EntityQuery _uiQuery = default!; /// /// The collision mask used by default for @@ -103,17 +103,6 @@ namespace Content.Shared.Interaction public override void Initialize() { - _ignoreUiRangeQuery = GetEntityQuery(); - _fixtureQuery = GetEntityQuery(); - _itemQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _handsQuery = GetEntityQuery(); - _relayQuery = GetEntityQuery(); - _combatQuery = GetEntityQuery(); - _wallMountQuery = GetEntityQuery(); - _delayQuery = GetEntityQuery(); - _uiQuery = GetEntityQuery(); - SubscribeLocalEvent(HandleUserInterfaceRangeCheck); // TODO make this a broadcast event subscription again when engine has updated. diff --git a/Content.Shared/Inventory/Events/BeforeEquipEvents.cs b/Content.Shared/Inventory/Events/BeforeEquipEvents.cs new file mode 100644 index 0000000000..6ea408107b --- /dev/null +++ b/Content.Shared/Inventory/Events/BeforeEquipEvents.cs @@ -0,0 +1,50 @@ +namespace Content.Shared.Inventory.Events; + +public abstract class BeforeEquipEventBase(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : EntityEventArgs +{ + /// + /// The entity performing the action. + /// NOT necessarily the one actually "receiving" the equipment. + /// + public readonly EntityUid User = user; + + /// + /// The entity which got equipped to. + /// NOT necessarily the one who performed the interaction. + /// + public readonly EntityUid EquipTarget = equipTarget; + + /// + /// The entity which got equipped. + /// + public readonly EntityUid Equipment = equipment; + + /// + /// The slot the entity got equipped to. + /// + public readonly string Slot = slotDefinition.Name; + + /// + /// The slot group the entity got equipped in. + /// + public readonly string SlotGroup = slotDefinition.SlotGroup; + + /// + /// Slotflags of the slot the entity just got equipped to. + /// + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; +} + +/// +/// Raised directed on an equipee before something is equipped to them. +/// Note: This is only raised when equipped using TryEquip, not if the container is directly modified. +/// +public sealed class BeforeEquipEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : BeforeEquipEventBase(user, equipTarget, equipment, slotDefinition); + +/// +/// Raised directed on equipment before it is equipped to an equipee. +/// Note: This is only raised when equipped using TryEquip, not if the container is directly modified. +/// +public sealed class BeforeGettingEquippedEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : BeforeEquipEventBase(user, equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/BeforeUnequipEvents.cs b/Content.Shared/Inventory/Events/BeforeUnequipEvents.cs new file mode 100644 index 0000000000..a084e1536a --- /dev/null +++ b/Content.Shared/Inventory/Events/BeforeUnequipEvents.cs @@ -0,0 +1,50 @@ +namespace Content.Shared.Inventory.Events; + +public abstract class BeforeUnequipEventBase(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : EntityEventArgs +{ + /// + /// The entity performing the action. + /// NOT necessarily the same as the entity whose equipment is being removed. + /// + public readonly EntityUid User = user; + + /// + /// The entity which was unequipped from. + /// NOT necessarily the one who performed the interaction. + /// + public readonly EntityUid EquipTarget = equipTarget; + + /// + /// The entity which got unequipped. + /// + public readonly EntityUid Equipment = equipment; + + /// + /// The slot the entity got unequipped from. + /// + public readonly string Slot = slotDefinition.Name; + + /// + /// The slot group the entity got unequipped from. + /// + public readonly string SlotGroup = slotDefinition.SlotGroup; + + /// + /// Slotflags of the slot the entity just got unequipped from. + /// + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; +} + +/// +/// Raised directed on an equipee before something is unequipped from them. +/// Note: This is only raised when enequipped using TryUnequip, not if the container is directly modified or the item is deleted. +/// +public sealed class BeforeUnequipEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : BeforeUnequipEventBase(user, equipTarget, equipment, slotDefinition); + +/// +/// Raised directed on equipment before it is unequipped from an equipee. +/// Note: This is only raised when enequipped using TryUnequip, not if the container is directly modified or the item is deleted. +/// +public sealed class BeforeGettingUnequippedEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : BeforeUnequipEventBase(user, equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/EquipAttemptEvents.cs b/Content.Shared/Inventory/Events/EquipAttemptEvents.cs index 2914dd469e..97babafe26 100644 --- a/Content.Shared/Inventory/Events/EquipAttemptEvents.cs +++ b/Content.Shared/Inventory/Events/EquipAttemptEvents.cs @@ -1,14 +1,15 @@ namespace Content.Shared.Inventory.Events; -public abstract class EquipAttemptBase(EntityUid equipee, EntityUid equipTarget, EntityUid equipment, +public abstract class EquipAttemptBase(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : CancellableEntityEventArgs, IInventoryRelayEvent { public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; /// - /// The entity performing the action. NOT necessarily the one actually "receiving" the equipment. + /// The entity performing the action. + /// NOT necessarily the one actually "receiving" the equipment. /// - public readonly EntityUid Equipee = equipee; + public readonly EntityUid User = user; /// /// The entity being equipped to. @@ -39,17 +40,17 @@ public abstract class EquipAttemptBase(EntityUid equipee, EntityUid equipTarget, /// /// Raised on the item that is being equipped. /// -public sealed class BeingEquippedAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition); +public sealed class BeingEquippedAttemptEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : EquipAttemptBase(user, equipTarget, equipment, slotDefinition); /// /// Raised on the entity that is equipping an item. /// -public sealed class IsEquippingAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition); +public sealed class IsEquippingAttemptEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : EquipAttemptBase(user, equipTarget, equipment, slotDefinition); /// /// Raised on the entity on who item is being equipped. /// -public sealed class IsEquippingTargetAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition); +public sealed class IsEquippingTargetAttemptEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : EquipAttemptBase(user, equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/EquippedEvents.cs b/Content.Shared/Inventory/Events/EquippedEvents.cs index 77fbe83e8a..a152a54b44 100644 --- a/Content.Shared/Inventory/Events/EquippedEvents.cs +++ b/Content.Shared/Inventory/Events/EquippedEvents.cs @@ -1,58 +1,42 @@ namespace Content.Shared.Inventory.Events; -public abstract class EquippedEventBase : EntityEventArgs +public abstract class EquippedEventBase(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : EntityEventArgs { /// - /// The entity equipping. + /// The entity which got equipped to. + /// NOT necessarily the one who performed the interaction. /// - public readonly EntityUid Equipee; + public readonly EntityUid EquipTarget = equipTarget; /// /// The entity which got equipped. /// - public readonly EntityUid Equipment; + public readonly EntityUid Equipment = equipment; /// /// The slot the entity got equipped to. /// - public readonly string Slot; + public readonly string Slot = slotDefinition.Name; /// /// The slot group the entity got equipped in. /// - public readonly string SlotGroup; + public readonly string SlotGroup = slotDefinition.SlotGroup; /// /// Slotflags of the slot the entity just got equipped to. /// - public readonly SlotFlags SlotFlags; - - public EquippedEventBase(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) - { - Equipee = equipee; - Equipment = equipment; - Slot = slotDefinition.Name; - SlotGroup = slotDefinition.SlotGroup; - SlotFlags = slotDefinition.SlotFlags; - } + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; } /// -/// Raised directed on an equipee when something is equipped. +/// Raised directed on an equipee when something was equipped. /// -public sealed class DidEquipEvent : EquippedEventBase -{ - public DidEquipEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } -} +public sealed class DidEquipEvent(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : EquippedEventBase(equipTarget, equipment, slotDefinition); /// -/// Raised directed on equipment when it's equipped to an equipee +/// Raised directed on equipment when it was equipped to an equipee. /// -public sealed class GotEquippedEvent : EquippedEventBase -{ - public GotEquippedEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } -} +public sealed class GotEquippedEvent(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : EquippedEventBase(equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs b/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs index a74b3e2d7c..4e557cc157 100644 --- a/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs +++ b/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs @@ -1,14 +1,15 @@ namespace Content.Shared.Inventory.Events; -public abstract class UnequipAttemptEventBase(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment, +public abstract class UnequipAttemptEventBase(EntityUid user, EntityUid unEquipTarget, EntityUid equipment, SlotDefinition slotDefinition) : CancellableEntityEventArgs, IInventoryRelayEvent { public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; /// - /// The entity performing the action. NOT necessarily the same as the entity whose equipment is being removed.. + /// The entity performing the action. + /// NOT necessarily the same as the entity whose equipment is being removed. /// - public readonly EntityUid Unequipee = unequipee; + public readonly EntityUid User = user; /// /// The entity being unequipped from. @@ -39,17 +40,17 @@ public abstract class UnequipAttemptEventBase(EntityUid unequipee, EntityUid unE /// /// Raised on the item that is being unequipped. /// -public sealed class BeingUnequippedAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition); +public sealed class BeingUnequippedAttemptEvent(EntityUid user, EntityUid unEquipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : UnequipAttemptEventBase(user, unEquipTarget, equipment, slotDefinition); /// /// Raised on the entity that is unequipping an item. /// -public sealed class IsUnequippingAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition); +public sealed class IsUnequippingAttemptEvent(EntityUid user, EntityUid unEquipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : UnequipAttemptEventBase(user, unEquipTarget, equipment, slotDefinition); /// /// Raised on the entity from who item is being unequipped. /// -public sealed class IsUnequippingTargetAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition); +public sealed class IsUnequippingTargetAttemptEvent(EntityUid user, EntityUid unEquipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : UnequipAttemptEventBase(user, unEquipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/UnequippedEvents.cs b/Content.Shared/Inventory/Events/UnequippedEvents.cs index 4e1764a7d2..3f553c9591 100644 --- a/Content.Shared/Inventory/Events/UnequippedEvents.cs +++ b/Content.Shared/Inventory/Events/UnequippedEvents.cs @@ -1,52 +1,42 @@ namespace Content.Shared.Inventory.Events; -public abstract class UnequippedEventBase : EntityEventArgs +public abstract class UnequippedEventBase(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : EntityEventArgs { /// - /// The entity unequipping. + /// The entity which was unequipped from. + /// NOT necessarily the one who performed the interaction. /// - public readonly EntityUid Equipee; + public readonly EntityUid EquipTarget = equipTarget; /// /// The entity which got unequipped. /// - public readonly EntityUid Equipment; + public readonly EntityUid Equipment = equipment; /// /// The slot the entity got unequipped from. /// - public readonly string Slot; + public readonly string Slot = slotDefinition.Name; /// /// The slot group the entity got unequipped from. /// - public readonly string SlotGroup; + public readonly string SlotGroup = slotDefinition.SlotGroup; /// /// Slotflags of the slot the entity just got unequipped from. /// - public readonly SlotFlags SlotFlags; - - public UnequippedEventBase(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) - { - Equipee = equipee; - Equipment = equipment; - Slot = slotDefinition.Name; - SlotGroup = slotDefinition.SlotGroup; - SlotFlags = slotDefinition.SlotFlags; - } + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; } -public sealed class DidUnequipEvent : UnequippedEventBase -{ - public DidUnequipEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } -} +/// +/// Raised directed on an equipee when something was unequipped. +/// +public sealed class DidUnequipEvent(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : UnequippedEventBase(equipTarget, equipment, slotDefinition); -public sealed class GotUnequippedEvent : UnequippedEventBase -{ - public GotUnequippedEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } -} +/// +/// Raised directed on equipment when it was unequipped from an equipee. +/// +public sealed class GotUnequippedEvent(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : UnequippedEventBase(equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index ca069b71ca..311761e1e2 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -133,7 +133,7 @@ public abstract partial class InventorySystem { if (!Resolve(target, ref inventory, false)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-equip-cannot")); return false; } @@ -144,14 +144,14 @@ public abstract partial class InventorySystem if (!TryGetSlotContainer(target, slot, out var slotContainer, out var slotDefinition, inventory)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-equip-cannot")); return false; } if (!force && !CanEquip(actor, target, itemUid, slot, out var reason, slotDefinition, inventory, clothing)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString(reason)); return false; } @@ -179,9 +179,17 @@ public abstract partial class InventorySystem return false; } + // give other systems a chance to do stuff before equipping + var beforeGettingEquippedEvent = new BeforeGettingEquippedEvent(actor, target, itemUid, slotDefinition); + var beforeEquipEvent = new BeforeEquipEvent(actor, target, itemUid, slotDefinition); + + RaiseLocalEvent(itemUid, beforeGettingEquippedEvent); + RaiseLocalEvent(target, beforeEquipEvent); + + // actually equip the item if (!_containerSystem.Insert(itemUid, slotContainer)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot")); return false; } @@ -400,14 +408,14 @@ public abstract partial class InventorySystem if (!Resolve(target, ref inventory, false)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot")); return false; } if (!TryGetSlotContainer(target, slot, out var slotContainer, out var slotDefinition, inventory)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot")); return false; } @@ -419,7 +427,7 @@ public abstract partial class InventorySystem if (!force && !CanUnequip(actor, target, slot, out var reason, slotContainer, slotDefinition, inventory)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString(reason)); return false; } @@ -450,6 +458,14 @@ public abstract partial class InventorySystem return false; } + // give other systems a chance do stuff before unequipping + var beforeGettingUnequippedEvent = new BeforeGettingUnequippedEvent(actor, target, removedItem.Value, slotDefinition); + var beforeUnequipEvent = new BeforeUnequipEvent(actor, target, removedItem.Value, slotDefinition); + + RaiseLocalEvent(removedItem.Value, beforeGettingUnequippedEvent); + RaiseLocalEvent(target, beforeUnequipEvent); + + // actually unequip the item if (!_containerSystem.Remove(removedItem.Value, slotContainer, force: force, reparent: reparent)) return false; @@ -457,6 +473,8 @@ public abstract partial class InventorySystem var firstRun = itemsDropped == 0; ++itemsDropped; + // TODO: This is not being checked at the moment if we remove clothing by any other means than TryUnequip, for example when deleting the item or teleporting it away. + // But checking this in a EntGotRemovedFromContainerMessage subscription is fundamentally incompatible with the current prediction API for popups and audio since we don't have a user. foreach (var slotDef in inventory.Slots) { if (slotDef != slotDefinition && slotDef.DependsOn == slotDefinition.Name) diff --git a/Content.Shared/Inventory/InventorySystem.Slots.cs b/Content.Shared/Inventory/InventorySystem.Slots.cs index 3f0bdf10d4..005f78eef7 100644 --- a/Content.Shared/Inventory/InventorySystem.Slots.cs +++ b/Content.Shared/Inventory/InventorySystem.Slots.cs @@ -263,6 +263,10 @@ public partial class InventorySystem : EntitySystem _containers = containers; } + /// + /// Get the next ContainerSlot in this inventory. + /// The slot may not contain an item. + /// public bool MoveNext([NotNullWhen(true)] out ContainerSlot? container) { while (_nextIdx < _slots.Length) @@ -281,6 +285,30 @@ public partial class InventorySystem : EntitySystem return false; } + /// + /// Get the next ContainerSlot in this inventory, along with the corresponding SlotDefinition. + /// The slot may not contain an item. + /// + public bool MoveNext([NotNullWhen(true)] out ContainerSlot? container, [NotNullWhen(true)] out SlotDefinition? slot) + { + while (_nextIdx < _slots.Length) + { + var i = _nextIdx++; + var slotCandidate = _slots[i]; + + if ((slotCandidate.SlotFlags & _flags) == 0) + continue; + + container = _containers[i]; + slot = slotCandidate; + return true; + } + + container = null; + slot = null; + return false; + } + public bool NextItem(out EntityUid item) { while (_nextIdx < _slots.Length) diff --git a/Content.Shared/Inventory/SelfEquipOnlySystem.cs b/Content.Shared/Inventory/SelfEquipOnlySystem.cs index 2bd113e22b..113457e564 100644 --- a/Content.Shared/Inventory/SelfEquipOnlySystem.cs +++ b/Content.Shared/Inventory/SelfEquipOnlySystem.cs @@ -23,7 +23,7 @@ public sealed class SelfEquipOnlySystem : EntitySystem if (TryComp(ent, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) return; - if (args.Equipee != args.EquipTarget) + if (args.User != args.EquipTarget) args.Cancel(); } @@ -32,7 +32,7 @@ public sealed class SelfEquipOnlySystem : EntitySystem if (args.Cancelled) return; - if (args.Unequipee == args.UnEquipTarget) + if (args.User == args.UnEquipTarget) return; if (TryComp(ent, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) diff --git a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs index c260529ded..2315961d97 100644 --- a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs @@ -26,14 +26,12 @@ public sealed class ItemToggleSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - private EntityQuery _query; + [Dependency] private readonly EntityQuery _itemToggleQuery = default!; public override void Initialize() { base.Initialize(); - _query = GetEntityQuery(); - SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(TurnOffOnUnwielded); @@ -121,7 +119,7 @@ public sealed class ItemToggleSystem : EntitySystem /// Same as public bool Toggle(Entity ent, EntityUid? user = null, bool predicted = true, bool showPopup = true) { - if (!_query.Resolve(ent, ref ent.Comp, false)) + if (!_itemToggleQuery.Resolve(ent, ref ent.Comp, false)) return false; return TrySetActive(ent, !ent.Comp.Activated, user, predicted, showPopup); @@ -144,7 +142,7 @@ public sealed class ItemToggleSystem : EntitySystem /// public bool TryActivate(Entity ent, EntityUid? user = null, bool predicted = true, bool showPopup = true) { - if (!_query.Resolve(ent, ref ent.Comp, false)) + if (!_itemToggleQuery.Resolve(ent, ref ent.Comp, false)) return false; var uid = ent.Owner; @@ -191,7 +189,7 @@ public sealed class ItemToggleSystem : EntitySystem /// public bool TryDeactivate(Entity ent, EntityUid? user = null, bool predicted = true, bool showPopup = true) { - if (!_query.Resolve(ent, ref ent.Comp, false)) + if (!_itemToggleQuery.Resolve(ent, ref ent.Comp, false)) return false; var uid = ent.Owner; @@ -322,7 +320,7 @@ public sealed class ItemToggleSystem : EntitySystem public bool IsActivated(Entity ent) { - if (!_query.Resolve(ent, ref ent.Comp, false)) + if (!_itemToggleQuery.Resolve(ent, ref ent.Comp, false)) return true; // assume always activated if no component return ent.Comp.Activated; diff --git a/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs b/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs deleted file mode 100644 index 39be05a148..0000000000 --- a/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Light.Components; - -/// -/// Can activate when collided with. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class LightOnCollideColliderComponent : Component -{ - [DataField] - public string FixtureId = "lightTrigger"; -} diff --git a/Content.Shared/Light/Components/LightOnCollideComponent.cs b/Content.Shared/Light/Components/LightOnCollideComponent.cs deleted file mode 100644 index c3b4bd7396..0000000000 --- a/Content.Shared/Light/Components/LightOnCollideComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Light.Components; - -/// -/// Enables / disables pointlight whenever entities are contacting with it -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class LightOnCollideComponent : Component -{ -} diff --git a/Content.Shared/Light/EntitySystems/LightCollideSystem.cs b/Content.Shared/Light/EntitySystems/LightCollideSystem.cs deleted file mode 100644 index 2de7c5591f..0000000000 --- a/Content.Shared/Light/EntitySystems/LightCollideSystem.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Content.Shared.Light.Components; -using Robust.Shared.Physics.Events; -using Robust.Shared.Physics.Systems; - -namespace Content.Shared.Light.EntitySystems; - -public sealed class LightCollideSystem : EntitySystem -{ - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SlimPoweredLightSystem _lights = default!; - - private EntityQuery _lightQuery; - - public override void Initialize() - { - base.Initialize(); - - _lightQuery = GetEntityQuery(); - - SubscribeLocalEvent(OnPreventCollide); - SubscribeLocalEvent(OnStart); - SubscribeLocalEvent(OnEnd); - - SubscribeLocalEvent(OnCollideShutdown); - } - - private void OnCollideShutdown(Entity ent, ref ComponentShutdown args) - { - // TODO: Check this on the event. - if (TerminatingOrDeleted(ent.Owner)) - return; - - // Regenerate contacts for everything we were colliding with. - var contacts = _physics.GetContacts(ent.Owner); - - while (contacts.MoveNext(out var contact)) - { - if (!contact.IsTouching) - continue; - - var other = contact.OtherEnt(ent.Owner); - - if (_lightQuery.HasComp(other)) - { - _physics.RegenerateContacts(other); - } - } - } - - // You may be wondering what de fok this is doing here. - // At the moment there's no easy way to do collision whitelists based on components. - private void OnPreventCollide(Entity ent, ref PreventCollideEvent args) - { - if (!_lightQuery.HasComp(args.OtherEntity)) - { - args.Cancelled = true; - } - } - - private void OnEnd(Entity ent, ref EndCollideEvent args) - { - if (args.OurFixtureId != ent.Comp.FixtureId) - return; - - if (!_lightQuery.HasComp(args.OtherEntity)) - return; - - // TODO: Engine bug IsTouching box2d yay. - var contacts = _physics.GetTouchingContacts(args.OtherEntity) - 1; - - if (contacts > 0) - return; - - _lights.SetEnabled(args.OtherEntity, false); - } - - private void OnStart(Entity ent, ref StartCollideEvent args) - { - if (args.OurFixtureId != ent.Comp.FixtureId) - return; - - if (!_lightQuery.HasComp(args.OtherEntity)) - return; - - _lights.SetEnabled(args.OtherEntity, true); - } -} diff --git a/Content.Shared/Machines/EntitySystems/SharedMultipartMachineSystem.cs b/Content.Shared/Machines/EntitySystems/SharedMultipartMachineSystem.cs index 7185d60305..ff0693a257 100644 --- a/Content.Shared/Machines/EntitySystems/SharedMultipartMachineSystem.cs +++ b/Content.Shared/Machines/EntitySystems/SharedMultipartMachineSystem.cs @@ -8,15 +8,6 @@ namespace Content.Shared.Machines.EntitySystems; /// public abstract class SharedMultipartMachineSystem : EntitySystem { - protected EntityQuery XformQuery; - - public override void Initialize() - { - base.Initialize(); - - XformQuery = GetEntityQuery(); - } - /// /// Returns whether each non-optional part of the machine has a matched entity /// diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 82cae19ec1..9bdb39ecaf 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Magic.Components; using Content.Shared.Magic.Events; using Content.Shared.Maps; using Content.Shared.Mind; +using Content.Shared.Objectives.Systems; using Content.Shared.Physics; using Content.Shared.Popups; using Content.Shared.Speech.Muting; @@ -67,6 +68,7 @@ public abstract class SharedMagicSystem : EntitySystem [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly SharedChargesSystem _charges = default!; [Dependency] private readonly ExamineSystemShared _examine= default!; + [Dependency] private readonly TargetSystem _target = default!; private static readonly ProtoId InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell"; @@ -268,11 +270,14 @@ public abstract class SharedMagicSystem : EntitySystem #region Projectile Spells private void OnProjectileSpell(ProjectileSpellEvent ev) { - if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || !_net.IsServer) + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer)) return; ev.Handled = true; + if (!_net.IsServer) + return; // client returns handled for predicted audio + var xform = Transform(ev.Performer); var fromCoords = xform.Coordinates; var toCoords = ev.Target; @@ -428,7 +433,7 @@ public abstract class SharedMagicSystem : EntitySystem if (TryComp(target, out var doorComp) && doorComp.State is not DoorState.Open) _door.StartOpening(target); - if (TryComp(target, out var lockComp) && lockComp.Locked) + if (TryComp(target, out var lockComp) && lockComp.Locked && lockComp.BreakOnAccessBreaker) _lock.Unlock(target, performer, lockComp); } } @@ -472,7 +477,7 @@ public abstract class SharedMagicSystem : EntitySystem ev.Handled = true; - var allHumans = _mind.GetAliveHumans(); + var allHumans = _target.GetAliveHumans(); foreach (var human in allHumans) { diff --git a/Content.Shared/Maps/TurfSystem.cs b/Content.Shared/Maps/TurfSystem.cs index f5477441d9..005b05b4d0 100644 --- a/Content.Shared/Maps/TurfSystem.cs +++ b/Content.Shared/Maps/TurfSystem.cs @@ -20,6 +20,7 @@ public sealed class TurfSystem : EntitySystem [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitions = default!; + [Dependency] private readonly EntityQuery _fixtureQuery = default!; /// /// Attempts to get the turf at or under some given coordinates or null if no such turf exists. @@ -77,8 +78,7 @@ public sealed class TurfSystem : EntitySystem if (!Resolve(gridUid, ref grid, ref gridXform)) return false; - var xformQuery = GetEntityQuery(); - var (gridPos, gridRot, matrix) = _transform.GetWorldPositionRotationMatrix(gridXform, xformQuery); + var (gridPos, gridRot, matrix) = _transform.GetWorldPositionRotationMatrix(gridXform); var size = grid.TileSize; var localPos = new Vector2(indices.X * size + (size / 2f), indices.Y * size + (size / 2f)); @@ -90,14 +90,13 @@ public sealed class TurfSystem : EntitySystem tileAabb = tileAabb.Translated(localPos); var intersectionArea = 0f; - var fixtureQuery = GetEntityQuery(); foreach (var ent in _entityLookup.GetEntitiesIntersecting(gridUid, worldBox, LookupFlags.Dynamic | LookupFlags.Static)) { - if (!fixtureQuery.TryGetComponent(ent, out var fixtures)) + if (!_fixtureQuery.TryGetComponent(ent, out var fixtures)) continue; // get grid local coordinates - var (pos, rot) = _transform.GetWorldPositionRotation(xformQuery.GetComponent(ent), xformQuery); + var (pos, rot) = _transform.GetWorldPositionRotation(ent); rot -= gridRot; pos = (-gridRot).RotateVec(pos - gridPos); diff --git a/Content.Shared/Materials/OreSilo/SharedOreSiloSystem.cs b/Content.Shared/Materials/OreSilo/SharedOreSiloSystem.cs index 33168db1db..ad7aeff7dd 100644 --- a/Content.Shared/Materials/OreSilo/SharedOreSiloSystem.cs +++ b/Content.Shared/Materials/OreSilo/SharedOreSiloSystem.cs @@ -10,7 +10,7 @@ public abstract class SharedOreSiloSystem : EntitySystem [Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - private EntityQuery _clientQuery; + [Dependency] private readonly EntityQuery _clientQuery = default!; /// public override void Initialize() @@ -27,8 +27,6 @@ public abstract class SharedOreSiloSystem : EntitySystem SubscribeLocalEvent(OnGetStoredMaterials); SubscribeLocalEvent(OnConsumeStoredMaterials); SubscribeLocalEvent(OnClientShutdown); - - _clientQuery = GetEntityQuery(); } private void OnToggleOreSiloClient(Entity ent, ref ToggleOreSiloClientMessage args) diff --git a/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs b/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs index 386d0989d9..6dbe11776d 100644 --- a/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs +++ b/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs @@ -127,7 +127,7 @@ public enum CryoPodUiKey : byte [Serializable, NetSerializable] public sealed class CryoPodUserMessage : BoundUserInterfaceMessage { - public GasAnalyzerComponent.GasMixEntry GasMix; + public GasMixEntry GasMix; public HealthAnalyzerUiState Health; public FixedPoint2? BeakerCapacity; public List? Beaker; @@ -135,7 +135,7 @@ public sealed class CryoPodUserMessage : BoundUserInterfaceMessage public bool HasDamage; public CryoPodUserMessage( - GasAnalyzerComponent.GasMixEntry gasMix, + GasMixEntry gasMix, HealthAnalyzerUiState health, FixedPoint2? beakerCapacity, List? beaker, diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs index ac1a00d579..f3b8024d6d 100644 --- a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs +++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs @@ -54,10 +54,10 @@ public abstract partial class SharedCryoPodSystem : EntitySystem [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; [Dependency] private readonly StandingStateSystem _standingState = default!; - private EntityQuery _bloodstreamQuery; - private EntityQuery _itemSlotsQuery; - private EntityQuery _dispenserQuery; - private EntityQuery _solutionContainerQuery; + [Dependency] private readonly EntityQuery _bloodstreamQuery = default!; + [Dependency] private readonly EntityQuery _itemSlotsQuery = default!; + [Dependency] private readonly EntityQuery _dispenserQuery = default!; + [Dependency] private readonly EntityQuery _solutionContainerQuery = default!; public override void Initialize() @@ -78,11 +78,6 @@ public abstract partial class SharedCryoPodSystem : EntitySystem SubscribeLocalEvent(OnEjected); SubscribeLocalEvent(OnBodyInserted); - _bloodstreamQuery = GetEntityQuery(); - _itemSlotsQuery = GetEntityQuery(); - _dispenserQuery = GetEntityQuery(); - _solutionContainerQuery = GetEntityQuery(); - InitializeInsideCryoPod(); Subs.BuiEvents(CryoPodUiKey.Key, subs => diff --git a/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs b/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs index 75dbc4a7c8..57be19a776 100644 --- a/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs +++ b/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs @@ -43,7 +43,8 @@ public abstract class SharedSuitSensorSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - private EntityQuery _sensorQuery; + [Dependency] private readonly EntityQuery _sensorQuery = default!; + public override void Initialize() { base.Initialize(); @@ -59,8 +60,6 @@ public abstract class SharedSuitSensorSystem : EntitySystem SubscribeLocalEvent(OnInsert); SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnSuitSensorDoAfter); - - _sensorQuery = GetEntityQuery(); } /// @@ -401,18 +400,17 @@ public abstract class SharedSuitSensorSystem : EntitySystem status.TotalDamage = totalDamage; status.TotalDamageThreshold = totalDamageThreshold; EntityCoordinates coordinates; - var xformQuery = GetEntityQuery(); if (transform.GridUid != null) { coordinates = new EntityCoordinates(transform.GridUid.Value, - Vector2.Transform(_transform.GetWorldPosition(transform, xformQuery), - _transform.GetInvWorldMatrix(xformQuery.GetComponent(transform.GridUid.Value), xformQuery))); + Vector2.Transform(_transform.GetWorldPosition(transform), + _transform.GetInvWorldMatrix(transform.GridUid.Value))); } else if (transform.MapUid != null) { coordinates = new EntityCoordinates(transform.MapUid.Value, - _transform.GetWorldPosition(transform, xformQuery)); + _transform.GetWorldPosition(transform)); } else { diff --git a/Content.Shared/Metabolism/MetabolizerSystem.cs b/Content.Shared/Metabolism/MetabolizerSystem.cs index 3e31fae1ac..af9ee038e2 100644 --- a/Content.Shared/Metabolism/MetabolizerSystem.cs +++ b/Content.Shared/Metabolism/MetabolizerSystem.cs @@ -31,16 +31,13 @@ public sealed class MetabolizerSystem : EntitySystem [Dependency] private readonly SharedEntityEffectsSystem _entityEffects = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; - private EntityQuery _organQuery; - private EntityQuery _solutionQuery; + [Dependency] private readonly EntityQuery _organQuery = default!; + [Dependency] private readonly EntityQuery _solutionQuery = default!; public override void Initialize() { base.Initialize(); - _organQuery = GetEntityQuery(); - _solutionQuery = GetEntityQuery(); - SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent>(OnApplyMetabolicMultiplier); } diff --git a/Content.Shared/Mind/Filters/AliveAiPool.cs b/Content.Shared/Mind/Filters/AliveAiPool.cs new file mode 100644 index 0000000000..88fd557bbf --- /dev/null +++ b/Content.Shared/Mind/Filters/AliveAiPool.cs @@ -0,0 +1,16 @@ +using Content.Shared.Objectives.Systems; +using Content.Shared.Silicons.StationAi; + +namespace Content.Shared.Mind.Filters; + +/// +/// A mind pool that uses . +/// +public sealed partial class AliveAiPool : MindPool +{ + public override void FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, TargetSystem targetSys) + { + var aiSys = entMan.System(); + aiSys.AddAliveAis(minds, exclude); + } +} diff --git a/Content.Shared/Mind/Filters/AliveHumansPool.cs b/Content.Shared/Mind/Filters/AliveHumansPool.cs index c8e5c55bae..1dfa2d2379 100644 --- a/Content.Shared/Mind/Filters/AliveHumansPool.cs +++ b/Content.Shared/Mind/Filters/AliveHumansPool.cs @@ -1,12 +1,14 @@ +using Content.Shared.Objectives.Systems; + namespace Content.Shared.Mind.Filters; /// -/// A mind pool that uses . +/// A mind pool that uses . /// -public sealed partial class AliveHumansPool : IMindPool +public sealed partial class AliveHumansPool : MindPool { - void IMindPool.FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + public override void FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, TargetSystem targetSystem) { - mindSys.AddAliveHumans(minds, exclude); + targetSystem.AddAliveHumans(minds, exclude); } } diff --git a/Content.Shared/Mind/Filters/AntagonistMindFilter.cs b/Content.Shared/Mind/Filters/AntagonistMindFilter.cs index d805138ac3..ff192698f5 100644 --- a/Content.Shared/Mind/Filters/AntagonistMindFilter.cs +++ b/Content.Shared/Mind/Filters/AntagonistMindFilter.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Mind.Filters; /// public sealed partial class AntagonistMindFilter : MindFilter { - protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan) { var roleSys = entMan.System(); return !roleSys.MindIsAntagonist(mind); diff --git a/Content.Shared/Mind/Filters/BodyMindFilter.cs b/Content.Shared/Mind/Filters/BodyMindFilter.cs index 334539634f..0e471b0187 100644 --- a/Content.Shared/Mind/Filters/BodyMindFilter.cs +++ b/Content.Shared/Mind/Filters/BodyMindFilter.cs @@ -10,7 +10,7 @@ public sealed partial class BodyMindFilter : MindFilter [DataField(required: true)] public EntityWhitelist Whitelist = new(); - protected override bool ShouldRemove(Entity ent, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity ent, EntityUid? exclude, IEntityManager entMan) { if (ent.Comp.OwnedEntity is not {} mob) return true; diff --git a/Content.Shared/Mind/Filters/HasRoleMindFilter.cs b/Content.Shared/Mind/Filters/HasRoleMindFilter.cs index e8098d72a2..8e76e5a47e 100644 --- a/Content.Shared/Mind/Filters/HasRoleMindFilter.cs +++ b/Content.Shared/Mind/Filters/HasRoleMindFilter.cs @@ -14,7 +14,7 @@ public sealed partial class HasRoleMindFilter : MindFilter [DataField(required: true)] public EntityWhitelist Whitelist; - protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan) { var roleSys = entMan.System(); return !roleSys.MindHasRole(mind, Whitelist); diff --git a/Content.Shared/Mind/Filters/JobMindFilter.cs b/Content.Shared/Mind/Filters/JobMindFilter.cs index a6565e4d33..d3bec4d8d6 100644 --- a/Content.Shared/Mind/Filters/JobMindFilter.cs +++ b/Content.Shared/Mind/Filters/JobMindFilter.cs @@ -13,7 +13,7 @@ public sealed partial class JobMindFilter : MindFilter [DataField(required: true)] public ProtoId Job; - protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan) { var jobSys = entMan.System(); return jobSys.MindHasJobWithId(mind, Job); diff --git a/Content.Shared/Mind/Filters/MindFilter.cs b/Content.Shared/Mind/Filters/MindFilter.cs index c2daf3e361..47c8c4178b 100644 --- a/Content.Shared/Mind/Filters/MindFilter.cs +++ b/Content.Shared/Mind/Filters/MindFilter.cs @@ -3,25 +3,25 @@ using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Shared.Mind.Filters; /// -/// A mind filter that can be used to filter out minds from a . +/// A mind filter that can be used to filter out minds from a . /// [ImplicitDataDefinitionForInheritors] public abstract partial class MindFilter { /// /// The actual filter function, this has to return false for minds that get removed from the pool. - /// An excluded mind will be the same one passed to . + /// An excluded mind will be the same one passed to . /// /// The mind to check /// The same mind passed to FindMinds - protected abstract bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys); + protected abstract bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan); /// /// The high-level filter function to be used by the mind system. /// - public bool Filter(Entity mind, EntityUid? exclude, EntityManager entMan, SharedMindSystem mindSys) + public bool Filter(Entity mind, EntityUid? exclude, EntityManager entMan) { - return ShouldRemove(mind, exclude, entMan, mindSys) ^ Inverted; + return ShouldRemove(mind, exclude, entMan) ^ Inverted; } /// diff --git a/Content.Shared/Mind/Filters/IMindPool.cs b/Content.Shared/Mind/Filters/MindPool.cs similarity index 51% rename from Content.Shared/Mind/Filters/IMindPool.cs rename to Content.Shared/Mind/Filters/MindPool.cs index 263d15d812..0886e0716d 100644 --- a/Content.Shared/Mind/Filters/IMindPool.cs +++ b/Content.Shared/Mind/Filters/MindPool.cs @@ -1,13 +1,13 @@ -using Robust.Shared.Serialization.Manager.Attributes; +using Content.Shared.Objectives.Systems; namespace Content.Shared.Mind.Filters; /// /// A mind pool that can find minds to use for objectives etc. -/// Further filtered by . +/// Further filtered by . /// [ImplicitDataDefinitionForInheritors] -public partial interface IMindPool +public abstract partial class MindPool { /// /// Add minds for this pool to a hashset. @@ -15,5 +15,7 @@ public partial interface IMindPool /// /// The hashset to add to /// A mind entity that must not be returned - void FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys); + /// entity Manager for further control + /// targeting system which explicitly searches for targets. + public abstract void FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, TargetSystem targetSys); } diff --git a/Content.Shared/Mind/Filters/ObjectiveMindFilter.cs b/Content.Shared/Mind/Filters/ObjectiveMindFilter.cs index 5b64fc36c6..275f49872d 100644 --- a/Content.Shared/Mind/Filters/ObjectiveMindFilter.cs +++ b/Content.Shared/Mind/Filters/ObjectiveMindFilter.cs @@ -10,7 +10,7 @@ public sealed partial class ObjectiveMindFilter : MindFilter [DataField(required: true)] public EntityWhitelist Blacklist = new(); - protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan) { var whitelistSys = entMan.System(); foreach (var obj in mind.Comp.Objectives) diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index a21642c4ec..8571c35aa2 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Systems; using Content.Shared.Players; +using Content.Shared.Silicons.StationAi; using Content.Shared.Speech; using Content.Shared.Whitelist; using Robust.Shared.Containers; @@ -32,20 +33,17 @@ namespace Content.Shared.Mind; public abstract partial class SharedMindSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedObjectivesSystem _objectives = default!; - [Dependency] private readonly SharedPlayerSystem _player = default!; - [Dependency] private readonly MetaDataSystem _metadata = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedPlayerSystem _player = default!; [ViewVariables] protected readonly Dictionary UserMinds = new(); - private HashSet> _pickingMinds = new(); - private readonly EntProtoId _mindProto = "MindBase"; public override void Initialize() @@ -595,57 +593,12 @@ public abstract partial class SharedMindSystem : EntitySystem return TryGetMind(userId, out _, out var mind) ? mind.CharacterName : null; } - /// - /// Returns a list of every living humanoid player's minds, except for a single one which is exluded. - /// A new hashset is allocated for every call, consider using instead. - /// - public HashSet> GetAliveHumans(EntityUid? exclude = null) - { - var allHumans = new HashSet>(); - AddAliveHumans(allHumans, exclude); - return allHumans; - } - - /// - /// Adds to a hashset every living humanoid player's minds, except for a single one which is exluded. - /// - public void AddAliveHumans(HashSet> allHumans, EntityUid? exclude = null) - { - // HumanoidProfileComponent is used to prevent mice, pAIs, etc from being chosen - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var mobState)) - { - // the player needs to have a mind and not be the excluded one + - // the player has to be alive - if (!TryGetMind(uid, out var mind, out var mindComp) || mind == exclude || !_mobState.IsAlive(uid, mobState)) - continue; - - allHumans.Add((mind, mindComp)); - } - } - - /// - /// Picks a random mind from a pool after applying a list of filters. - /// Returns null if no valid mind could be found. - /// - public Entity? PickFromPool(IMindPool pool, List filters, EntityUid? exclude = null) - { - _pickingMinds.Clear(); - pool.FindMinds(_pickingMinds, exclude, EntityManager, this); - FilterMinds(_pickingMinds, filters, exclude); - - if (_pickingMinds.Count == 0) - return null; - - return _random.Pick(_pickingMinds); - } - /// /// Filters minds from a hashset using a single . /// public void FilterMinds(HashSet> minds, MindFilter filter, EntityUid? exclude = null) { - minds.RemoveWhere(mind => filter.Filter(mind, exclude, EntityManager, this)); + minds.RemoveWhere(mind => filter.Filter(mind, exclude, EntityManager)); } /// diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index d531b6bb80..bd96ad5eb9 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -203,14 +203,14 @@ public partial class MobStateSystem private void OnEquipAttempt(EntityUid target, MobStateComponent component, IsEquippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Equipee == target) + if (args.User == target) CheckAct(target, component, args); } private void OnUnequipAttempt(EntityUid target, MobStateComponent component, IsUnequippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Unequipee == target) + if (args.User == target) CheckAct(target, component, args); } diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.cs b/Content.Shared/Mobs/Systems/MobStateSystem.cs index 0497f3cce0..c55c4d1bd6 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.cs @@ -19,12 +19,11 @@ public partial class MobStateSystem : EntitySystem [Dependency] private readonly DamageableSystem _damageable = default!; private ISawmill _sawmill = default!; - private EntityQuery _mobStateQuery; + [Dependency] private readonly EntityQuery _mobStateQuery = default!; public override void Initialize() { _sawmill = _logManager.GetSawmill("MobState"); - _mobStateQuery = GetEntityQuery(); base.Initialize(); SubscribeEvents(); } diff --git a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs index 197d7cfd7c..172ab449b4 100644 --- a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs @@ -21,7 +21,10 @@ public sealed partial class PullerComponent : Component [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, Access(Other = AccessPermissions.ReadWriteExecute)] public TimeSpan NextThrow; - [DataField] + /// + /// Minimum time between pull throws. + /// + [DataField, AutoNetworkedField] public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(1); // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed @@ -32,16 +35,19 @@ public sealed partial class PullerComponent : Component /// /// Entity currently being pulled if applicable. /// - [AutoNetworkedField, DataField] + [DataField, AutoNetworkedField] public EntityUid? Pulling; /// - /// Does this entity need hands to be able to pull something? + /// Does this entity need hands to be able to pull something? /// - [DataField] + [DataField, AutoNetworkedField] public bool NeedsHands = true; - [DataField] + /// + /// The alert shown to the puller indicating that they are pulling something. + /// + [DataField, AutoNetworkedField] public ProtoId PullingAlert = "Pulling"; } diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 0fcdf7ebed..03e37ed3e7 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -614,4 +614,21 @@ public sealed class PullingSystem : EntitySystem StopPulling(pullableUid, pullable); return true; } + + /// + /// Copies compatible datafields of onto the target entity. + /// + /// The entity who's component will be taken. + /// The entity to apply it to. + public void CopyPullerComponent(Entity source, EntityUid target) + { + if (!Resolve(source, ref source.Comp)) + return; + + var targetComp = EnsureComp(target); + targetComp.ThrowCooldown = source.Comp.ThrowCooldown; + targetComp.NeedsHands = source.Comp.NeedsHands; + targetComp.PullingAlert = source.Comp.PullingAlert; + Dirty(target, targetComp); + } } diff --git a/Content.Shared/Movement/Systems/MovementModStatusSystem.cs b/Content.Shared/Movement/Systems/MovementModStatusSystem.cs index a56ff9e683..91b8f304a3 100644 --- a/Content.Shared/Movement/Systems/MovementModStatusSystem.cs +++ b/Content.Shared/Movement/Systems/MovementModStatusSystem.cs @@ -52,7 +52,7 @@ public sealed class MovementModStatusSystem : EntitySystem ref StatusEffectRelayedEvent args ) { - args.Args.ModifySpeed(entity.Comp.WalkSpeedModifier, entity.Comp.WalkSpeedModifier); + args.Args.ModifySpeed(entity.Comp.WalkSpeedModifier, entity.Comp.SprintSpeedModifier); } private void OnRefreshFrictionStatus(Entity ent, ref StatusEffectRelayedEvent args) @@ -158,8 +158,7 @@ public sealed class MovementModStatusSystem : EntitySystem /// Status effect entity whose modifiers we are updating /// New walkSpeedModifer we're applying /// New sprintSpeedModifier we're applying - public bool TryUpdateMovementStatus( - EntityUid uid, + public bool TryUpdateMovementStatus(EntityUid uid, Entity status, float walkSpeedModifier, float sprintSpeedModifier @@ -170,7 +169,8 @@ public sealed class MovementModStatusSystem : EntitySystem status.Comp.SprintSpeedModifier = sprintSpeedModifier; status.Comp.WalkSpeedModifier = walkSpeedModifier; - + Dirty(status); + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); return true; diff --git a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs index 0d6a9858ff..8485dc4386 100644 --- a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs +++ b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs @@ -20,6 +20,8 @@ public abstract class SharedJetpackSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly EntityQuery _jetpackQuery = default!; + public override void Initialize() { base.Initialize(); @@ -54,13 +56,11 @@ public abstract class SharedJetpackSystem : EntitySystem private void OnJetpackUserGravityChanged(ref GravityChangedEvent ev) { var gridUid = ev.ChangedGridIndex; - var jetpackQuery = GetEntityQuery(); - var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var user, out var transform)) { if (transform.GridUid == gridUid && ev.HasGravity && - jetpackQuery.TryGetComponent(user.Jetpack, out var jetpack)) + _jetpackQuery.TryGetComponent(user.Jetpack, out var jetpack)) { _popup.PopupClient(Loc.GetString("jetpack-to-grid"), uid, uid); diff --git a/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs b/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs index 598e4b564a..d55596ad7d 100644 --- a/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs +++ b/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs @@ -99,13 +99,15 @@ public sealed partial class SharedJumpAbilitySystem : EntitySystem if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; + // Make sure to set the datafields before adding the component so that the correct action gets spawned on map init. var targetComp = Factory.GetComponent(); targetComp.Action = ent.Comp.Action; - targetComp.CanCollide = ent.Comp.CanCollide; - targetComp.JumpSound = ent.Comp.JumpSound; - targetComp.CollideKnockdown = ent.Comp.CollideKnockdown; targetComp.JumpDistance = ent.Comp.JumpDistance; targetComp.JumpThrowSpeed = ent.Comp.JumpThrowSpeed; + targetComp.CanCollide = ent.Comp.CanCollide; + targetComp.CollideKnockdown = ent.Comp.CollideKnockdown; + targetComp.JumpSound = ent.Comp.JumpSound; + targetComp.JumpFailedPopup = ent.Comp.JumpFailedPopup; AddComp(args.CloneUid, targetComp, true); } } diff --git a/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs b/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs index fab9552271..63a92fc4ab 100644 --- a/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs +++ b/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs @@ -20,8 +20,8 @@ public abstract class SharedMobCollisionSystem : EntitySystem [Dependency] protected readonly SharedPhysicsSystem Physics = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; - protected EntityQuery MobQuery; - protected EntityQuery PhysicsQuery; + [Dependency] protected readonly EntityQuery MobQuery = default!; + [Dependency] protected readonly EntityQuery PhysicsQuery = default!; /// /// @@ -64,8 +64,6 @@ public abstract class SharedMobCollisionSystem : EntitySystem }, true); Subs.CVar(CfgManager, CCVars.MovementPushMassCap, val => _massDiffCap = val, true); - MobQuery = GetEntityQuery(); - PhysicsQuery = GetEntityQuery(); SubscribeAllEvent(OnCollision); SubscribeLocalEvent(OnMoveModifier); diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index bda05da019..12f23ddf09 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -48,22 +48,22 @@ public abstract partial class SharedMoverController : VirtualController [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly TagSystem _tags = default!; - protected EntityQuery CanMoveInAirQuery; - protected EntityQuery FootstepModifierQuery; - protected EntityQuery FTLQuery; - protected EntityQuery MoverQuery; - protected EntityQuery MapQuery; - protected EntityQuery MapGridQuery; - protected EntityQuery MobMoverQuery; - protected EntityQuery RelayTargetQuery; - protected EntityQuery ModifierQuery; - protected EntityQuery NoRotateQuery; - protected EntityQuery PhysicsQuery; - protected EntityQuery PilotQuery; - protected EntityQuery PreventPilotQuery; - protected EntityQuery RelayQuery; - protected EntityQuery PullableQuery; - protected EntityQuery XformQuery; + [Dependency] protected readonly EntityQuery CanMoveInAirQuery = default!; + [Dependency] protected readonly EntityQuery FootstepModifierQuery = default!; + [Dependency] protected readonly EntityQuery FTLQuery = default!; + [Dependency] protected readonly EntityQuery MoverQuery = default!; + [Dependency] protected readonly EntityQuery MapQuery = default!; + [Dependency] protected readonly EntityQuery MapGridQuery = default!; + [Dependency] protected readonly EntityQuery MobMoverQuery = default!; + [Dependency] protected readonly EntityQuery RelayTargetQuery = default!; + [Dependency] protected readonly EntityQuery ModifierQuery = default!; + [Dependency] protected readonly EntityQuery NoRotateQuery = default!; + [Dependency] protected readonly EntityQuery PhysicsQuery = default!; + [Dependency] protected readonly EntityQuery PilotQuery = default!; + [Dependency] protected readonly EntityQuery PreventPilotQuery = default!; + [Dependency] protected readonly EntityQuery RelayQuery = default!; + [Dependency] protected readonly EntityQuery PullableQuery = default!; + [Dependency] protected readonly EntityQuery XformQuery = default!; private static readonly ProtoId FootstepSoundTag = "FootstepSound"; @@ -84,23 +84,6 @@ public abstract partial class SharedMoverController : VirtualController UpdatesBefore.Add(typeof(TileFrictionController)); base.Initialize(); - MoverQuery = GetEntityQuery(); - MobMoverQuery = GetEntityQuery(); - ModifierQuery = GetEntityQuery(); - RelayTargetQuery = GetEntityQuery(); - PhysicsQuery = GetEntityQuery(); - RelayQuery = GetEntityQuery(); - PullableQuery = GetEntityQuery(); - XformQuery = GetEntityQuery(); - NoRotateQuery = GetEntityQuery(); - CanMoveInAirQuery = GetEntityQuery(); - FootstepModifierQuery = GetEntityQuery(); - MapGridQuery = GetEntityQuery(); - MapQuery = GetEntityQuery(); - FTLQuery = GetEntityQuery(); - PilotQuery = GetEntityQuery(); - PreventPilotQuery = GetEntityQuery(); - SubscribeLocalEvent(OnTileFriction); SubscribeLocalEvent(OnMoverStartup); SubscribeLocalEvent(OnPhysicsBodyChanged); diff --git a/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs b/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs index e69f0c2f7a..e52573ab03 100644 --- a/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs +++ b/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs @@ -9,14 +9,11 @@ namespace Content.Shared.NPC.Systems; /// public sealed partial class NpcFactionSystem { - private EntityQuery _exceptionQuery; - private EntityQuery _trackerQuery; + [Dependency] private readonly EntityQuery _exceptionQuery = default!; + [Dependency] private readonly EntityQuery _trackerQuery = default!; public void InitializeException() { - _exceptionQuery = GetEntityQuery(); - _trackerQuery = GetEntityQuery(); - SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnTrackerShutdown); } @@ -25,13 +22,13 @@ public sealed partial class NpcFactionSystem { foreach (var uid in ent.Comp.Hostiles) { - if (_trackerQuery.TryGetComponent(uid, out var tracker)) + if (_trackerQuery.TryComp(uid, out var tracker)) tracker.Entities.Remove(ent); } foreach (var uid in ent.Comp.Ignored) { - if (_trackerQuery.TryGetComponent(uid, out var tracker)) + if (_trackerQuery.TryComp(uid, out var tracker)) tracker.Entities.Remove(ent); } } @@ -40,7 +37,7 @@ public sealed partial class NpcFactionSystem { foreach (var uid in ent.Comp.Entities) { - if (!_exceptionQuery.TryGetComponent(uid, out var exception)) + if (!_exceptionQuery.TryComp(uid, out var exception)) continue; exception.Ignored.Remove(ent); @@ -114,7 +111,7 @@ public sealed partial class NpcFactionSystem if (!Resolve(ent, ref ent.Comp, false)) return; - if (!ent.Comp.Hostiles.Remove(target) || !_trackerQuery.TryGetComponent(target, out var tracker)) + if (!ent.Comp.Hostiles.Remove(target) || !_trackerQuery.TryComp(target, out var tracker)) return; tracker.Entities.Remove(ent); diff --git a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs index c2425b9264..053e7aa8b4 100644 --- a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs +++ b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs @@ -23,7 +23,7 @@ public sealed class EnergyKatanaSystem : EntitySystem /// private void OnEquipped(Entity ent, ref GotEquippedEvent args) { - _ninja.BindKatana(args.Equipee, ent); + _ninja.BindKatana(args.EquipTarget, ent); } private void OnCheckDash(Entity ent, ref CheckDashEvent args) diff --git a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs index 01f860da12..e5325c55ba 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs @@ -98,7 +98,7 @@ public abstract class SharedNinjaSuitSystem : EntitySystem /// private void OnUnequipped(Entity ent, ref GotUnequippedEvent args) { - var user = args.Equipee; + var user = args.EquipTarget; if (_ninja.NinjaQuery.TryComp(user, out var ninja)) UserUnequippedSuit(ent, (user, ninja)); } diff --git a/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs b/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs index 62ddcecb81..b25c1d79ac 100644 --- a/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs @@ -14,14 +14,12 @@ public abstract class SharedSpaceNinjaSystem : EntitySystem [Dependency] protected readonly SharedNinjaSuitSystem Suit = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; - public EntityQuery NinjaQuery; + [Dependency] public readonly EntityQuery NinjaQuery = default!; public override void Initialize() { base.Initialize(); - NinjaQuery = GetEntityQuery(); - SubscribeLocalEvent(OnNinjaAttacked); SubscribeLocalEvent(OnNinjaAttack); SubscribeLocalEvent(OnShotAttempted); diff --git a/Content.Shared/Nutrition/Components/CreamPieComponent.cs b/Content.Shared/Nutrition/Components/CreamPieComponent.cs index b6bd124084..91ba3b3d6e 100644 --- a/Content.Shared/Nutrition/Components/CreamPieComponent.cs +++ b/Content.Shared/Nutrition/Components/CreamPieComponent.cs @@ -1,21 +1,39 @@ using Content.Shared.Nutrition.EntitySystems; using Robust.Shared.Audio; +using Robust.Shared.GameStates; -namespace Content.Shared.Nutrition.Components +namespace Content.Shared.Nutrition.Components; + +/// +/// Component used for banana cream pies. +/// These can be thrown at someone to stun them and cream their face. +/// +[Access(typeof(SharedCreamPieSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class CreamPieComponent : Component { - [Access(typeof(SharedCreamPieSystem))] - [RegisterComponent] - public sealed partial class CreamPieComponent : Component - { - [DataField("paralyzeTime")] - public float ParalyzeTime { get; private set; } = 1f; + /// + /// The time being hit by this entity will stun you. + /// + [DataField, AutoNetworkedField] + public TimeSpan ParalyzeTime = TimeSpan.FromSeconds(1); - [DataField("sound")] - public SoundSpecifier Sound { get; private set; } = new SoundCollectionSpecifier("desecration"); + /// + /// The sound to play when hitting something. + /// + [DataField] + public SoundSpecifier Sound = new SoundCollectionSpecifier("desecration", AudioParams.Default.WithVariation(0.125f)); - [ViewVariables] - public bool Splatted { get; set; } = false; + /// + /// Has this pie been splatted by hitting something? + /// + [DataField, AutoNetworkedField] + public bool Splatted = false; - public const string PayloadSlotName = "payloadSlot"; - } + /// + /// Items in this container will be triggered when the pie hits something. + /// This allows throwable C4 pies or similar. + /// + [ViewVariables] + public const string PayloadSlotName = "payloadSlot"; } diff --git a/Content.Shared/Nutrition/Components/CreamPiedComponent.cs b/Content.Shared/Nutrition/Components/CreamPiedComponent.cs index 6779fe3666..08c583ff7b 100644 --- a/Content.Shared/Nutrition/Components/CreamPiedComponent.cs +++ b/Content.Shared/Nutrition/Components/CreamPiedComponent.cs @@ -1,19 +1,48 @@ using Content.Shared.Nutrition.EntitySystems; +using Robust.Shared.GameStates; using Robust.Shared.Serialization; +using Robust.Shared.Utility; -namespace Content.Shared.Nutrition.Components +namespace Content.Shared.Nutrition.Components; + +/// +/// Allows this entity to be hit by banana cream pies. +/// See . +/// +[Access(typeof(SharedCreamPieSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(raiseAfterAutoHandleState: true)] +public sealed partial class CreamPiedComponent : Component { - [Access(typeof(SharedCreamPieSystem))] - [RegisterComponent] - public sealed partial class CreamPiedComponent : Component - { - [ViewVariables] - public bool CreamPied { get; set; } = false; - } + /// + /// Was this entity hit by a banana cream pie? + /// This is reset if they get splashed with water. + /// + [DataField, AutoNetworkedField] + public bool CreamPied; - [Serializable, NetSerializable] - public enum CreamPiedVisuals - { - Creamed, - } + /// + /// The sprite to draw on someone's face if they were hit by a pie. + /// The layer will be dynamically added with the component. + /// + [DataField, AutoNetworkedField] + public SpriteSpecifier? Sprite; +} + +/// +/// Key to be used with appearance data, indicating if the entity has a banana cream pie in their face. +/// +[Serializable, NetSerializable] +public enum CreamPiedVisuals +{ + Creamed, +} + +/// +/// The visual layer for the creampied face. +/// Will be dynamically added and removed with the component. +/// +[Serializable, NetSerializable] +public enum CreamPiedVisualLayer +{ + Key, } diff --git a/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs b/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs index 931d47838b..65bc5519ed 100644 --- a/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs +++ b/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Nutrition.EntitySystems; +using Robust.Shared.GameStates; namespace Content.Shared.Nutrition.Components; @@ -9,12 +10,12 @@ namespace Content.Shared.Nutrition.Components; /// In the event that more head-wear & mask functionality is added (like identity systems, or raising/lowering of /// masks), then this component might become redundant. /// -[RegisterComponent, Access(typeof(IngestionSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(IngestionSystem))] public sealed partial class IngestionBlockerComponent : Component { /// - /// Is this component currently blocking consumption. + /// Whether this item currently blocks consuming something. /// - [DataField] - public bool Enabled { get; set; } = true; + [DataField, AutoNetworkedField] + public bool Enabled = true; } diff --git a/Content.Shared/Nutrition/EntitySystems/ExaminableHungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/ExaminableHungerSystem.cs index 1a1418b644..9b30e5d438 100644 --- a/Content.Shared/Nutrition/EntitySystems/ExaminableHungerSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/ExaminableHungerSystem.cs @@ -8,14 +8,13 @@ namespace Content.Shared.Nutrition.EntitySystems; public sealed class ExaminableHungerSystem : EntitySystem { [Dependency] private readonly HungerSystem _hunger = default!; - private EntityQuery _hungerQuery; + + [Dependency] private readonly EntityQuery _hungerQuery = default!; public override void Initialize() { base.Initialize(); - _hungerQuery = GetEntityQuery(); - SubscribeLocalEvent(OnExamine); } diff --git a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs index 3e7c9da122..a4d5d62391 100644 --- a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs +++ b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs @@ -43,9 +43,10 @@ public sealed partial class IngestionSystem args.Cancelled = true; } - private void OnBlockerMaskToggled(Entity ent, ref ItemMaskToggledEvent args) + private void OnBlockerMaskToggled(Entity entity, ref ItemMaskToggledEvent args) { - ent.Comp.Enabled = !args.Mask.Comp.IsToggled; + entity.Comp.Enabled = !args.Mask.Comp.IsToggled; + Dirty(entity); } private void OnIngestionBlockerAttempt(Entity entity, ref IngestionAttemptEvent args) diff --git a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs index 8446cf5d0f..3c70060b9e 100644 --- a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs +++ b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs @@ -15,15 +15,13 @@ public sealed partial class IngestionSystem [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly IGameTiming _timing = default!; - private EntityQuery _utensilsQuery; + [Dependency] private readonly EntityQuery _utensilsQuery = default!; public void InitializeUtensils() { SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(ToolOpenableSystem) }); SubscribeLocalEvent(OnGetEdibleUtensils); - - _utensilsQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs index 74fed83653..ae16a6027b 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs @@ -1,7 +1,12 @@ +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Fluids; +using Content.Shared.IdentityManagement; using Content.Shared.Nutrition.Components; +using Content.Shared.Popups; +using Content.Shared.Rejuvenate; using Content.Shared.Stunnable; using Content.Shared.Throwing; -using JetBrains.Annotations; // WL Golem species start using Content.Shared.Damage; using Content.Shared.Damage.Systems; @@ -10,86 +15,173 @@ using Robust.Shared.Prototypes; using Content.Shared.Buckle; using Content.Shared._WL.Slippery; // WL Golem species end +using Content.Shared.Trigger.Components; +using Content.Shared.Trigger.Systems; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; +using Robust.Shared.Player; -namespace Content.Shared.Nutrition.EntitySystems +namespace Content.Shared.Nutrition.EntitySystems; + +public abstract class SharedCreamPieSystem : EntitySystem { - [UsedImplicitly] - public abstract class SharedCreamPieSystem : EntitySystem + [Dependency] private readonly SharedStunSystem _stunSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly IngestionSystem _ingestion = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPuddleSystem _puddle = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutions = default!; + [Dependency] private readonly TriggerSystem _trigger = default!; + [Dependency] private readonly INetManager _net = default!; + // WL Golem species start + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly IEntityManager _entity = default!; + [Dependency] private readonly SharedBuckleSystem _buckle = default!; + // WL Golem species end + + public override void Initialize() { - [Dependency] private SharedStunSystem _stunSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + base.Initialize(); + + SubscribeLocalEvent(OnCreamPieHit); + SubscribeLocalEvent(OnCreamPieLand); + SubscribeLocalEvent(OnCreamPiedHitBy); + SubscribeLocalEvent(OnSlice); + SubscribeLocalEvent(OnRejuvenate); + } + + /// + /// SPLAT! + /// + public void SplatCreamPie(Entity creamPie) + { + // Already splatted! Do nothing. + if (creamPie.Comp.Splatted) + return; + + // The pie will be queued for deletion but there may be multiple collisions in the same tick, so we prevent it from splatting more than once. + creamPie.Comp.Splatted = true; + Dirty(creamPie); + + // The entity is being deleted, so play the sound at its position rather than parenting. + if (_net.IsServer) // we don't have a user to pass in TODO: make the popup API sane and remove this guard + { + var coordinates = Transform(creamPie).Coordinates; + _audio.PlayPvs(creamPie.Comp.Sound, coordinates); + } + + if (TryComp(creamPie, out var edibleComp)) + { + if (_solutions.TryGetSolution(creamPie.Owner, edibleComp.Solution, out _, out var solution)) + _puddle.TrySpillAt(creamPie.Owner, solution, out _, false); + + _ingestion.SpawnTrash((creamPie.Owner, edibleComp)); + } + + ActivatePayload(creamPie); + PredictedQueueDel(creamPie); + } + + /// + /// Drop any item hidden in the cream pie and trigger it. + /// + public void ActivatePayload(EntityUid uid) + { + // Keep this server side for now since we don't have a user we can pass in for prediction purposes. + // Ideally the popup and audio API will be reworked so that is not needed anymore. + if (_net.IsClient) + return; + + if (_itemSlots.TryGetSlot(uid, CreamPieComponent.PayloadSlotName, out var itemSlot) + && _itemSlots.TryEject(uid, itemSlot, user: null, out var item) + && TryComp(item.Value, out var timerTrigger)) + _trigger.ActivateTimerTrigger((item.Value, timerTrigger)); + } + + /// + /// Sets the creampied status of an entity. + /// This toggles the visuals for the pie in their face. + /// + public void SetCreamPied(Entity ent, bool value) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + if (value == ent.Comp.CreamPied) + return; + + ent.Comp.CreamPied = value; + Dirty(ent); + + _appearance.SetData(ent.Owner, CreamPiedVisuals.Creamed, value); + } + + private void OnCreamPieLand(Entity ent, ref LandEvent args) + { + SplatCreamPie(ent); + } + + private void OnCreamPieHit(Entity ent, ref ThrowDoHitEvent args) + { + SplatCreamPie(ent); + } + + private void OnCreamPiedHitBy(Entity creamPied, ref ThrowHitByEvent args) + { + if (creamPied.Comp.CreamPied || !Exists(args.Thrown) || !TryComp(args.Thrown, out var creamPie)) + return; + + // TODO: Check if they even have a head that can be hit. + SetCreamPied(creamPied.AsNullable(), true); + _stunSystem.TryUpdateParalyzeDuration(creamPied.Owner, creamPie.ParalyzeTime); + // WL Golem species start - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly IEntityManager _entity = default!; - [Dependency] private readonly SharedBuckleSystem _buckle = default!; + if (!_buckle.IsBuckled(creamPied.Owner)) + { + if (_entity.TryGetComponent(creamPied.Owner, out var hardslip)) + { + _damageableSystem.TryChangeDamage(creamPied.Owner, hardslip.FallDamage); + } + } // WL Golem species end - public override void Initialize() - { - base.Initialize(); + // Throwing is not predicted, so the thrower is not equal to the client predicting the collision, so we cannot pass in a user. + // TODO: Make the popup API sane. + if (_net.IsClient) + return; - SubscribeLocalEvent(OnCreamPieHit); - SubscribeLocalEvent(OnCreamPieLand); - SubscribeLocalEvent(OnCreamPiedHitBy); - } + // Shown only to the player that was hit. + _popup.PopupEntity( + Loc.GetString( + "cream-pied-component-on-hit-by-message", + ("thrown", args.Thrown)), + creamPied.Owner, creamPied.Owner); - public void SplatCreamPie(Entity creamPie) - { - // Already splatted! Do nothing. - if (creamPie.Comp.Splatted) - return; + var otherPlayers = Filter.PvsExcept(creamPied.Owner); - creamPie.Comp.Splatted = true; + // Show to everyone else. + _popup.PopupEntity( + Loc.GetString( + "cream-pied-component-on-hit-by-message-others", + ("owner", Identity.Entity(creamPied.Owner, EntityManager)), + ("thrown", args.Thrown)), + creamPied.Owner, otherPlayers, false); + } - SplattedCreamPie(creamPie); - } + private void OnRejuvenate(Entity ent, ref RejuvenateEvent args) + { + SetCreamPied(ent.AsNullable(), false); + } - protected virtual void SplattedCreamPie(Entity entity) { } + // TODO + // A regression occured here. Previously creampies would activate their hidden payload if you tried to eat them. + // However, the refactor to IngestionSystem caused the event to not be reached, + // because eating is blocked if an item is inside the food. - public void SetCreamPied(EntityUid uid, CreamPiedComponent creamPied, bool value) - { - if (value == creamPied.CreamPied) - return; - - creamPied.CreamPied = value; - - if (TryComp(uid, out AppearanceComponent? appearance)) - { - _appearance.SetData(uid, CreamPiedVisuals.Creamed, value, appearance); - } - } - - private void OnCreamPieLand(Entity entity, ref LandEvent args) - { - SplatCreamPie(entity); - } - - private void OnCreamPieHit(Entity entity, ref ThrowDoHitEvent args) - { - SplatCreamPie(entity); - } - - private void OnCreamPiedHitBy(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) - { - if (!Exists(args.Thrown) || !TryComp(args.Thrown, out CreamPieComponent? creamPie)) return; - - SetCreamPied(uid, creamPied, true); - - CreamedEntity(uid, creamPied, args); - - _stunSystem.TryUpdateParalyzeDuration(uid, TimeSpan.FromSeconds(creamPie.ParalyzeTime)); - - // WL Golem species start - if (!_buckle.IsBuckled(uid)) - { - if (_entity.TryGetComponent(uid, out var hardslip)) - { - _damageableSystem.TryChangeDamage(uid, hardslip.FallDamage); - } - } - // WL Golem species end - } - - protected virtual void CreamedEntity(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) {} + private void OnSlice(Entity ent, ref SliceFoodEvent args) + { + ActivatePayload(ent); } } diff --git a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs index 315f8d8115..bbefe4f421 100644 --- a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs +++ b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs @@ -14,15 +14,6 @@ public abstract class SharedObjectivesSystem : EntitySystem [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!; - private EntityQuery _metaQuery; - - public override void Initialize() - { - base.Initialize(); - - _metaQuery = GetEntityQuery(); - } - /// /// Checks requirements and duplicate objectives to see if an objective can be assigned. /// @@ -39,10 +30,10 @@ public abstract class SharedObjectivesSystem : EntitySystem // only check for duplicate prototypes if it's unique if (comp.Unique) { - var proto = _metaQuery.GetComponent(uid).EntityPrototype?.ID; + var proto = MetaData(uid).EntityPrototype?.ID; foreach (var objective in mind.Objectives) { - if (_metaQuery.GetComponent(objective).EntityPrototype?.ID == proto) + if (MetaData(objective).EntityPrototype?.ID == proto) return false; } } diff --git a/Content.Shared/Objectives/Systems/TargetSystem.cs b/Content.Shared/Objectives/Systems/TargetSystem.cs new file mode 100644 index 0000000000..215d87d62f --- /dev/null +++ b/Content.Shared/Objectives/Systems/TargetSystem.cs @@ -0,0 +1,66 @@ +using Content.Shared.Humanoid; +using Content.Shared.Mind; +using Content.Shared.Mind.Filters; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Robust.Shared.Random; + +namespace Content.Shared.Objectives.Systems; + +/// +/// This system stores enumerators to find valid Targets, typically searching for minds. +/// Typically used in conjunction with a or an Objective. +/// +public sealed class TargetSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + + private HashSet> _pickingMinds = new(); + + /// + /// Returns a list of every living humanoid player's minds, except for a single one which is exluded. + /// A new hashset is allocated for every call, consider using instead. + /// + public HashSet> GetAliveHumans(EntityUid? exclude = null) + { + var allHumans = new HashSet>(); + AddAliveHumans(allHumans, exclude); + return allHumans; + } + + /// + /// Adds to a hashset every living humanoid player's minds, except for a single one which is exluded. + /// + public void AddAliveHumans(HashSet> allHumans, EntityUid? exclude = null) + { + // HumanoidProfileComponent is used to prevent mice, pAIs, etc from being chosen + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var mobState)) + { + // the player needs to have a mind and not be the excluded one + + // the player has to be alive + if (!_mind.TryGetMind(uid, out var mind, out var mindComp) || mind == exclude || !_mobState.IsAlive(uid, mobState)) + continue; + + allHumans.Add((mind, mindComp)); + } + } + + /// + /// Picks a random mind from a pool after applying a list of filters. + /// Returns null if no valid mind could be found. + /// + public Entity? PickFromPool(MindPool pool, List filters, EntityUid? exclude = null) + { + _pickingMinds.Clear(); + pool.FindMinds(_pickingMinds, exclude, EntityManager, this); + _mind.FilterMinds(_pickingMinds, filters, exclude); + + if (_pickingMinds.Count == 0) + return null; + + return _random.Pick(_pickingMinds); + } +} diff --git a/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs b/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs index 2dbbfb5efc..327cd608b5 100644 --- a/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs +++ b/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs @@ -3,19 +3,12 @@ using Robust.Shared.GameStates; namespace Content.Shared.PDA.Ringer; /// -/// Opens the store UI when the ringstone is set to the secret code. +/// Makes a PDA able to open store UIs when the ringtone is set to a secret code. /// Traitors are told the code when greeted. /// [RegisterComponent, NetworkedComponent, Access(typeof(SharedRingerSystem))] public sealed partial class RingerUplinkComponent : Component { - /// - /// Notes to set ringtone to in order to lock or unlock the uplink. - /// Set via GenerateUplinkCodeEvent. - /// - [DataField] - public Note[]? Code; - /// /// Whether to show the toggle uplink button in PDA settings. /// diff --git a/Content.Shared/PDA/SharedRingerSystem.cs b/Content.Shared/PDA/SharedRingerSystem.cs index 392cd46e72..ee0a7fb0bc 100644 --- a/Content.Shared/PDA/SharedRingerSystem.cs +++ b/Content.Shared/PDA/SharedRingerSystem.cs @@ -27,6 +27,7 @@ public abstract class SharedRingerSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPdaSystem _pda = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] protected readonly SharedStoreSystem Store = default!; [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; @@ -145,12 +146,12 @@ public abstract class SharedRingerSystem : EntitySystem /// On the client side, it does nothing since the client cannot know the code in advance. /// On the server side, the code is verified. /// - /// The entity with the RingerUplinkComponent. + /// The entity with the RingerUplinkComponent. /// The ringtone to check against the uplink code. /// The entity attempting to toggle the uplink. /// True if the uplink state was toggled, false otherwise. [PublicAPI] - public virtual bool TryToggleUplink(EntityUid uid, Note[] ringtone, EntityUid? user = null) + public virtual bool TryToggleUplink(Entity entity, Note[] ringtone, EntityUid? user = null) { return false; } @@ -177,7 +178,7 @@ public abstract class SharedRingerSystem : EntitySystem return; // Try to toggle the uplink first - if (TryToggleUplink(ent, args.Ringtone)) + if (TryToggleUplink(ent.Owner, args.Ringtone)) return; // Don't save the uplink code as the ringtone UpdateRingerRingtone(ent, args.Ringtone); @@ -244,12 +245,13 @@ public abstract class SharedRingerSystem : EntitySystem ent.Comp.Unlocked = !ent.Comp.Unlocked; // Update PDA UI if needed - if (TryComp(ent, out var pda)) - _pda.UpdatePdaUi(ent, pda); + _pda.UpdatePdaUi(ent.Owner); // Close store UI if we're locking if (!ent.Comp.Unlocked) + { UI.CloseUi(ent.Owner, StoreUiKey.Key); + } return true; } diff --git a/Content.Shared/Paper/PaperSystem.cs b/Content.Shared/Paper/PaperSystem.cs index 556ab59a7e..0ad826757c 100644 --- a/Content.Shared/Paper/PaperSystem.cs +++ b/Content.Shared/Paper/PaperSystem.cs @@ -28,10 +28,11 @@ public sealed class PaperSystem : EntitySystem [Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly EntityQuery _paperQuery = default!; + private static readonly ProtoId WriteIgnoreStampsTag = "WriteIgnoreStamps"; private static readonly ProtoId WriteTag = "Write"; - private EntityQuery _paperQuery; public override void Initialize() { @@ -47,8 +48,6 @@ public sealed class PaperSystem : EntitySystem SubscribeLocalEvent(OnRandomPaperContentMapInit); SubscribeLocalEvent(OnPaperWrite); - - _paperQuery = GetEntityQuery(); } private void OnMapInit(Entity entity, ref MapInitEvent args) diff --git a/Content.Shared/Photography/PhotographComponent.cs b/Content.Shared/Photography/PhotographComponent.cs new file mode 100644 index 0000000000..3fb065a6e7 --- /dev/null +++ b/Content.Shared/Photography/PhotographComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Utility; + +namespace Content.Shared.Photography; + +/// +/// Represents the photograph data on a picture. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PhotographComponent : Component +{ + /// + /// The description of the photographed object. + /// + [DataField, AutoNetworkedField] + public FormattedMessage? Description; + + /// + /// The full text mentioning the name of the photographed object. + /// For example "This is a picture of Urist McHands" + /// + [DataField, AutoNetworkedField] + public string? NameText; +} diff --git a/Content.Shared/Photography/PhotographySystem.cs b/Content.Shared/Photography/PhotographySystem.cs new file mode 100644 index 0000000000..e8b2334b25 --- /dev/null +++ b/Content.Shared/Photography/PhotographySystem.cs @@ -0,0 +1,96 @@ +using Content.Shared.EntityTable; +using Content.Shared.Examine; +using Content.Shared.Flash; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.IdentityManagement; +using Robust.Shared.Network; +using Robust.Shared.Utility; + +namespace Content.Shared.Photography; + +/// +/// Handles everything related to photography. +/// +public sealed class PhotographySystem : EntitySystem +{ + [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly EntityTableSystem _tables = default!; + [Dependency] private readonly INetManager _net = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnFlashActivated); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + using (args.PushGroup(nameof(PhotographComponent))) + { + if (string.IsNullOrEmpty(ent.Comp.NameText)) + args.PushText(Loc.GetString("photograph-name-text-empty")); + else + args.PushText(ent.Comp.NameText); + if (ent.Comp.Description != null) + // TODO: For some weird reason ExamineSystem is adding a new line at the end of message we are pushing with each examine. + // I'm not soaping this PR even more, so for now I'll just bandaid that by sending a clone to prevent it from getting modified. + args.PushMessage(new FormattedMessage(ent.Comp.Description)); + } + } + + // The flash system is handling charges and all interactions, we just print the picture afterwards. + private void OnFlashActivated(Entity ent, ref AfterFlashActivatedEvent args) + { + TakePicture(ent, args.Target, args.User); + } + + /// + /// Processes entity aimed at with a camera and prints a picture of it. + /// TODO: This is basically a placeholder mechanic for a more elaborate photography system. + /// However, this will need major refactoring to be possible. See + /// https://github.com/space-wizards/docs/pull/307 and + /// https://github.com/space-wizards/space-station-14/pull/43327 + /// for details. + /// + public void TakePicture(Entity camera, EntityUid? target, EntityUid? user) + { + if (_net.IsClient) + return; // Can't interact with predictively spawned entities yet. + + var tableResult = _tables.GetSpawns(camera.Comp.Photographs); + var coords = Transform(camera).Coordinates; + + FormattedMessage? description = null; + string? nameText = null; + if (target != null) + { + description = _examine.GetExamineText(target.Value, user); + // Get the full string now instead of indexing it later because we need the entity to know if it uses a proper noun or not. + nameText = Loc.GetString("photograph-name-text", ("entity", Identity.Entity(target.Value, EntityManager))); + // We don't want photographs to contain the descriptions of other photographs, because that makes entities with, in theory, infinite descriptions. + if (HasComp(target.Value)) + { + description = null; + nameText = Loc.GetString("photograph-name-text-photograph"); + } + } + + foreach (var prototype in tableResult) + { + // we generate an individual photograph (there should be only one tough) + var spawned = Spawn(prototype, coords); + var photoComp = EnsureComp(spawned); + photoComp.NameText = nameText; + photoComp.Description = description; + Dirty(spawned, photoComp); + + _hands.PickupOrDrop(user, spawned, dropNear: true); + } + } +} diff --git a/Content.Shared/Photography/PictureTakerComponent.cs b/Content.Shared/Photography/PictureTakerComponent.cs new file mode 100644 index 0000000000..a636b1ede2 --- /dev/null +++ b/Content.Shared/Photography/PictureTakerComponent.cs @@ -0,0 +1,19 @@ +using Content.Shared.EntityTable.EntitySelectors; +using Robust.Shared.GameStates; + +namespace Content.Shared.Photography; + +// since camera is taken... +/// +/// Marks an entity as able to take pictures (when you smash other entities with it). +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PictureTakerComponent : Component +{ + /// + /// The entities that will be spawned & given a PhotographComponent when the owning entity is used. + /// The table should only select one item at a time. + /// + [DataField] + public EntityTableSelector? Photographs; +} diff --git a/Content.Shared/Physics/Controllers/SharedConveyorController.cs b/Content.Shared/Physics/Controllers/SharedConveyorController.cs index 74b1a5026d..26f9df9424 100644 --- a/Content.Shared/Physics/Controllers/SharedConveyorController.cs +++ b/Content.Shared/Physics/Controllers/SharedConveyorController.cs @@ -31,20 +31,16 @@ public abstract class SharedConveyorController : VirtualController private ConveyorJob _job; - private EntityQuery _conveyorQuery; - private EntityQuery _conveyedQuery; - protected EntityQuery PhysicsQuery; - protected EntityQuery XformQuery; + [Dependency] private readonly EntityQuery _conveyorQuery = default!; + [Dependency] private readonly EntityQuery _conveyedQuery = default!; + [Dependency] protected readonly EntityQuery PhysicsQuery = default!; + [Dependency] protected readonly EntityQuery XformQuery = default!; protected HashSet Intersecting = new(); public override void Initialize() { _job = new ConveyorJob(this); - _conveyorQuery = GetEntityQuery(); - _conveyedQuery = GetEntityQuery(); - PhysicsQuery = GetEntityQuery(); - XformQuery = GetEntityQuery(); UpdatesAfter.Add(typeof(SharedMoverController)); diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 5d64803273..2f7e1dfdfd 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -26,8 +26,9 @@ public abstract class SharedNavMapSystem : EntitySystem [Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!; [Robust.Shared.IoC.Dependency] private readonly INetManager _net = default!; + [Robust.Shared.IoC.Dependency] private readonly EntityQuery _doorQuery = default!; + private static readonly ProtoId[] WallTags = {"Wall", "Window"}; - private EntityQuery _doorQuery; public override void Initialize() { @@ -36,8 +37,6 @@ public abstract class SharedNavMapSystem : EntitySystem // Data handling events SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnConfigurableExamined); - - _doorQuery = GetEntityQuery(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Content.Shared/Power/EntitySystems/PowerStateSystem.cs b/Content.Shared/Power/EntitySystems/PowerStateSystem.cs index aba41e2432..c669557115 100644 --- a/Content.Shared/Power/EntitySystems/PowerStateSystem.cs +++ b/Content.Shared/Power/EntitySystems/PowerStateSystem.cs @@ -11,14 +11,7 @@ public abstract class SharedPowerStateSystem : EntitySystem { [Dependency] private readonly SharedPowerReceiverSystem _powerReceiverSystem = default!; - private EntityQuery _powerStateQuery; - - public override void Initialize() - { - base.Initialize(); - - _powerStateQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _powerStateQuery = default!; /// /// Sets the working state of the entity, adjusting its power draw accordingly. diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index e1a4faa075..b9dc1f972c 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -1176,8 +1176,7 @@ namespace Content.Shared.Preferences { var sponsorsManager = IoCManager.Resolve(); sponsorPrototypes = sponsorsManager.TryGetServerPrototypes(session.UserId, out var prototypes) - ? prototypes.ToArray() - : Array.Empty(); + ? prototypes.ToArray() : Array.Empty(); } catch (Exception) { diff --git a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs index a00ec1d0c6..56c3c2a9a6 100644 --- a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs +++ b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs @@ -13,16 +13,12 @@ public sealed class ProximityDetectionSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ItemToggleSystem _toggle = default!; - private EntityQuery _xformQuery; - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnToggled); - - _xformQuery = GetEntityQuery(); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -85,7 +81,7 @@ public sealed class ProximityDetectionSystem : EntitySystem { var component = detector.Comp; - if (!_xformQuery.TryGetComponent(detector, out var transform)) + if (!TryComp(detector, out TransformComponent? transform)) return; if (Deleted(component.Target)) @@ -98,7 +94,7 @@ public sealed class ProximityDetectionSystem : EntitySystem while (query.MoveNext(out var uid)) { - if (!_xformQuery.TryGetComponent(uid, out var xForm)) + if (!TryComp(uid, out TransformComponent? xForm)) continue; if (!transform.Coordinates.TryDistance(EntityManager, xForm.Coordinates, out var distance) || diff --git a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs index 874024a905..51979f1c02 100644 --- a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs +++ b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs @@ -1,9 +1,12 @@ +using Content.Shared.Radiation.Systems; + namespace Content.Shared.Radiation.Components; /// /// Irradiate all objects in range. /// [RegisterComponent] +[Access(typeof(SharedRadiationSystem))] public sealed partial class RadiationSourceComponent : Component { /// diff --git a/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs new file mode 100644 index 0000000000..a431ffa4fd --- /dev/null +++ b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs @@ -0,0 +1,21 @@ +using Content.Shared.Radiation.Components; + +namespace Content.Shared.Radiation.Systems; + +public abstract partial class SharedRadiationSystem : EntitySystem +{ + [Dependency] protected readonly EntityQuery SourceQuery = default!; + + /// + /// Sets the intensity of a to the passed intensity. + /// + /// Radiation source we're attempting to update + /// Intensity we're setting the source to. + public void SetIntensity(Entity entity, float intensity) + { + if (!SourceQuery.Resolve(entity, ref entity.Comp, false)) + return; + + entity.Comp.Intensity = intensity; + } +} diff --git a/Content.Shared/Random/Rules/NearbyAccess.cs b/Content.Shared/Random/Rules/NearbyAccess.cs index 2a8ad077c6..3f91d0fbed 100644 --- a/Content.Shared/Random/Rules/NearbyAccess.cs +++ b/Content.Shared/Random/Rules/NearbyAccess.cs @@ -31,9 +31,7 @@ public sealed partial class NearbyAccessRule : RulesRule public override bool Check(EntityManager entManager, EntityUid uid) { - var xformQuery = entManager.GetEntityQuery(); - - if (!xformQuery.TryGetComponent(uid, out var xform) || + if (!entManager.TryGetComponent(uid, out TransformComponent? xform) || xform.MapUid == null) { return false; @@ -44,7 +42,7 @@ public sealed partial class NearbyAccessRule : RulesRule var reader = entManager.System(); var found = false; - var worldPos = transform.GetWorldPosition(xform, xformQuery); + var worldPos = transform.GetWorldPosition(xform); var count = 0; // TODO: Update this when we get the callback version @@ -54,7 +52,7 @@ public sealed partial class NearbyAccessRule : RulesRule { if (!reader.AreAccessTagsAllowed(Access, comp) || Anchored && - (!xformQuery.TryGetComponent(comp, out var compXform) || + (!entManager.TryGetComponent(comp, out TransformComponent? compXform) || !compXform.Anchored)) { continue; diff --git a/Content.Shared/Random/Rules/NearbyComponents.cs b/Content.Shared/Random/Rules/NearbyComponents.cs index 13108e88d6..eb5b7073eb 100644 --- a/Content.Shared/Random/Rules/NearbyComponents.cs +++ b/Content.Shared/Random/Rules/NearbyComponents.cs @@ -22,9 +22,8 @@ public sealed partial class NearbyComponentsRule : RulesRule public override bool Check(EntityManager entManager, EntityUid uid) { var inRange = new HashSet>(); - var xformQuery = entManager.GetEntityQuery(); - if (!xformQuery.TryGetComponent(uid, out var xform) || + if (!entManager.TryGetComponent(uid, out TransformComponent? xform) || xform.MapUid == null) { return false; @@ -44,7 +43,7 @@ public sealed partial class NearbyComponentsRule : RulesRule foreach (var comp in inRange) { if (Anchored && - (!xformQuery.TryGetComponent(comp, out var compXform) || + (!entManager.TryGetComponent(comp, out TransformComponent? compXform) || !compXform.Anchored)) { continue; diff --git a/Content.Shared/RepulseAttract/RepulseAttractSystem.cs b/Content.Shared/RepulseAttract/RepulseAttractSystem.cs index f95a38d2eb..e526ab00dc 100644 --- a/Content.Shared/RepulseAttract/RepulseAttractSystem.cs +++ b/Content.Shared/RepulseAttract/RepulseAttractSystem.cs @@ -20,14 +20,14 @@ public sealed class RepulseAttractSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _xForm = default!; [Dependency] private readonly UseDelaySystem _delay = default!; - private EntityQuery _physicsQuery; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + private HashSet _entSet = new(); + public override void Initialize() { base.Initialize(); - _physicsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnMeleeAttempt, before: [typeof(UseDelayOnMeleeHitSystem)], after: [typeof(SharedWieldableSystem)]); SubscribeLocalEvent(OnRepulseAttractAction); } @@ -44,7 +44,7 @@ public sealed class RepulseAttractSystem : EntitySystem { if (args.Handled) return; - + var position = _xForm.GetMapCoordinates(args.Performer); args.Handled = TryRepulseAttract(position, args.Performer, ent.Comp.Speed, ent.Comp.Range, ent.Comp.Whitelist, ent.Comp.CollisionMask); } diff --git a/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs b/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs index 02ffc77616..0afb845a9f 100644 --- a/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs +++ b/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs @@ -39,8 +39,8 @@ public sealed partial class TechnologyDiskComponent : Component [DataField] public Dictionary DiskPricePerTier = new() { - [1] = 100, - [2] = 500, - [3] = 1500 + [1] = 50, + [2] = 135, + [3] = 1000, }; } diff --git a/Content.Shared/Revenant/Components/RevenantComponent.cs b/Content.Shared/Revenant/Components/RevenantComponent.cs index e434bba1d9..846a28e6c7 100644 --- a/Content.Shared/Revenant/Components/RevenantComponent.cs +++ b/Content.Shared/Revenant/Components/RevenantComponent.cs @@ -214,4 +214,17 @@ public sealed partial class RevenantComponent : Component [DataField("harvestingState")] public string HarvestingState = "harvesting"; #endregion + + /// + /// The scaling for passively chilling surroundings. + /// + [DataField] + public FixedPoint2 ChillScaling = 7000; + + /// + /// The upper limit for essence when passively chilling surroundings. + /// Beyond this point, more essence will not cause more chilling. + /// + [DataField] + public FixedPoint2 ChillUpperBound = 500; } diff --git a/Content.Shared/Rootable/RootableSystem.cs b/Content.Shared/Rootable/RootableSystem.cs index 1d718dbdf3..451b994aff 100644 --- a/Content.Shared/Rootable/RootableSystem.cs +++ b/Content.Shared/Rootable/RootableSystem.cs @@ -44,16 +44,13 @@ public sealed class RootableSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; - private EntityQuery _puddleQuery; - private EntityQuery _physicsQuery; + [Dependency] private readonly EntityQuery _puddleQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; public override void Initialize() { base.Initialize(); - _puddleQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnRootableMapInit); SubscribeLocalEvent(OnRootableShutdown); SubscribeLocalEvent(OnStartCollide); @@ -121,12 +118,15 @@ public sealed class RootableSystem : EntitySystem if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; - var cloneComp = EnsureComp(args.CloneUid); + // Make sure to set the datafields before adding the component so that the correct action gets spawned on map init. + var cloneComp = Factory.GetComponent(); + cloneComp.Action = ent.Comp.Action; + cloneComp.RootedAlert = ent.Comp.RootedAlert; cloneComp.TransferRate = ent.Comp.TransferRate; cloneComp.TransferFrequency = ent.Comp.TransferFrequency; cloneComp.SpeedModifier = ent.Comp.SpeedModifier; cloneComp.RootSound = ent.Comp.RootSound; - Dirty(args.CloneUid, cloneComp); + AddComp(args.CloneUid, cloneComp, true); } private void OnRootableMapInit(Entity ent, ref MapInitEvent args) diff --git a/Content.Shared/Sericulture/SericultureSystem.cs b/Content.Shared/Sericulture/SericultureSystem.cs index fb87c907f4..e733b93483 100644 --- a/Content.Shared/Sericulture/SericultureSystem.cs +++ b/Content.Shared/Sericulture/SericultureSystem.cs @@ -41,13 +41,15 @@ public abstract partial class SharedSericultureSystem : EntitySystem if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; - var comp = EnsureComp(args.CloneUid); - comp.PopupText = ent.Comp.PopupText; - comp.ProductionLength = ent.Comp.ProductionLength; - comp.HungerCost = ent.Comp.HungerCost; - comp.EntityProduced = ent.Comp.EntityProduced; - comp.MinHungerThreshold = ent.Comp.MinHungerThreshold; - Dirty(args.CloneUid, comp); + // Make sure to set the datafields before adding the component so that the correct action gets spawned on map init. + var cloneComp = Factory.GetComponent(); + cloneComp.PopupText = ent.Comp.PopupText; + cloneComp.EntityProduced = ent.Comp.EntityProduced; + cloneComp.Action = ent.Comp.Action; + cloneComp.ProductionLength = ent.Comp.ProductionLength; + cloneComp.HungerCost = ent.Comp.HungerCost; + cloneComp.MinHungerThreshold = ent.Comp.MinHungerThreshold; + AddComp(args.CloneUid, cloneComp, true); } /// diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs index 891a5c9e12..c551cbc112 100644 --- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs +++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs @@ -26,9 +26,8 @@ public abstract partial class SharedShuttleSystem : EntitySystem public const float FTLBufferRange = 8f; public const float TileDensityMultiplier = 0.5f; - private EntityQuery _gridQuery; - private EntityQuery _physicsQuery; - private EntityQuery _xformQuery; + [Dependency] private readonly EntityQuery _gridQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; private List> _grids = new(); @@ -37,10 +36,6 @@ public abstract partial class SharedShuttleSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnGridFixtureChange); - - _gridQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); } private void OnGridFixtureChange(EntityUid uid, FixturesComponent manager, GridFixtureChangeEvent args) @@ -58,7 +53,7 @@ public abstract partial class SharedShuttleSystem : EntitySystem public bool CanFTLTo(EntityUid shuttleUid, MapId targetMap, EntityUid consoleUid) { var mapUid = Maps.GetMapOrInvalid(targetMap); - var shuttleMap = _xformQuery.GetComponent(shuttleUid).MapID; + var shuttleMap = Transform(shuttleUid).MapID; if (shuttleMap == targetMap) return true; @@ -190,7 +185,7 @@ public abstract partial class SharedShuttleSystem : EntitySystem public bool FTLFree(EntityUid shuttleUid, EntityCoordinates coordinates, Angle angle, List? exclusionZones) { if (!_physicsQuery.TryGetComponent(shuttleUid, out var shuttlePhysics) || - !_xformQuery.TryGetComponent(shuttleUid, out var shuttleXform)) + !TryComp(shuttleUid, out TransformComponent? shuttleXform)) { return false; } diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.Module.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.Module.cs index 3095190710..5d6f162414 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.Module.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.Module.cs @@ -9,7 +9,7 @@ namespace Content.Shared.Silicons.Borgs; public abstract partial class SharedBorgSystem { - private EntityQuery _moduleQuery; + [Dependency] private readonly EntityQuery _moduleQuery = default!; public void InitializeModule() { @@ -30,8 +30,6 @@ public abstract partial class SharedBorgSystem SubscribeLocalEvent>( OnComponentModuleInstalledRelay); - - _moduleQuery = GetEntityQuery(); } #region BorgModule diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs index 41a27c0a38..ff27fd9d9d 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs @@ -72,8 +72,8 @@ public abstract partial class SharedStationAiSystem : EntitySystem // StationAiOverlay handles the static overlay. It also handles interaction blocking on client and server // for anything under it. - private EntityQuery _broadphaseQuery; - private EntityQuery _gridQuery; + [Dependency] private readonly EntityQuery _broadphaseQuery = default!; + [Dependency] private readonly EntityQuery _gridQuery = default!; private static readonly EntProtoId DefaultAi = "StationAiBrain"; private readonly ProtoId _downloadChatNotificationPrototype = "IntellicardDownload"; @@ -82,9 +82,6 @@ public abstract partial class SharedStationAiSystem : EntitySystem { base.Initialize(); - _broadphaseQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - InitializeAirlock(); InitializeHeld(); InitializeLight(); @@ -632,6 +629,29 @@ public abstract partial class SharedStationAiSystem : EntitySystem return _blocker.CanComplexInteract(entity.Owner); } + + /// + /// Gets all alive AI minds and adds them to the inputted hashset, excluding one optional mind + /// + /// Hashset of alive AI minds + /// Optional mind to exclude + public void AddAliveAis(HashSet> aliveAis, EntityUid? exclude = null) + { + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out _, out var aiHolder)) + { + // the player needs to have a mind and not be the excluded one + + // the player has to be alive + if (!TryGetHeld((uid, aiHolder), out var held) || _mobState.IsDead(held.Value)) + continue; + + if (!_mind.TryGetMind(held.Value, out var mind, out var mindComp) || mind == exclude) + continue; + + aliveAis.Add((mind, mindComp)); + } + } } public sealed partial class JumpToCoreEvent : InstantActionEvent diff --git a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs index 7ae27da497..253c9df60c 100644 --- a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs +++ b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs @@ -20,6 +20,8 @@ public sealed class StationAiVisionSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _xforms = default!; [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] private readonly EntityQuery _occluderQuery = default!; + private SeedJob _seedJob; private ViewJob _job; @@ -27,8 +29,6 @@ public sealed class StationAiVisionSystem : EntitySystem private readonly HashSet> _seeds = new(); private readonly HashSet _viewportTiles = new(); - private EntityQuery _occluderQuery; - // Dummy set private readonly HashSet _singleTiles = new(); @@ -45,8 +45,6 @@ public sealed class StationAiVisionSystem : EntitySystem { base.Initialize(); - _occluderQuery = GetEntityQuery(); - _seedJob = new() { System = this, diff --git a/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs b/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs index b7bf071457..eccfbe58d8 100644 --- a/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs +++ b/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Shared.Radiation.Components; +using Content.Shared.Radiation.Systems; using Content.Shared.Singularity.Components; using Content.Shared.Singularity.Events; using Robust.Shared.Containers; @@ -19,6 +20,7 @@ public abstract class SharedSingularitySystem : EntitySystem [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly SharedEventHorizonSystem _horizons = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedRadiationSystem _radiation = default!; [Dependency] protected readonly IViewVariablesManager Vvm = default!; #endregion Dependencies @@ -138,10 +140,7 @@ public abstract class SharedSingularitySystem : EntitySystem _visualizer.SetData(uid, SingularityAppearanceKeys.Singularity, singularity.Level, appearance); } - if (TryComp(uid, out var radiationSource)) - { - UpdateRadiation(uid, singularity, radiationSource); - } + UpdateRadiation(uid, singularity); RaiseLocalEvent(uid, new SingularityLevelChangedEvent(singularity.Level, oldValue, singularity)); if (singularity.Level <= 0) @@ -165,12 +164,12 @@ public abstract class SharedSingularitySystem : EntitySystem /// /// The uid of the singularity to update the radiation of. /// The state of the singularity to update the radiation of. - /// The state of the radioactivity of the singularity to update. - private void UpdateRadiation(EntityUid uid, SingularityComponent? singularity = null, RadiationSourceComponent? rads = null) + private void UpdateRadiation(EntityUid uid, SingularityComponent? singularity = null) { - if(!Resolve(uid, ref singularity, ref rads, logMissing: false)) + if(!Resolve(uid, ref singularity, logMissing: false)) return; - rads.Intensity = singularity.Level * singularity.RadsPerLevel; + + _radiation.SetIntensity(uid, singularity.Level * singularity.RadsPerLevel); } #endregion Getters/Setters diff --git a/Content.Shared/Slippery/SlidingSystem.cs b/Content.Shared/Slippery/SlidingSystem.cs index e0084c11ab..2bd771da5d 100644 --- a/Content.Shared/Slippery/SlidingSystem.cs +++ b/Content.Shared/Slippery/SlidingSystem.cs @@ -13,14 +13,12 @@ public sealed class SlidingSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!; - private EntityQuery _slipperyQuery; + [Dependency] private readonly EntityQuery _slipperyQuery = default!; public override void Initialize() { base.Initialize(); - _slipperyQuery = GetEntityQuery(); - SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnStand); diff --git a/Content.Shared/Slippery/SlipperySystem.cs b/Content.Shared/Slippery/SlipperySystem.cs index 9fdc6e58bd..a2f81c2a21 100644 --- a/Content.Shared/Slippery/SlipperySystem.cs +++ b/Content.Shared/Slippery/SlipperySystem.cs @@ -41,18 +41,14 @@ public sealed class SlipperySystem : EntitySystem // WL Golem species end [Dependency] private readonly SpeedModifierContactsSystem _speedModifier = default!; - private EntityQuery _knockedDownQuery; - private EntityQuery _physicsQuery; - private EntityQuery _slidingQuery; + [Dependency] private readonly EntityQuery _knockedDownQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + [Dependency] private readonly EntityQuery _slidingQuery = default!; public override void Initialize() { base.Initialize(); - _knockedDownQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _slidingQuery = GetEntityQuery(); - SubscribeLocalEvent(HandleAttemptCollide); SubscribeLocalEvent(HandleStepTrigger); SubscribeLocalEvent(OnNoSlipAttempt); diff --git a/Content.Shared/Stacks/SharedStackSystem.API.cs b/Content.Shared/Stacks/SharedStackSystem.API.cs index 1356c8ecda..372255701c 100644 --- a/Content.Shared/Stacks/SharedStackSystem.API.cs +++ b/Content.Shared/Stacks/SharedStackSystem.API.cs @@ -7,6 +7,29 @@ namespace Content.Shared.Stacks; // Partial for public API functions. public abstract partial class SharedStackSystem { + #region Spawning + // Interactions with spawned entities can not currently be predicted. + // This means that when spawning a stack it should not be given directly to the player, but have some intermediary. + + /// + /// Gets or spawns an entity with a stack count of 1. + /// Useful when you don't know if something is a stack, and want to make sure you just have a single entity. + /// + /// An entity to pop one count off the stack. + /// An entity with a stack count of 1, or a non-stack. + [PublicAPI] + public EntityUid GetOne(Entity stackEnt) + { + if (!Resolve(stackEnt.Owner, ref stackEnt.Comp, logMissing: false) // If it's not a stack, you already have the one + || stackEnt.Comp.Count == 1) // If it's at one, just use this + return stackEnt.Owner; + + ReduceCount(stackEnt, 1); + var stackId = _prototype.Index(stackEnt.Comp.StackTypeId); + return PredictedSpawnNextToOrDrop(stackId.Spawn, stackEnt.Owner); + } + + #endregion #region Merge Stacks /// diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index 4ed0da5b9e..7cc93d25f6 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -24,19 +24,10 @@ public abstract class SharedStationSpawningSystem : EntitySystem [Dependency] private readonly SharedStorageSystem _storage = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; - private EntityQuery _handsQuery; - private EntityQuery _inventoryQuery; - private EntityQuery _storageQuery; - private EntityQuery _xformQuery; - - public override void Initialize() - { - base.Initialize(); - _handsQuery = GetEntityQuery(); - _inventoryQuery = GetEntityQuery(); - _storageQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _handsQuery = default!; + [Dependency] private readonly EntityQuery _inventoryQuery = default!; + [Dependency] private readonly EntityQuery _storageQuery = default!; + [Dependency] private readonly EntityQuery _xformQuery = default!; /// /// Equips the data from a `RoleLoadout` onto an entity. diff --git a/Content.Shared/Station/SharedStationSystem.Tracker.cs b/Content.Shared/Station/SharedStationSystem.Tracker.cs index 770420d47e..41129da13f 100644 --- a/Content.Shared/Station/SharedStationSystem.Tracker.cs +++ b/Content.Shared/Station/SharedStationSystem.Tracker.cs @@ -50,7 +50,7 @@ public abstract partial class SharedStationSystem var xform = ent.Comp2; - if (!_xformQuery.Resolve(ent, ref xform)) + if (!Resolve(ent, ref xform)) return; // Entity is in nullspace or not on a grid diff --git a/Content.Shared/Station/SharedStationSystem.cs b/Content.Shared/Station/SharedStationSystem.cs index afd2e77258..13726ac2e1 100644 --- a/Content.Shared/Station/SharedStationSystem.cs +++ b/Content.Shared/Station/SharedStationSystem.cs @@ -11,8 +11,7 @@ public abstract partial class SharedStationSystem : EntitySystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly MetaDataSystem _meta = default!; - private EntityQuery _xformQuery; - private EntityQuery _stationMemberQuery; + [Dependency] private readonly EntityQuery _stationMemberQuery = default!; /// public override void Initialize() @@ -20,9 +19,6 @@ public abstract partial class SharedStationSystem : EntitySystem base.Initialize(); InitializeTracker(); - - _xformQuery = GetEntityQuery(); - _stationMemberQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs index cd86aef943..e04de09d65 100644 --- a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs +++ b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs @@ -1,14 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using Robust.Shared.Random; -using Robust.Shared.Timing; -using Content.Shared.Random.Helpers; namespace Content.Shared.StationRecords; public abstract class SharedStationRecordsSystem : EntitySystem { - [Dependency] protected readonly IGameTiming Timing = default!; - public StationRecordKey? Convert((NetEntity, uint)? input) { return input == null ? null : Convert(input.Value); @@ -103,27 +98,4 @@ public abstract class SharedStationRecordsSystem : EntitySystem return null; } - - /// - /// Gets a random record from the station's record entries. - /// - /// The EntityId of the station from which you want to get the record. - /// The resulting entry. - /// Type to get from the record set. - /// True if a record was obtained. False otherwise. - public bool TryGetRandomRecord(Entity ent, [NotNullWhen(true)] out T? entry) - { - entry = default; - - if (!Resolve(ent.Owner, ref ent.Comp)) - return false; - - if (ent.Comp.Records.Keys.Count == 0) - return false; - - var random = SharedRandomExtensions.PredictedRandom(Timing, GetNetEntity(ent.Owner)); - var key = random.Pick(ent.Comp.Records.Keys); - - return ent.Comp.Records.TryGetRecordEntry(key, out entry); - } } diff --git a/Content.Shared/StatusEffectNew/Components/CloneableStatusEffectComponent.cs b/Content.Shared/StatusEffectNew/Components/CloneableStatusEffectComponent.cs new file mode 100644 index 0000000000..e40a62af25 --- /dev/null +++ b/Content.Shared/StatusEffectNew/Components/CloneableStatusEffectComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.StatusEffectNew.Components; + +/// +/// A simple marker component for a which allows this status effect to be cloned +/// by the CloningSystem (for example for paradox clones, cloning pods or changeling transformations). +/// This is used for traits that use permanent status effects. +/// +[RegisterComponent] +public sealed partial class CloneableStatusEffectComponent : Component; diff --git a/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs b/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs index 1405a5fd62..e5614cd971 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs @@ -10,7 +10,7 @@ public sealed class StatusEffectAlertSystem : EntitySystem { [Dependency] private readonly AlertsSystem _alerts = default!; - private EntityQuery _effectQuery; + [Dependency] private readonly EntityQuery _effectQuery = default!; public override void Initialize() { @@ -19,8 +19,6 @@ public sealed class StatusEffectAlertSystem : EntitySystem SubscribeLocalEvent(OnStatusEffectApplied); SubscribeLocalEvent(OnStatusEffectRemoved); SubscribeLocalEvent(OnEndTimeUpdated); - - _effectQuery = GetEntityQuery(); } private void OnStatusEffectApplied(Entity ent, ref StatusEffectAppliedEvent args) diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs index a65d4fe063..33f9b7f59b 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs @@ -407,4 +407,56 @@ public sealed partial class StatusEffectsSystem return endTime is not null; } + + /// + /// Enumerates through and returns all status effects on an entity + /// + /// Status effect container we're enumerating through + /// All status effects in this container + public IEnumerable> EnumerateStatusEffects( + Entity container) + { + if (!_containerQuery.Resolve(container, ref container.Comp, false) || container.Comp.ActiveStatusEffects == null) + yield break; + + foreach (var effect in container.Comp.ActiveStatusEffects.ContainedEntities) + { + if (_effectQuery.TryComp(effect, out var status)) + yield return (effect, status); + } + } + + /// + /// Enumerates through all status effects on an entity. Returning those with a {T} status effect. + /// + /// Status effect container we're enumerating through + /// Component we're looking for on each status effect + /// All status effects with {T} component in this container + public IEnumerable> EnumerateStatusEffects( + Entity container) where T : Component + { + if (!_containerQuery.Resolve(container, ref container.Comp, false) || container.Comp.ActiveStatusEffects == null) + yield break; + + foreach (var effect in container.Comp.ActiveStatusEffects.ContainedEntities) + { + if (_effectQuery.TryComp(effect, out var status) && TryComp(effect, out var comp)) + yield return (effect, status, comp); + } + } + + /// + public IEnumerable> EnumerateStatusEffects( + Entity container, + EntityQuery query) where T : Component + { + if (!_containerQuery.Resolve(container, ref container.Comp, false) || container.Comp.ActiveStatusEffects == null) + yield break; + + foreach (var effect in container.Comp.ActiveStatusEffects.ContainedEntities) + { + if (_effectQuery.TryComp(effect, out var status) && query.TryComp(effect, out var comp)) + yield return (effect, status, comp); + } + } } diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs index cafe5075c9..cf81f66d9f 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs @@ -1,5 +1,6 @@ using Content.Shared.Body.Events; using Content.Shared.Damage.Events; +using Content.Shared.Damage.Systems; using Content.Shared.Mobs.Events; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; @@ -35,6 +36,7 @@ public sealed partial class StatusEffectsSystem SubscribeLocalEvent(RelayStatusEffectEvent); SubscribeLocalEvent(RefRelayStatusEffectEvent); + SubscribeLocalEvent(RelayStatusEffectEvent); } private void RefRelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, ref T args) where T : struct diff --git a/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs b/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs index 512285eaf3..18dcf20c98 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs @@ -20,8 +20,8 @@ public sealed partial class StatusEffectsSystem : EntitySystem [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly IPrototypeManager _proto = default!; - private EntityQuery _containerQuery; - private EntityQuery _effectQuery; + [Dependency] private readonly EntityQuery _containerQuery = default!; + [Dependency] private readonly EntityQuery _effectQuery = default!; public readonly HashSet StatusEffectPrototypes = []; @@ -40,9 +40,6 @@ public sealed partial class StatusEffectsSystem : EntitySystem SubscribeLocalEvent(OnPrototypesReloaded); - _containerQuery = GetEntityQuery(); - _effectQuery = GetEntityQuery(); - ReloadStatusEffectsCache(); } diff --git a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs index f1d6a9c7dd..c25b37865c 100644 --- a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs +++ b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs @@ -15,6 +15,8 @@ public sealed class StepTriggerSystem : EntitySystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly EntityQuery _physicsquery = default!; + public override void Initialize() { UpdatesOutsidePrediction = true; @@ -37,12 +39,11 @@ public sealed class StepTriggerSystem : EntitySystem public override void Update(float frameTime) { - var query = GetEntityQuery(); var enumerator = EntityQueryEnumerator(); while (enumerator.MoveNext(out var uid, out var active, out var trigger, out var transform)) { - if (!Update(uid, trigger, transform, query)) + if (!Update(uid, trigger, transform)) { continue; } @@ -51,7 +52,7 @@ public sealed class StepTriggerSystem : EntitySystem } } - private bool Update(EntityUid uid, StepTriggerComponent component, TransformComponent transform, EntityQuery query) + private bool Update(EntityUid uid, StepTriggerComponent component, TransformComponent transform) { if (!component.Active || component.Colliding.Count == 0) @@ -78,15 +79,15 @@ public sealed class StepTriggerSystem : EntitySystem foreach (var otherUid in component.Colliding) { - UpdateColliding(uid, component, transform, otherUid, query); + UpdateColliding(uid, component, transform, otherUid); } return false; } - private void UpdateColliding(EntityUid uid, StepTriggerComponent component, TransformComponent ownerXform, EntityUid otherUid, EntityQuery query) + private void UpdateColliding(EntityUid uid, StepTriggerComponent component, TransformComponent ownerXform, EntityUid otherUid) { - if (!query.TryGetComponent(otherUid, out var otherPhysics)) + if (!_physicsquery.TryComp(otherUid, out var otherPhysics)) return; var otherXform = Transform(otherUid); diff --git a/Content.Shared/Sticky/Systems/StickySystem.cs b/Content.Shared/Sticky/Systems/StickySystem.cs index bc04c81f55..f060cfe700 100644 --- a/Content.Shared/Sticky/Systems/StickySystem.cs +++ b/Content.Shared/Sticky/Systems/StickySystem.cs @@ -102,7 +102,7 @@ public sealed class StickySystem : EntitySystem private void OnStickyDoAfter(Entity ent, ref StickyDoAfterEvent args) { - // target is the sticky item when unsticking and the surface when sticking, it will never be null + // target is the surface when sticking/unsticking, it will never be null if (args.Handled || args.Cancelled || args.Args.Target is not {} target) return; @@ -141,7 +141,7 @@ public sealed class StickySystem : EntitySystem } // start unsticking object - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, comp.UnstickDelay, new StickyDoAfterEvent(), uid, target: uid) + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, comp.UnstickDelay, new StickyDoAfterEvent(), uid, target: stuckTo) { BreakOnMove = true, NeedHand = true, diff --git a/Content.Shared/Storage/Components/EntityStorageComponent.cs b/Content.Shared/Storage/Components/EntityStorageComponent.cs index ecfcccc45b..ba267aa5bf 100644 --- a/Content.Shared/Storage/Components/EntityStorageComponent.cs +++ b/Content.Shared/Storage/Components/EntityStorageComponent.cs @@ -131,7 +131,21 @@ public sealed partial class EntityStorageComponent : Component, IGasMixtureHolde /// standard requirement that the entity must be an item or mob is waived. /// [DataField] - public EntityWhitelist? Whitelist; + public EntityWhitelist? Whitelist = new() + { + Components = + [ + "MobState", + "Item", + ], + }; + + /// + /// Blacklist for what entities are not allowed to be inserted into this container. + /// Blacklist takes priority over whitelist. + /// + [DataField] + public EntityWhitelist? Blacklist; /// /// The contents of the storage. diff --git a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs index 0d744a4fe9..dea4f79656 100644 --- a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs @@ -20,12 +20,12 @@ public sealed class DumpableSystem : EntitySystem [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - private EntityQuery _itemQuery; + [Dependency] private readonly EntityQuery _itemQuery = default!; public override void Initialize() { base.Initialize(); - _itemQuery = GetEntityQuery(); + SubscribeLocalEvent(OnAfterInteract, after: new[]{ typeof(SharedEntityStorageSystem) }); SubscribeLocalEvent>(AddDumpVerb); SubscribeLocalEvent>(AddUtilityVerbs); diff --git a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs index 27a15c87a6..6d33fac51f 100644 --- a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs +++ b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs @@ -18,15 +18,14 @@ public sealed class MagnetPickupSystem : EntitySystem [Dependency] private readonly SharedStorageSystem _storage = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; private static readonly TimeSpan ScanDelay = TimeSpan.FromSeconds(1); - private EntityQuery _physicsQuery; public override void Initialize() { base.Initialize(); - _physicsQuery = GetEntityQuery(); SubscribeLocalEvent(OnMagnetMapInit); } diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index 93a534843a..9618ee900b 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -377,12 +377,8 @@ public abstract class SharedEntityStorageSystem : EntitySystem if (containerAttemptEvent.Cancelled) return false; - // Consult the whitelist. The whitelist ignores the default assumption about how entity storage works. - if (component.Whitelist != null) - return _whitelistSystem.IsValid(component.Whitelist, toInsert); - - // The inserted entity must be a mob or an item. - return HasComp(toInsert) || HasComp(toInsert); + // Check the whitelist/blacklist. + return _whitelistSystem.CheckBoth(toInsert, component.Blacklist, component.Whitelist); } public bool TryOpenStorage(EntityUid user, EntityUid target, bool silent = false) diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 6ede24e8b7..5f5eaf0d1c 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -73,10 +73,10 @@ public abstract class SharedStorageSystem : EntitySystem [Dependency] private readonly TagSystem _tag = default!; [Dependency] protected readonly UseDelaySystem UseDelay = default!; - private EntityQuery _itemQuery; - private EntityQuery _stackQuery; - private EntityQuery _xformQuery; - private EntityQuery _userQuery; + [Dependency] private readonly EntityQuery _itemQuery = default!; + [Dependency] private readonly EntityQuery _stackQuery = default!; + [Dependency] private readonly EntityQuery _xformQuery = default!; + [Dependency] private readonly EntityQuery _userQuery = default!; /// /// Whether we're allowed to go up-down storage via UI. @@ -124,10 +124,6 @@ public abstract class SharedStorageSystem : EntitySystem { base.Initialize(); - _itemQuery = GetEntityQuery(); - _stackQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - _userQuery = GetEntityQuery(); _prototype.PrototypesReloaded += OnPrototypesReloaded; Subs.CVar(_cfg, CCVars.StorageLimit, OnStorageLimitChanged, true); diff --git a/Content.Server/Store/Components/CurrencyComponent.cs b/Content.Shared/Store/Components/CurrencyComponent.cs similarity index 95% rename from Content.Server/Store/Components/CurrencyComponent.cs rename to Content.Shared/Store/Components/CurrencyComponent.cs index eb4ca1c0e3..a4c5b3c027 100644 --- a/Content.Server/Store/Components/CurrencyComponent.cs +++ b/Content.Shared/Store/Components/CurrencyComponent.cs @@ -1,8 +1,7 @@ using Content.Shared.FixedPoint; -using Content.Shared.Store; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -namespace Content.Server.Store.Components; +namespace Content.Shared.Store.Components; /// /// Identifies a component that can be inserted into a store diff --git a/Content.Shared/Store/Components/RemoteStoreComponent.cs b/Content.Shared/Store/Components/RemoteStoreComponent.cs new file mode 100644 index 0000000000..3b07e03e09 --- /dev/null +++ b/Content.Shared/Store/Components/RemoteStoreComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Store.Components; + +/// +/// This component manages a store which players can use to purchase different listings +/// through the ui. The currency, listings, and categories are defined in yaml. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class RemoteStoreComponent : Component +{ + /// + /// The store which is currently being targetted for remote opening + /// + [DataField] + public EntityUid? Store; +} diff --git a/Content.Shared/Store/ListingPrototype.cs b/Content.Shared/Store/ListingPrototype.cs index d2e56073c2..cb33904866 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -32,6 +32,7 @@ public partial class ListingData : IEquatable other.Icon, other.Priority, other.ProductEntity, + other.ProductComponents, other.ProductAction, other.ProductUpgradeId, other.ProductActionEntity, @@ -59,6 +60,7 @@ public partial class ListingData : IEquatable SpriteSpecifier? icon, int priority, EntProtoId? productEntity, + EntProtoId? productComponents, EntProtoId? productAction, ProtoId? productUpgradeId, EntityUid? productActionEntity, @@ -82,6 +84,7 @@ public partial class ListingData : IEquatable Icon = icon; Priority = priority; ProductEntity = productEntity; + ProductComponents = productComponents; ProductAction = productAction; ProductUpgradeId = productUpgradeId; ProductActionEntity = productActionEntity; @@ -153,6 +156,15 @@ public partial class ListingData : IEquatable [DataField] public int Priority; + /// + /// A dummy entity containing the components to be added to the buyer if the listing is bought. + /// + /// + /// We use an EntProtoId rather than a ComponentRegistry to keep ListingData equatable. + /// + [DataField] + public EntProtoId? ProductComponents; + /// /// The entity that is given when the listing is purchased. /// @@ -240,6 +252,7 @@ public partial class ListingData : IEquatable Name != listing.Name || Description != listing.Description || ProductEntity != listing.ProductEntity || + ProductComponents != listing.ProductComponents || ProductAction != listing.ProductAction || ProductEvent?.GetType() != listing.ProductEvent?.GetType() || RestockTime != listing.RestockTime || @@ -380,6 +393,7 @@ public sealed partial class ListingDataWithCostModifiers : ListingData listingData.Icon, listingData.Priority, listingData.ProductEntity, + listingData.ProductComponents, listingData.ProductAction, listingData.ProductUpgradeId, listingData.ProductActionEntity, diff --git a/Content.Server/Store/Systems/StoreSystem.Listings.cs b/Content.Shared/Store/SharedStoreSystem.Listings.cs similarity index 93% rename from Content.Server/Store/Systems/StoreSystem.Listings.cs rename to Content.Shared/Store/SharedStoreSystem.Listings.cs index 412114ce03..fa0f29bb4c 100644 --- a/Content.Server/Store/Systems/StoreSystem.Listings.cs +++ b/Content.Shared/Store/SharedStoreSystem.Listings.cs @@ -1,12 +1,12 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Content.Shared.Mind; -using Content.Shared.Store; using Content.Shared.Store.Components; using Robust.Shared.Prototypes; -namespace Content.Server.Store.Systems; +namespace Content.Shared.Store; -public sealed partial class StoreSystem + +public abstract partial class SharedStoreSystem { /// /// Refreshes all listings on a store. @@ -46,7 +46,7 @@ public sealed partial class StoreSystem public HashSet GetAllListings() { var clones = new HashSet(); - foreach (var prototype in _proto.EnumeratePrototypes()) + foreach (var prototype in Proto.EnumeratePrototypes()) { clones.Add(new ListingDataWithCostModifiers(prototype)); } @@ -62,7 +62,7 @@ public sealed partial class StoreSystem /// Whether or not the listing was added successfully public bool TryAddListing(StoreComponent component, string listingId) { - if (!_proto.TryIndex(listingId, out var proto)) + if (!Proto.TryIndex(listingId, out var proto)) { Log.Error("Attempted to add invalid listing."); return false; @@ -145,7 +145,7 @@ public sealed partial class StoreSystem /// The buying entity. public EntityUid GetBuyerMind(EntityUid buyer) { - if (!HasComp(buyer) && _mind.TryGetMind(buyer, out var buyerMind, out var _)) + if (!HasComp(buyer) && Mind.TryGetMind(buyer, out var buyerMind, out _)) return buyerMind; return buyer; diff --git a/Content.Shared/Store/SharedStoreSystem.UI.cs b/Content.Shared/Store/SharedStoreSystem.UI.cs new file mode 100644 index 0000000000..2d4b166c27 --- /dev/null +++ b/Content.Shared/Store/SharedStoreSystem.UI.cs @@ -0,0 +1,105 @@ +using System.Linq; +using Content.Shared.FixedPoint; +using Content.Shared.PDA.Ringer; +using Content.Shared.Store.Components; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Store; + +/// +/// This handles... +/// +public abstract partial class SharedStoreSystem +{ + /// + /// Toggles the store Ui open and closed + /// + /// the person doing the toggling + /// the store being toggled + /// + /// The entity remotely accessing the store, if any. + /// The remote access component, if any. + public void ToggleUi(EntityUid user, EntityUid storeEnt, StoreComponent? component = null, EntityUid? remoteAccess = null, RemoteStoreComponent? remoteComponent = null) + { + if (!Resolve(storeEnt, ref component)) + return; + + if (remoteAccess != null && !Resolve(remoteAccess.Value, ref remoteComponent) && remoteComponent!.Store != storeEnt) + return; + + if (!TryComp(user, out var actor)) + return; + + if (!UI.TryToggleUi(remoteAccess != null ? remoteAccess.Value : storeEnt, StoreUiKey.Key, actor.PlayerSession)) + return; + + UpdateUserInterface(user, storeEnt, component); + } + + /// + /// Closes the store UI for everyone, if it's open + /// + public void CloseUi(EntityUid uid, StoreComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + UI.CloseUi(uid, StoreUiKey.Key); + } + + /// + /// Updates the user interface for a store and refreshes the listings + /// + /// The person who if opening the store ui. Listings are filtered based on this. + /// The store entity itself + /// The store component being refreshed. + public void UpdateUserInterface(EntityUid? user, EntityUid store, StoreComponent? component = null) + { + if (!Resolve(store, ref component)) + return; + + //this is the person who will be passed into logic for all listing filtering. + if (user != null) //if we have no "buyer" for this update, then don't update the listings + { + component.LastAvailableListings = GetAvailableListings(component.AccountOwner ?? user.Value, store, component).ToHashSet(); + } + + //dictionary for all currencies, including 0 values for currencies on the whitelist + Dictionary, FixedPoint2> allCurrency = new(); + foreach (var supported in component.CurrencyWhitelist) + { + allCurrency.Add(supported, FixedPoint2.Zero); + + if (component.Balance.TryGetValue(supported, out var value)) + allCurrency[supported] = value; + } + + // TODO: if multiple users are supposed to be able to interact with a single BUI & see different + // stores/listings, this needs to use session specific BUI states. + + // only tell operatives to lock their uplink if it can be locked + var showFooter = HasComp(store); + + var state = new StoreUpdateState(component.LastAvailableListings, allCurrency, showFooter, component.RefundAllowed); + UpdateRemoteStores(store, state); + UI.SetUiState(store, StoreUiKey.Key, state); + } + + /// + /// Updates any remote store connections to a specific store. + /// + /// The store being updated. + /// The state being applied. + public void UpdateRemoteStores(EntityUid store, StoreUpdateState state) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var remote, out var ui)) + { + if (remote.Store != store) + continue; + + UI.SetUiState((uid, ui), StoreUiKey.Key, state); + } + } +} diff --git a/Content.Shared/Store/SharedStoreSystem.cs b/Content.Shared/Store/SharedStoreSystem.cs new file mode 100644 index 0000000000..315d70d52a --- /dev/null +++ b/Content.Shared/Store/SharedStoreSystem.cs @@ -0,0 +1,254 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Shared.FixedPoint; +using Content.Shared.Implants; +using Content.Shared.Interaction; +using Content.Shared.Mind; +using Content.Shared.Popups; +using Content.Shared.Stacks; +using Content.Shared.Store.Components; +using Content.Shared.Store.Events; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Store; + +/// +/// Manages general interactions with a store and different entities, +/// getting listings for stores, and interfacing with the store UI. +/// +public abstract partial class SharedStoreSystem : EntitySystem +{ + [Dependency] protected readonly IPrototypeManager Proto = default!; + [Dependency] protected readonly SharedMindSystem Mind = default!; + [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] protected readonly SharedStackSystem Stack = default!; + [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; + + [Dependency] protected readonly EntityQuery StoreQuery = default!; + [Dependency] protected readonly EntityQuery RemoteStoreQuery = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnGetStore); + SubscribeLocalEvent>((x, ref y) => + { + var ev = y.Event; + OnGetStore(x, ref ev); + y.Event = ev; + }); + SubscribeLocalEvent>(OnImplantInsertAttempt); + SubscribeLocalEvent(OnIntrinsicStoreAction); + } + + private void OnGetStore(Entity entity, ref GetStoreEvent args) + { + if (args.Handled) + return; + + if (!StoreQuery.TryComp(entity.Comp.Store, out var store)) + return; + + args.Store = (entity.Comp.Store.Value, store); + } + + private void OnImplantInsertAttempt(Entity implant, ref ImplantRelayEvent args) + { + var ev = args.Event; + + // Only allow insertion if the person implanted is doing the action. + if (ev.User == ev.Target) + ev.TargetOverride = implant; + else + ev.Cancel(); + + args.Event = ev; + } + + private void OnAfterInteract(EntityUid uid, CurrencyComponent component, AfterInteractEvent args) + { + if (args.Handled || !args.CanReach || args.Target is not { } target) + return; + + if (!TryGetStore(target, out var store)) + return; + + var ev = new CurrencyInsertAttemptEvent(args.User, target, args.Used, store.Value.Comp); + RaiseLocalEvent(target, ev); + if (ev.Cancelled) + return; + + if (!TryAddCurrency((uid, component), (store.Value, store.Value.Comp))) + return; + + args.Handled = true; + var msg = Loc.GetString("store-currency-inserted", ("used", args.Used), ("target", ev.TargetOverride ?? target)); + Popup.PopupEntity(msg, target, args.User); + } + + /// + /// Attempts to find a store connected to this entity. + /// First checking for a on this entity, + /// then checking for a to find a remotely connected store. + /// + /// Entity we're checking for an attached store on + /// Store entity we're returning. + /// True if a store was found. + public bool TryGetStore(EntityUid entity, [NotNullWhen(true)] out Entity? store) + { + store = GetStore(entity); + return store != null; + } + + /// + /// Attempts to find a store connected to this entity. + /// First checking for a on this entity, + /// then checking for a to find a remotely connected store. + /// + /// Entity we're checking for an attached store on + /// The store entity and component if found. + public Entity? GetStore(EntityUid entity) + { + if (StoreQuery.TryComp(entity, out var storeComp)) + return (entity, storeComp); + + var ev = new GetStoreEvent(); + RaiseLocalEvent(entity, ref ev); + return ev.Store; + } + + /// + /// Attempts to find a remote store connected to this entity. + /// Checking for a with an attached store entity. + /// + /// Entity we're checking for an attached store on + /// The store entity and component if found. + public Entity? GetRemoteStore(Entity entity) + { + if (RemoteStoreQuery.Resolve(entity, ref entity.Comp) + && entity.Comp.Store != null + && StoreQuery.TryComp(entity.Comp.Store, out var storeComp)) + return (entity.Comp.Store.Value, storeComp); + + return null; + } + + public void SetRemoteStore(Entity entity, EntityUid? store) + { + if (!RemoteStoreQuery.Resolve(entity, ref entity.Comp)) + return; + + entity.Comp.Store = store; + } + + /// + /// Gets the value from an entity's currency component. + /// Scales with stacks. + /// + /// + /// If this result is intended to be used with , + /// consider using instead to ensure that the currency is consumed in the process. + /// + /// + /// + /// The value of the currency + public Dictionary GetCurrencyValue(EntityUid uid, CurrencyComponent component) + { + var amount = EntityManager.GetComponentOrNull(uid)?.Count ?? 1; + return component.Price.ToDictionary(v => v.Key, p => p.Value * amount); + } + + /// + /// Tries to add a currency to a store's balance. Note that if successful, this will consume the currency in the process. + /// + public bool TryAddCurrency(Entity currency, Entity store) + { + if (!Resolve(currency.Owner, ref currency.Comp)) + return false; + + if (!Resolve(store.Owner, ref store.Comp)) + return false; + + var value = currency.Comp.Price; + if (TryComp(currency.Owner, out StackComponent? stack) && stack.Count != 1) + { + value = currency.Comp.Price + .ToDictionary(v => v.Key, p => p.Value * stack.Count); + } + + if (!TryAddCurrency(value, store, store.Comp)) + return false; + + // Avoid having the currency accidentally be re-used. E.g., if multiple clients try to use the currency in the + // same tick + currency.Comp.Price.Clear(); + if (stack != null) + Stack.SetCount((currency.Owner, stack), 0); + + QueueDel(currency); + return true; + } + + /// + /// Tries to add a currency to a store's balance + /// + /// The value to add to the store + /// + /// The store to add it to + /// Whether or not the currency was succesfully added + public bool TryAddCurrency(Dictionary currency, EntityUid uid, StoreComponent? store = null) + { + if (!Resolve(uid, ref store)) + return false; + + //verify these before values are modified + foreach (var type in currency) + { + if (!store.CurrencyWhitelist.Contains(type.Key)) + return false; + } + + foreach (var type in currency) + { + if (!store.Balance.TryAdd(type.Key, type.Value)) + store.Balance[type.Key] += type.Value; + } + + UpdateUserInterface(null, uid, store); + return true; + } + + private void OnIntrinsicStoreAction(Entity ent, ref IntrinsicStoreActionEvent args) + { + ToggleUi(args.Performer, ent.Owner, ent.Comp); + } +} + +[ByRefEvent] +public record struct GetStoreEvent +{ + public readonly bool Handled => Store != null; + public Entity? Store; +} + +public sealed class CurrencyInsertAttemptEvent : CancellableEntityEventArgs +{ + public readonly EntityUid User; + public readonly EntityUid Target; + public readonly EntityUid Used; + public readonly StoreComponent Store; + + // An optional override for the "Target" of this interaction, used to change the name that pops up! + public EntityUid? TargetOverride; + + public CurrencyInsertAttemptEvent(EntityUid user, EntityUid target, EntityUid used, StoreComponent store) + { + User = user; + Target = target; + Used = used; + Store = store; + } +} + diff --git a/Content.Shared/Store/StoreUi.cs b/Content.Shared/Store/StoreUi.cs index 05cdd0923d..1779efe200 100644 --- a/Content.Shared/Store/StoreUi.cs +++ b/Content.Shared/Store/StoreUi.cs @@ -38,9 +38,10 @@ public sealed class StoreRequestUpdateInterfaceMessage : BoundUserInterfaceMessa } [Serializable, NetSerializable] -public sealed class StoreBuyListingMessage(ProtoId listing) : BoundUserInterfaceMessage +public sealed class StoreBuyListingMessage(ProtoId listing, NetEntity? soundSource) : BoundUserInterfaceMessage { public ProtoId Listing = listing; + public NetEntity? SoundSource = soundSource; } [Serializable, NetSerializable] diff --git a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs index d707879d83..57f98ae909 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs @@ -29,8 +29,6 @@ namespace Content.Shared.Stunnable; /// public abstract partial class SharedStunSystem { - private EntityQuery _crawlerQuery; - [Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; @@ -38,12 +36,13 @@ public abstract partial class SharedStunSystem [Dependency] private readonly StandingStateSystem _standingState = default!; [Dependency] private readonly IConfigurationManager _cfgManager = default!; + [Dependency] private readonly EntityQuery _crawlerQuery = default!; + [Dependency] private readonly EntityQuery _fixtureQuery = default!; + public static readonly ProtoId KnockdownAlert = "Knockdown"; private void InitializeKnockdown() { - _crawlerQuery = GetEntityQuery(); - SubscribeLocalEvent(OnRejuvenate); // Startup and Shutdown @@ -465,17 +464,14 @@ public abstract partial class SharedStunSystem if (intersecting.Count == 0) return false; - var fixtureQuery = GetEntityQuery(); - var xformQuery = GetEntityQuery(); - var ourAABB = _entityLookup.GetAABBNoContainer(entity, entity.Comp.LocalPosition, entity.Comp.LocalRotation); foreach (var ent in intersecting) { - if (!fixtureQuery.TryGetComponent(ent, out var fixtures)) + if (!_fixtureQuery.TryGetComponent(ent, out var fixtures)) continue; - if (!xformQuery.TryComp(ent, out var xformComp)) + if (!TryComp(ent, out TransformComponent? xformComp)) continue; var xform = new Transform(xformComp.LocalPosition, xformComp.LocalRotation); diff --git a/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs b/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs index 4d49621a8a..3ab1411428 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs @@ -10,6 +10,12 @@ public abstract partial class SharedStunSystem { SubscribeLocalEvent(OnStunMobStateChanged); SubscribeLocalEvent(OnSleepStateChanged); + SubscribeLocalEvent(OnStunned); + } + + private void OnStunned(Entity ent, ref StunnedEvent args) + { + TrySeeingStars(ent.Owner); } private bool GetStarsData(Entity entity) diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index d064434eac..2a3d96b9c7 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -392,14 +392,14 @@ public abstract partial class SharedStunSystem : EntitySystem private void OnEquipAttempt(EntityUid uid, StunnedComponent stunned, IsEquippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Equipee == uid) + if (args.User == uid) args.Cancel(); } private void OnUnequipAttempt(EntityUid uid, StunnedComponent stunned, IsUnequippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Unequipee == uid) + if (args.User == uid) args.Cancel(); } diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs index a4fa463ee0..a66060bf4c 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -26,14 +26,12 @@ namespace Content.Shared.SubFloor [Dependency] private readonly SharedVisibilitySystem _visibility = default!; [Dependency] protected readonly SharedPopupSystem _popup = default!; - private EntityQuery _hideQuery; + [Dependency] private readonly EntityQuery _hideQuery = default!; public override void Initialize() { base.Initialize(); - _hideQuery = GetEntityQuery(); - SubscribeLocalEvent(OnTileChanged); SubscribeLocalEvent(OnSubFloorStarted); SubscribeLocalEvent(OnSubFloorTerminating); diff --git a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs index 9a7c829f14..03e5e517d6 100644 --- a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs +++ b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs @@ -80,12 +80,12 @@ public abstract class SharedTrayScannerSystem : EntitySystem private void OnTrayUnequipped(Entity ent, ref GotUnequippedEvent args) { - OnUnequip(args.Equipee); + OnUnequip(args.EquipTarget); } private void OnTrayEquipped(Entity ent, ref GotEquippedEvent args) { - OnEquip(args.Equipee); + OnEquip(args.EquipTarget); } private void OnTrayScannerActivate(EntityUid uid, TrayScannerComponent scanner, ActivateInWorldEvent args) diff --git a/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideColliderComponent.cs b/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideColliderComponent.cs new file mode 100644 index 0000000000..f92f395eb9 --- /dev/null +++ b/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideColliderComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.SurveillanceCamera.Components; + +/// +/// Can activate when collided with. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class CameraActiveOnCollideColliderComponent : Component +{ + /// + /// The fixture id used for detecting the collision. + /// + [DataField] + public string FixtureId = "lightTrigger"; +} diff --git a/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideComponent.cs b/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideComponent.cs new file mode 100644 index 0000000000..27c4bf7c09 --- /dev/null +++ b/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.SurveillanceCamera.Components; + +/// +/// Marks an entity with whenever entities are contacting with it. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class CameraActiveOnCollideComponent : Component +{ + /// + /// Whether this camera is currently being collided with. + /// + [DataField, AutoNetworkedField] + public bool Enabled; + + /// + /// Whether the entity must be powered for this component to work. + /// + [DataField, AutoNetworkedField] + public bool RequiresPower = true; +} diff --git a/Content.Shared/SurveillanceCamera/Components/SurveillanceCameraComponent.cs b/Content.Shared/SurveillanceCamera/Components/SurveillanceCameraComponent.cs index 21381e753f..914ba4a63f 100644 --- a/Content.Shared/SurveillanceCamera/Components/SurveillanceCameraComponent.cs +++ b/Content.Shared/SurveillanceCamera/Components/SurveillanceCameraComponent.cs @@ -8,18 +8,23 @@ namespace Content.Shared.SurveillanceCamera.Components; [Access(typeof(SharedSurveillanceCameraSystem))] public sealed partial class SurveillanceCameraComponent : Component { - // List of active viewers. This is for bookkeeping purposes, - // so that when a camera shuts down, any entity viewing it - // will immediately have their subscription revoked. + /// + /// List of active viewers who have a PVS view subscription on this camera. + /// This is for bookkeeping purposes, + /// so that when a camera shuts down, any entity viewing it + /// will immediately have their subscription revoked. + /// [ViewVariables] - public HashSet ActiveViewers { get; } = new(); + public HashSet ActivePvsViewers { get; } = new(); - // Monitors != Viewers, as viewers are entities that are tied - // to a player session that's viewing from this camera - // - // Monitors are grouped sets of viewers, and may be - // completely different monitor types (e.g., monitor console, - // AI, etc.) + /// + /// Monitors != Viewers, as viewers are entities that are tied + /// to a player session that's viewing from this camera + /// + /// Monitors are grouped sets of viewers, and may be + /// completely different monitor types (e.g., monitor console, + /// AI, etc.) + /// [ViewVariables] public HashSet ActiveMonitors { get; } = new(); diff --git a/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs b/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs index b6743669e9..1be49f8fc8 100644 --- a/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs +++ b/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs @@ -5,7 +5,7 @@ using Robust.Shared.Serialization; namespace Content.Shared.SurveillanceCamera; -public abstract class SharedSurveillanceCameraSystem : EntitySystem +public abstract partial class SharedSurveillanceCameraSystem : EntitySystem { public override void Initialize() { @@ -68,3 +68,11 @@ public enum SurveillanceCameraVisuals : byte Xray, Emp } + +/// +/// Raised on a camera entity to find whether it is externally viewed by some entity. +/// This does not use the actual viewers or monitors camera has and is simply used to see whether the camera is "technically" +/// being looked through by somebody, such as the Station AI. +/// +[ByRefEvent] +public record struct SurveillanceCameraGetIsViewedExternallyEvent(bool Viewed = false); diff --git a/Content.Shared/Tag/TagSystem.cs b/Content.Shared/Tag/TagSystem.cs index b75e2a4af1..fd74c10a59 100644 --- a/Content.Shared/Tag/TagSystem.cs +++ b/Content.Shared/Tag/TagSystem.cs @@ -18,14 +18,12 @@ public sealed class TagSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _proto = default!; - private EntityQuery _tagQuery; + [Dependency] private readonly EntityQuery _tagQuery = default!; public override void Initialize() { base.Initialize(); - _tagQuery = GetEntityQuery(); - #if DEBUG SubscribeLocalEvent(OnTagInit); #endif diff --git a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs index 69805fd585..8620cca292 100644 --- a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs +++ b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs @@ -29,8 +29,6 @@ public sealed class SwapTeleporterSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - private EntityQuery _xformQuery; - /// public override void Initialize() { @@ -40,8 +38,6 @@ public sealed class SwapTeleporterSystem : EntitySystem SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnShutdown); - - _xformQuery = GetEntityQuery(); } private void OnInteract(Entity ent, ref AfterInteractEvent args) @@ -224,7 +220,7 @@ public sealed class SwapTeleporterSystem : EntitySystem if (HasComp(parent) || HasComp(parent)) return ent; - if (!_xformQuery.TryGetComponent(parent, out var parentXform) || parentXform.Anchored) + if (!TryComp(parent, out TransformComponent? parentXform) || parentXform.Anchored) return ent; if (!TryComp(parent, out var body) || body.BodyType == BodyType.Static) diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainer.cs b/Content.Shared/Temperature/HeatContainer/HeatContainer.cs index c88d11b15a..cfa5a43c59 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainer.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainer.cs @@ -6,40 +6,24 @@ namespace Content.Shared.Temperature.HeatContainer; /// /// A general-purpose container for heat energy. -/// Any object that contains, stores, or transfers heat should use a -/// instead of implementing its own system. -/// This allows for consistent heat transfer mechanics across different objects and systems. /// [Serializable, NetSerializable, DataDefinition] [Access(typeof(HeatContainerHelpers), typeof(SharedAtmosphereSystem))] -public partial struct HeatContainer : IRobustCloneable +public partial struct HeatContainer : IRobustCloneable, IHeatContainer { - /// - /// The heat capacity of this container in Joules per Kelvin. - /// This determines how much energy is required to change the temperature of the container. - /// Higher values mean the container can absorb or release more heat energy - /// without a significant change in temperature. - /// + /// [DataField] - public float HeatCapacity = 4000f; // about 1kg of water + public float HeatCapacity { get; set; } = 4000f; // about 1kg of water - /// - /// The current temperature of the container in Kelvin. - /// + /// [DataField] - public float Temperature = Atmospherics.T20C; // room temperature + public float Temperature { get; set; } = Atmospherics.T20C; // room temperature - /// - /// The current temperature of the container in Celsius. - /// Ideal if you just need to read the temperature for UI. - /// Do not perform computations in Celsius/set this value, use Kelvin instead. - /// + /// [ViewVariables] public float TemperatureC => TemperatureHelpers.KelvinToCelsius(Temperature); - /// - /// The current thermal energy of the container in Joules. - /// + /// [ViewVariables] public float InternalEnergy => Temperature * HeatCapacity; diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs index c0de657fa9..0bbd9698aa 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs @@ -5,10 +5,10 @@ namespace Content.Shared.Temperature.HeatContainer; public static partial class HeatContainerHelpers { /// - /// Conducts heat between a and some body with a different temperature, + /// Conducts heat between a and some body with a different temperature, /// given some constant thermal conductance g and a small time delta. /// - /// The to conduct heat to. + /// The that should conduct heat. /// The temperature of the second object that we are conducting heat with, in kelvin. /// /// The amount of time that the heat is allowed to conduct, in seconds. @@ -26,19 +26,19 @@ public static partial class HeatContainerHelpers /// integration steps with adaptive step size. /// [PublicAPI] - public static float ConductHeat(this ref HeatContainer c, float temp, float deltaTime, float g) + public static float ConductHeat(ref T c, float temp, float deltaTime, float g) where T : IHeatContainer { - var dQ = c.ConductHeatQuery(temp, deltaTime, g); - c.AddHeat(dQ); + var dQ = ConductHeatQuery(ref c, temp, deltaTime, g); + AddHeat(ref c, dQ); return dQ; } /// - /// Conducts heat between two s, + /// Conducts heat between two s, /// given some constant thermal conductance g and a small time delta. /// - /// The first to conduct heat to. - /// The second to conduct heat to. + /// The first that should conduct heat. + /// The second that should conduct heat. /// /// The amount of time that the heat is allowed to conduct, in seconds. /// This value should be small such that deltaTime << C / g where C is the heat capacity of the containers. @@ -55,19 +55,21 @@ public static partial class HeatContainerHelpers /// integration steps with adaptive step size. /// [PublicAPI] - public static float ConductHeat(this ref HeatContainer cA, ref HeatContainer cB, float deltaTime, float g) + public static float ConductHeat(ref T1 cA, ref T2 cB, float deltaTime, float g) + where T1 : IHeatContainer + where T2 : IHeatContainer { - var dQ = ConductHeatQuery(ref cA, cB.Temperature, deltaTime, g); - cA.AddHeat(dQ); - cB.AddHeat(-dQ); + var dQ = ConductHeatQuery(ref cA, ref cB, deltaTime, g); + AddHeat(ref cA, dQ); + AddHeat(ref cB, -dQ); return dQ; } /// - /// Calculates the amount of heat that would be conducted between a and some body with a different temperature, + /// Calculates the amount of heat that would be conducted between a and some body with a different temperature, /// given some constant thermal conductance g and a small time delta. /// - /// The to conduct heat to. + /// The that should conduct heat. /// The temperature of the second object that we are conducting heat with, in kelvin. /// /// The amount of time that the heat is allowed to conduct, in seconds. @@ -85,7 +87,7 @@ public static partial class HeatContainerHelpers /// integration steps with adaptive step size. /// [PublicAPI] - public static float ConductHeatQuery(this ref HeatContainer c, float temp, float deltaTime, float g) + public static float ConductHeatQuery(ref T c, float temp, float deltaTime, float g) where T : IHeatContainer { var dQ = g * (temp - c.Temperature) * deltaTime; var dQMax = Math.Abs(ConductHeatToTempQuery(ref c, temp)); @@ -95,11 +97,11 @@ public static partial class HeatContainerHelpers } /// - /// Calculates the amount of heat that would be conducted between two s, + /// Calculates the amount of heat that would be conducted between two s, /// given some conductivity constant k and a time delta. Does not modify the containers. /// - /// The first to conduct heat to. - /// The second to conduct heat to. + /// The first that should conduct heat. + /// The second that should conduct heat. /// /// The amount of time that the heat is allowed to conduct, in seconds. /// This value should be small such that deltaTime << C / g where C is the heat capacity of the container. @@ -116,22 +118,28 @@ public static partial class HeatContainerHelpers /// integration steps with adaptive step size. /// [PublicAPI] - public static float ConductHeatQuery(this ref HeatContainer c1, ref HeatContainer c2, float deltaTime, float g) + public static float ConductHeatQuery(ref T1 c1, ref T2 c2, float deltaTime, float g) + where T1 : IHeatContainer + where T2 : IHeatContainer { - return ConductHeatQuery(ref c1, c2.Temperature, deltaTime, g); + var dQ = g * (c2.Temperature - c1.Temperature) * deltaTime; + var dQMax = Math.Abs(EquilibriumHeatQuery(ref c1, ref c2)); + + // Clamp the transferred heat amount in case we are overshooting the equilibrium temperature because our time step was too large. + return Math.Clamp(dQ, -dQMax, dQMax); } /// - /// Changes the temperature of a to a target temperature by + /// Changes the temperature of a to a target temperature by /// adding or removing the necessary amount of heat. /// - /// The to change the temperature of. + /// The to change the temperature of. /// The desired temperature to reach. - /// The amount of heat in joules that was transferred to or from the + /// The amount of heat in joules that was transferred to or from the /// to reach the target temperature. /// A positive value indicates heat must be added to the container to reach the target temperature. [PublicAPI] - public static float ConductHeatToTemp(this ref HeatContainer c, float targetTemp) + public static float ConductHeatToTemp(ref T c, float targetTemp) where T : IHeatContainer { var dQ = ConductHeatToTempQuery(ref c, targetTemp); c.Temperature = targetTemp; @@ -139,16 +147,16 @@ public static partial class HeatContainerHelpers } /// - /// Determines the amount of heat that must be transferred to or from a + /// Determines the amount of heat that must be transferred to or from a /// to reach a target temperature. Does not modify the heat container. /// - /// The to query. + /// The to query. /// The desired temperature to reach. - /// The amount of heat in joules that must be transferred to or from the + /// The amount of heat in joules that must be transferred to or from the /// to reach the target temperature. /// A positive value indicates heat must be added to the container to reach the target temperature. [PublicAPI] - public static float ConductHeatToTempQuery(this ref HeatContainer c, float targetTemp) + public static float ConductHeatToTempQuery(ref T c, float targetTemp) where T : IHeatContainer { return (targetTemp - c.Temperature) * c.HeatCapacity; } diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs index e745c59305..0e516446da 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs @@ -5,50 +5,66 @@ namespace Content.Shared.Temperature.HeatContainer; public static partial class HeatContainerHelpers { /// - /// Splits a into two. + /// Splits a into two. /// - /// The to split. This will be modified to contain the remaining heat capacity. + /// The to split. This will be modified to contain the remaining heat capacity. + /// A that will be modified to contain + /// the specified fraction of the original container's heat capacity and the same temperature. /// The fraction of the heat capacity to move to the new container. Clamped between 0 and 1. - /// A new containing the specified fraction of the original container's heat capacity and the same temperature. [PublicAPI] - public static HeatContainer Split(this ref HeatContainer c, float fraction = 0.5f) + public static void Split(ref T1 c, ref T2 cSplit, float fraction = 0.5f) + where T1 : IHeatContainer + where T2 : IHeatContainer { fraction = Math.Clamp(fraction, 0f, 1f); var newHeatCapacity = c.HeatCapacity * fraction; - var newContainer = new HeatContainer - { - HeatCapacity = newHeatCapacity, - Temperature = c.Temperature, - }; + cSplit.HeatCapacity = newHeatCapacity; + cSplit.Temperature = c.Temperature; c.HeatCapacity -= newHeatCapacity; - - return newContainer; } /// - /// Divides a source into a specified number of equal parts. + /// Splits a into two, + /// modifying the original container to contain the specified fraction of the original heat capacity and the same temperature. /// - /// The input to split. - /// The number of s - /// to split the source into. - /// Thrown when attempting to divide the source container by zero. - /// An array of s equally split from the source . + /// A that will be modified to contain + /// the specified fraction of the original container's heat capacity and the same temperature. + /// The fraction of the heat capacity to move to the new container. Clamped between 0 and 1. + /// This discards the leftover fraction. Be very careful with using this as you may void heat unintentionally. [PublicAPI] - public static HeatContainer[] Divide(this HeatContainer c, uint num) + public static void Split(ref T c, float fraction = 0.5f) + where T : IHeatContainer { - ArgumentOutOfRangeException.ThrowIfZero(num); + fraction = Math.Clamp(fraction, 0f, 1f); + var newHeatCapacity = c.HeatCapacity * fraction; + c.HeatCapacity = newHeatCapacity; + } + + /// + /// Divides a source into a specified number of equal parts. + /// + /// The input to split. + /// An array of s equally split from the source . + /// This will be written to. This must be the same length as num. + /// The number of s + /// to split the source into. + /// Thrown when attempting to divide the source container by zero. + /// Thrown when the length of the divided array does not match the specified number of divisions. + [PublicAPI] + public static void Divide(this T c, T[] dividedArray, int num) + where T : struct, IHeatContainer // if we allowed classes you'd just have an array reffing the same obj + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(num); + ArgumentOutOfRangeException.ThrowIfNotEqual(dividedArray.Length, num); var fraction = 1f / num; - var cFrac = c.Split(fraction); - var containers = new HeatContainer[num]; + Split(ref c, fraction); for (var i = 0; i < num; i++) { - containers[i] = cFrac; + dividedArray[i] = c; } - - return containers; } } diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs index 75611d449a..b12dd026ef 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs @@ -11,14 +11,16 @@ public static partial class HeatContainerHelpers /// to bring them into thermal equilibrium. /// Does not modify the containers. /// - /// The first to exchange heat. - /// The second to exchange heat with. - /// The amount of heat in joules that is needed + /// The first to exchange heat. + /// The second to exchange heat with. + /// The amount of transferred heat in joules that is needed /// to bring the containers to thermal equilibrium. /// A positive value indicates heat transfer from a hot cA to a cold cB. /// Thrown when the combined heat capacity of both containers is zero or negative. [PublicAPI] - public static float EquilibriumHeatQuery(this ref HeatContainer cA, ref HeatContainer cB) + public static float EquilibriumHeatQuery(ref T1 cA, ref T2 cB) + where T1 : IHeatContainer + where T2 : IHeatContainer { var cTotal = cA.HeatCapacity + cB.HeatCapacity; ArgumentOutOfRangeException.ThrowIfNegativeOrZero(cTotal); @@ -43,26 +45,30 @@ public static partial class HeatContainerHelpers /// Determines the resulting temperature if two heat containers are brought into thermal equilibrium. /// Does not modify the containers. /// - /// The first to exchange heat. - /// The second to exchange heat with. + /// The first to exchange heat. + /// The second to exchange heat with. /// The resulting equilibrium temperature both containers will be at. /// Thrown when the combined heat capacity of both containers is zero or negative. [PublicAPI] - public static float EquilibriumTemperatureQuery(this ref HeatContainer cA, ref HeatContainer cB) + public static float EquilibriumTemperatureQuery(ref T1 cA, ref T2 cB) + where T1 : IHeatContainer + where T2 : IHeatContainer { var cTotal = cA.HeatCapacity + cB.HeatCapacity; ArgumentOutOfRangeException.ThrowIfNegativeOrZero(cTotal); // Insert the above solution for Q into T_A_final = T_A_initial - Q / C_A and rearrange the result. - return (cA.HeatCapacity * cA.Temperature - cB.HeatCapacity * cB.Temperature) / cTotal; + return (cA.HeatCapacity * cA.Temperature + cB.HeatCapacity * cB.Temperature) / cTotal; } /// - /// Brings two s into thermal equilibrium by exchanging heat. + /// Brings two s into thermal equilibrium by exchanging heat. /// - /// The first to exchange heat. - /// The second to exchange heat with. + /// The first to exchange heat. + /// The second to exchange heat with. [PublicAPI] - public static void Equilibrate(this ref HeatContainer cA, ref HeatContainer cB) + public static void Equilibrate(ref T1 cA, ref T2 cB) + where T1 : IHeatContainer + where T2 : IHeatContainer { var tFinal = EquilibriumTemperatureQuery(ref cA, ref cB); cA.Temperature = tFinal; @@ -70,13 +76,15 @@ public static partial class HeatContainerHelpers } /// - /// Brings two s into thermal equilibrium by exchanging heat. + /// Brings two s into thermal equilibrium by exchanging heat. /// - /// The first to exchange heat. - /// The second to exchange heat with. + /// The first to exchange heat. + /// The second to exchange heat with. /// The amount of heat in joules that was transferred from container A to B. [PublicAPI] - public static void Equilibrate(this ref HeatContainer cA, ref HeatContainer cB, out float dQ) + public static void Equilibrate(ref T1 cA, ref T2 cB, out float dQ) + where T1 : IHeatContainer + where T2 : IHeatContainer { var tInitialA = cA.Temperature; var tFinal = EquilibriumTemperatureQuery(ref cA, ref cB); @@ -91,13 +99,13 @@ public static partial class HeatContainerHelpers #region N-Body Exchange /// - /// Brings an array of s into thermal equilibrium by exchanging heat. + /// Brings an array of s into thermal equilibrium by exchanging heat. /// - /// The array of s to bring into thermal equilibrium. + /// The array of s to bring into thermal equilibrium. [PublicAPI] - public static void Equilibrate(this HeatContainer[] cN) + public static void Equilibrate(this T[] cN) where T : IHeatContainer { - var tF = cN.EquilibriumTemperatureQuery(); + var tF = EquilibriumTemperatureQuery(cN); for (var i = 0; i < cN.Length; i++) { cN[i].Temperature = tF; @@ -105,15 +113,17 @@ public static partial class HeatContainerHelpers } /// - /// Brings a into thermal equilibrium - /// with an array of other s by exchanging heat. + /// Brings a into thermal equilibrium + /// with an array of other s by exchanging heat. /// - /// The first to bring into thermal equilibrium. - /// The array of s to bring into thermal equilibrium. + /// The first to bring into thermal equilibrium. + /// The array of s to bring into thermal equilibrium. [PublicAPI] - public static void Equilibrate(this ref HeatContainer cA, HeatContainer[] cN) + public static void Equilibrate(ref T1 cA, T2[] cN) + where T1 : IHeatContainer + where T2 : IHeatContainer { - var tF = cA.EquilibriumTemperatureQuery(cN); + var tF = EquilibriumTemperatureQuery(ref cA, cN); cA.Temperature = tF; for (var i = 0; i < cN.Length; i++) @@ -123,14 +133,14 @@ public static partial class HeatContainerHelpers } /// - /// Determines the final temperature of an array of s + /// Determines the final temperature of an array of s /// when they are brought into thermal equilibrium. Does not modify the containers. /// - /// The array of s to bring into thermal equilibrium. - /// The temperature of all s involved after reaching thermal equilibrium. + /// The array of s to bring into thermal equilibrium. + /// The temperature of all s involved after reaching thermal equilibrium. /// Thrown when the combined heat capacity of all containers is zero or negative. [PublicAPI] - public static float EquilibriumTemperatureQuery(this HeatContainer[] cN) + public static float EquilibriumTemperatureQuery(this T[] cN) where T : IHeatContainer { /* The solution is derived via the following: @@ -176,15 +186,15 @@ public static partial class HeatContainerHelpers } /// - /// Determines the final temperature of an array of s + /// Determines the final temperature of an array of s /// when they are brought into thermal equilibrium. Does not modify the containers. /// - /// The array of s to bring into thermal equilibrium. + /// The array of s to bring into thermal equilibrium. /// The amount of heat in joules that was added to each container /// to reach thermal equilibrium. - /// The temperature of all s involved after reaching thermal equilibrium. + /// The temperature of all s involved after reaching thermal equilibrium. [PublicAPI] - public static float EquilibriumTemperatureQuery(this HeatContainer[] cN, out float[] dQ) + public static float EquilibriumTemperatureQuery(this T[] cN, out float[] dQ) where T : IHeatContainer { /* For finding the total heat exchanged during the equalization between a group of bodies @@ -205,16 +215,18 @@ public static partial class HeatContainerHelpers } /// - /// Determines the final temperature of a when it is brought into thermal equilibrium - /// with an array of other s. Does not modify the containers. + /// Determines the final temperature of a when it is brought into thermal equilibrium + /// with an array of other s. Does not modify the containers. /// - /// The first to bring into thermal equilibrium. - /// The array of s to bring into thermal equilibrium. - /// The temperature of all s involved after reaching thermal equilibrium. + /// The first to bring into thermal equilibrium. + /// The array of s to bring into thermal equilibrium. + /// The temperature of all s involved after reaching thermal equilibrium. [PublicAPI] - public static float EquilibriumTemperatureQuery(this ref HeatContainer cA, HeatContainer[] cN) + public static float EquilibriumTemperatureQuery(ref T1 cA, T2[] cN) + where T1 : IHeatContainer + where T2 : IHeatContainer { - var cAll = new HeatContainer[cN.Length + 1]; + var cAll = new T1[cN.Length + 1]; cAll[0] = cA; cN.CopyTo(cAll, 1); diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs index 5aa3dcbbde..d3cb2e258a 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs @@ -7,48 +7,49 @@ public static partial class HeatContainerHelpers /// /// Merges two heat containers into one, conserving total internal energy. /// - /// The first to merge. This will be modified to contain the merged result. - /// The second to merge. + /// The first to merge. This will be modified to contain the merged result. + /// The second to merge. /// Thrown when the combined heat capacity of both containers is zero or negative. [PublicAPI] - public static void Merge(this ref HeatContainer cA, HeatContainer cB) + public static void Merge(ref T1 cA, ref T2 cB) + where T1 : IHeatContainer + where T2 : IHeatContainer { var combinedHeatCapacity = cA.HeatCapacity + cB.HeatCapacity; ArgumentOutOfRangeException.ThrowIfNegativeOrZero(combinedHeatCapacity); - var merged = new HeatContainer - { - HeatCapacity = combinedHeatCapacity, - Temperature = (cA.InternalEnergy + cB.InternalEnergy) / combinedHeatCapacity, - }; - cA = merged; + cA.HeatCapacity = combinedHeatCapacity; + cA.Temperature = (cA.InternalEnergy + cB.InternalEnergy) / combinedHeatCapacity; } /// - /// Merges an array of s into a single heat container, conserving total internal energy. + /// Merges an array of s into a single heat container, conserving total internal energy. /// - /// The first to merge. + /// The first to merge. /// This will be modified to contain the merged result. - /// The array of s to merge. + /// The array of s to merge. [PublicAPI] - public static void Merge(this ref HeatContainer cA, HeatContainer[] cN) + public static void Merge(ref T1 cA, T2[] cN) + where T1 : IHeatContainer + where T2 : IHeatContainer { - var cAcN = new HeatContainer[cN.Length + 1]; - cAcN[0] = cA; - cN.CopyTo(cAcN, 1); - - cA = cAcN.Merge(); + // merge the first array and then merge the result with cA to avoid alloc + var temp = new HeatContainer(); + cN.Merge(ref temp); + Merge(ref cA, ref temp); } /// - /// Merges an array of s into a single heat container, conserving total internal energy. + /// Merges an array of s into a single heat container, conserving total internal energy. /// - /// The array of s to merge. - /// A new representing the merged result. + /// The array of s to merge. + /// The modified containing the merged result. /// Thrown when the combined heat capacity of all containers is zero or negative. [PublicAPI] - public static HeatContainer Merge(this HeatContainer[] cN) + public static void Merge(this T1[] cN, ref T2 result) + where T1 : IHeatContainer + where T2 : IHeatContainer { var totalHeatCapacity = 0f; var totalEnergy = 0f; @@ -61,12 +62,7 @@ public static partial class HeatContainerHelpers ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalHeatCapacity); - var result = new HeatContainer - { - HeatCapacity = totalHeatCapacity, - Temperature = totalEnergy / totalHeatCapacity, - }; - - return result; + result.HeatCapacity = totalHeatCapacity; + result.Temperature = totalEnergy / totalHeatCapacity; } } diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs index ed1eadc74a..42b7ae7ca8 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs @@ -3,22 +3,22 @@ using JetBrains.Annotations; namespace Content.Shared.Temperature.HeatContainer; /// -/// Class containing helper methods for working with s. +/// Class containing helper methods for working with s. /// Use these classes instead of implementing your own heat transfer logic. /// public static partial class HeatContainerHelpers { /// - /// Adds or removes heat energy from the container. + /// Adds or removes heat energy from the . /// Positive values add heat, negative values remove heat. /// The temperature can never become lower than 0K even if more heat is removed. /// - /// The to add or remove energy. + /// The to add or remove energy. /// The energy in joules to add or remove. [PublicAPI] - public static void AddHeat(this ref HeatContainer c, float dQ) + public static void AddHeat(ref T c, float dQ) where T : IHeatContainer { - c.Temperature = c.AddHeatQuery(dQ); + c.Temperature = AddHeatQuery(ref c, dQ); } /// @@ -26,12 +26,12 @@ public static partial class HeatContainerHelpers /// Positive values add heat, negative values remove heat. This method doesn't change the container's state. /// The temperature can never become lower than 0K even if more heat is removed. /// - /// The to query. + /// The to query. /// The energy in joules to add or remove. /// The resulting temperature in kelvin after the heat change. /// Thrown when the heat capacity of the container is zero or negative. [PublicAPI] - public static float AddHeatQuery(this ref HeatContainer c, float dQ) + public static float AddHeatQuery(ref T c, float dQ) where T : IHeatContainer { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(c.HeatCapacity); // Don't allow the temperature to go below the absolute minimum. @@ -39,14 +39,14 @@ public static partial class HeatContainerHelpers } /// - /// Sets the heat capacity of a without altering its thermal energy. + /// Sets the heat capacity of a without altering its thermal energy. /// Adjusts the temperature accordingly to maintain the same internal energy. /// - /// The to modify. + /// The to modify. /// The new heat capacity to set. /// Thrown when the new heat capacity is zero or negative. [PublicAPI] - public static void SetHeatCapacity(this ref HeatContainer c, float newHeatCapacity) + public static void SetHeatCapacity(ref T c, float newHeatCapacity) where T : IHeatContainer { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(c.HeatCapacity); var currentEnergy = c.InternalEnergy; diff --git a/Content.Shared/Temperature/HeatContainer/IHeatContainer.cs b/Content.Shared/Temperature/HeatContainer/IHeatContainer.cs new file mode 100644 index 0000000000..95cf9c1728 --- /dev/null +++ b/Content.Shared/Temperature/HeatContainer/IHeatContainer.cs @@ -0,0 +1,35 @@ +namespace Content.Shared.Temperature.HeatContainer; + +/// +/// Interface that defines a general-purpose container for heat energy. +/// Any object that contains, stores, or transfers heat should use a +/// or inherit instead of implementing its own system. +/// This allows for consistent heat transfer mechanics across different objects and systems. +/// +public interface IHeatContainer +{ + /// + /// The heat capacity of this container in Joules per Kelvin. + /// This determines how much energy is required to change the temperature of the container. + /// Higher values mean the container can absorb or release more heat energy + /// without a significant change in temperature. + /// + float HeatCapacity { get; set; } + + /// + /// The current temperature of the container in Kelvin. + /// + float Temperature { get; set; } + + /// + /// The current temperature of the container in Celsius. + /// Ideal if you just need to read the temperature for UI. + /// + float TemperatureC => TemperatureHelpers.KelvinToCelsius(Temperature); + + /// + /// The current thermal energy of the container in Joules. + /// + float InternalEnergy => Temperature * HeatCapacity; +} + diff --git a/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs b/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs index 216349d6f5..a5e2dbab38 100644 --- a/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs +++ b/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs @@ -16,7 +16,7 @@ public abstract class SharedTemperatureSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; - protected EntityQuery TemperatureQuery; + [Dependency] protected readonly EntityQuery TemperatureQuery = default!; /// /// Band-aid for unpredicted atmos. Delays the application for a short period so that laggy clients can get the replicated temperature. @@ -29,8 +29,6 @@ public abstract class SharedTemperatureSystem : EntitySystem SubscribeLocalEvent(OnTemperatureChanged); SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); - - TemperatureQuery = GetEntityQuery(); } private void OnTemperatureChanged(Entity ent, ref OnTemperatureChangeEvent args) diff --git a/Content.Shared/Throwing/CatchableSystem.cs b/Content.Shared/Throwing/CatchableSystem.cs index 244522f192..93c07d1e7e 100644 --- a/Content.Shared/Throwing/CatchableSystem.cs +++ b/Content.Shared/Throwing/CatchableSystem.cs @@ -23,17 +23,14 @@ public sealed partial class CatchableSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; - private EntityQuery _handsQuery; - private EntityQuery _combatModeQuery; + [Dependency] private readonly EntityQuery _handsQuery = default!; + [Dependency] private readonly EntityQuery _combatModeQuery = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnDoHit); - - _handsQuery = GetEntityQuery(); - _combatModeQuery = GetEntityQuery(); } private void OnDoHit(Entity ent, ref ThrowDoHitEvent args) diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index c3ea10822e..530c591f60 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -38,14 +38,14 @@ public sealed class ThrowingSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly IConfigurationManager _configManager = default!; - private EntityQuery _anchorableQuery; + [Dependency] private readonly EntityQuery _anchorableQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + [Dependency] private readonly EntityQuery _projectileQuery = default!; public override void Initialize() { base.Initialize(); - _anchorableQuery = GetEntityQuery(); - Subs.CVar(_configManager, CCVars.TileFrictionModifier, value => _frictionModifier = value, true); Subs.CVar(_configManager, CCVars.AirFriction, value => _airDamping = value, true); } @@ -97,18 +97,14 @@ public sealed class ThrowingSystem : EntitySystem bool doSpin = true, ThrowingUnanchorStrength unanchor = ThrowingUnanchorStrength.None) { - var physicsQuery = GetEntityQuery(); - if (!physicsQuery.TryGetComponent(uid, out var physics)) + if (!_physicsQuery.TryComp(uid, out var physics)) return; - var projectileQuery = GetEntityQuery(); - TryThrow( uid, direction, physics, Transform(uid), - projectileQuery, baseThrowSpeed, user, pushbackRatio, @@ -130,7 +126,6 @@ public sealed class ThrowingSystem : EntitySystem Vector2 direction, PhysicsComponent physics, TransformComponent transform, - EntityQuery projectileQuery, float baseThrowSpeed = 10.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, @@ -156,7 +151,7 @@ public sealed class ThrowingSystem : EntitySystem return; // Allow throwing if this projectile only acts as a projectile when shot, otherwise disallow - if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot) + if (_projectileQuery.TryComp(uid, out var proj) && !proj.OnlyCollideWhenShot) return; var comp = new ThrownItemComponent diff --git a/Content.Shared/Tiles/FloorTileSystem.cs b/Content.Shared/Tiles/FloorTileSystem.cs index a2743ca6ca..15ca721dbd 100644 --- a/Content.Shared/Tiles/FloorTileSystem.cs +++ b/Content.Shared/Tiles/FloorTileSystem.cs @@ -38,8 +38,11 @@ public sealed class FloorTileSystem : EntitySystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly TurfSystem _turf = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + private static readonly Vector2 CheckRange = new(1f, 1f); + /// /// A recycled hashset used to check for walls when trying to place tiles on turfs. /// @@ -68,9 +71,6 @@ public sealed class FloorTileSystem : EntitySystem if (locationMap.MapId == MapId.Nullspace) return; - var physicQuery = GetEntityQuery(); - var transformQuery = GetEntityQuery(); - var map = _transform.ToMapCoordinates(location); // Disallow placement close to grids. @@ -97,7 +97,7 @@ public sealed class FloorTileSystem : EntitySystem return; } - var userPos = _transform.ToMapCoordinates(transformQuery.GetComponent(args.User).Coordinates).Position; + var userPos = _transform.ToMapCoordinates(Transform(args.User).Coordinates).Position; var dir = userPos - map.Position; var canAccessCenter = false; if (dir.LengthSquared() > 0.01) @@ -115,7 +115,7 @@ public sealed class FloorTileSystem : EntitySystem _lookup.GetEntitiesInTile(tileRef.Value, _turfCheck); foreach (var ent in _turfCheck) { - if (physicQuery.TryGetComponent(ent, out var phys) && + if (_physicsQuery.TryGetComponent(ent, out var phys) && phys.BodyType == BodyType.Static && phys.Hard && (phys.CollisionLayer & (int)CollisionGroup.Impassable) != 0) diff --git a/Content.Shared/Tools/Systems/WeldableSystem.cs b/Content.Shared/Tools/Systems/WeldableSystem.cs index c6c47d539e..2651fa7449 100644 --- a/Content.Shared/Tools/Systems/WeldableSystem.cs +++ b/Content.Shared/Tools/Systems/WeldableSystem.cs @@ -15,7 +15,8 @@ public sealed class WeldableSystem : EntitySystem [Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - private EntityQuery _query; + + [Dependency] private readonly EntityQuery _query = default!; public override void Initialize() { @@ -24,8 +25,6 @@ public sealed class WeldableSystem : EntitySystem SubscribeLocalEvent(OnWeldFinished); SubscribeLocalEvent(OnWeldChanged); SubscribeLocalEvent(OnExamine); - - _query = GetEntityQuery(); } public bool IsWelded(EntityUid uid, WeldableComponent? component = null) diff --git a/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs b/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs index 8a122a1f65..8fad7ea231 100644 --- a/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs +++ b/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs @@ -53,7 +53,7 @@ public sealed class TriggerOnEquipmentSystem : TriggerOnXSystem if ((ent.Comp.SlotFlags & args.SlotFlags) == 0) return; - Trigger.Trigger(ent.Owner, args.Equipee, ent.Comp.KeyOut); + Trigger.Trigger(ent.Owner, args.EquipTarget, ent.Comp.KeyOut); } private void OnGotUnequipped(Entity ent, ref GotUnequippedEvent args) @@ -64,6 +64,6 @@ public sealed class TriggerOnEquipmentSystem : TriggerOnXSystem if ((ent.Comp.SlotFlags & args.SlotFlags) == 0) return; - Trigger.Trigger(ent.Owner, args.Equipee, ent.Comp.KeyOut); + Trigger.Trigger(ent.Owner, args.EquipTarget, ent.Comp.KeyOut); } } diff --git a/Content.Shared/Trigger/Systems/TriggerSystem.cs b/Content.Shared/Trigger/Systems/TriggerSystem.cs index 1e7261043f..3ba9e76186 100644 --- a/Content.Shared/Trigger/Systems/TriggerSystem.cs +++ b/Content.Shared/Trigger/Systems/TriggerSystem.cs @@ -90,6 +90,9 @@ public sealed partial class TriggerSystem : EntitySystem if (!Resolve(ent, ref ent.Comp)) return false; + if (Terminating(ent)) + return false; // Stop trying to resurrect a dead horse. + if (HasComp(ent)) return false; // already activated diff --git a/Content.Shared/Warps/WarpPointSystem.cs b/Content.Shared/Warps/WarpPointSystem.cs index c8474acd33..7993b76234 100644 --- a/Content.Shared/Warps/WarpPointSystem.cs +++ b/Content.Shared/Warps/WarpPointSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Examine; +using Content.Shared.Examine; using Content.Shared.Ghost; namespace Content.Shared.Warps; @@ -16,7 +16,7 @@ public sealed class WarpPointSystem : EntitySystem if (!HasComp(args.Examiner)) return; - var loc = component.Location == null ? "" : $"'{component.Location}'"; + var loc = component.Location == null ? Name(uid) : component.Location; args.PushText(Loc.GetString("warp-point-component-on-examine-success", ("location", loc))); } } diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs index 4bcfe8a69f..7c4b97abc4 100644 --- a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs +++ b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs @@ -21,14 +21,12 @@ public sealed class HitscanBasicRaycastSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _log = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - private EntityQuery _visualsQuery; + [Dependency] private readonly EntityQuery _visualsQuery = default!; public override void Initialize() { base.Initialize(); - _visualsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnHitscanFired); } diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index 54e6ddbc1c..03276a43ab 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -130,7 +130,7 @@ public sealed partial class MeleeWeaponComponent : Component /// We don't connect it with attack range, because different weapons have different sprites, /// and this value should be adjusted manually for every weapon ideally /// - [DataField] + [DataField, AutoNetworkedField] public float AnimationOffset = 1f; // Sounds diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 67a8a78731..204b352184 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -67,6 +67,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem [Dependency] private readonly SharedStaminaSystem _stamina = default!; [Dependency] private readonly DamageExamineSystem _damageExamine = default!; + [Dependency] private readonly EntityQuery _damageQuery = default!; + private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque); /// @@ -591,7 +593,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem _meleeSound.PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component); - if (damageResult.GetTotal() > FixedPoint2.Zero) + if (damageResult.GetTotal() > FixedPoint2.Zero && !TerminatingOrDeleted(target.Value)) { DoDamageEffect(targets, user, targetXform); } @@ -650,7 +652,15 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // Validate client for (var i = entities.Count - 1; i >= 0; i--) { - if (ArcRaySuccessful(entities[i], + var entity = entities[i]; + + if (TerminatingOrDeleted(entity)) + { + entities.RemoveAt(i); + continue; + } + + if (!ArcRaySuccessful(entity, userPos, direction.ToWorldAngle(), component.Angle, @@ -659,20 +669,16 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem user, session)) { - continue; + // Bad input + entities.RemoveAt(i); } - - // Bad input - entities.RemoveAt(i); } var targets = new List(); - var damageQuery = GetEntityQuery(); - foreach (var entity in entities) { if (entity == user || - !damageQuery.HasComponent(entity)) + !_damageQuery.HasComponent(entity)) continue; targets.Add(entity); @@ -745,6 +751,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem $"{ToPrettyString(user):actor} melee attacked (heavy) {ToPrettyString(entity):subject} using {ToPrettyString(meleeUid):tool} and dealt {damageResult.GetTotal():damage} damage"); } } + + if (TerminatingOrDeleted(entity)) + targets.RemoveAt(i); } if (entities.Count != 0) @@ -753,7 +762,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem _meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component); } - if (appliedDamage.GetTotal() > FixedPoint2.Zero) + if (appliedDamage.GetTotal() > FixedPoint2.Zero && targets.Count > 0) { DoDamageEffect(targets, user, Transform(targets[0])); } diff --git a/Content.Shared/Weapons/Ranged/Components/BallisticAmmoInteractLoaderComponent.cs b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoInteractLoaderComponent.cs new file mode 100644 index 0000000000..04d4a80774 --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoInteractLoaderComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Ranged.Components; + +/// +/// If an entity with has this component, it can be used to interact +/// with the ammo entity to load it into the gun (or magazine). +/// Basically the reverse order (used vs target) to achieve the same result (loading the gun) +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class BallisticAmmoInteractLoaderComponent : Component; diff --git a/Content.Shared/Weapons/Ranged/Events/BeforeAmmoLoadedEvent.cs b/Content.Shared/Weapons/Ranged/Events/BeforeAmmoLoadedEvent.cs new file mode 100644 index 0000000000..896bfbcb6d --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Events/BeforeAmmoLoadedEvent.cs @@ -0,0 +1,21 @@ +using Content.Shared.Weapons.Ranged.Components; + +namespace Content.Shared.Weapons.Ranged.Events; + +/// +/// Raised on the ammo before it is loaded into a gun (or a magazine) +/// +[ByRefEvent] +public struct BeforeAmmoLoadedEvent() +{ + /// + /// if the entity can be used to load the gun or magazine + /// + public bool CanLoad = true; + + /// + /// If null the entity itself is used to load a gun or magazine, + /// if not null, the entity provided is used to load the gun or magazine + /// + public EntityUid? AmmoOverride; +} diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 3ddb114dd1..4b293053d2 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -3,6 +3,7 @@ using Content.Shared.Emp; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Stacks; using Content.Shared.Verbs; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; @@ -16,6 +17,7 @@ public abstract partial class SharedGunSystem { [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly SharedStackSystem _stack = null!; [MustCallBase] protected virtual void InitializeBallistic() @@ -34,6 +36,8 @@ public abstract partial class SharedGunSystem SubscribeLocalEvent(OnBallisticRefillerMapInit); SubscribeLocalEvent(OnRefillerEmpPulsed); + + SubscribeLocalEvent(OnBallisticAmmoLoad); } private void OnBallisticRefillerMapInit(Entity entity, ref MapInitEvent _) @@ -63,7 +67,6 @@ public abstract partial class SharedGunSystem { if (args.Handled || !component.MayTransfer || - !Timing.IsFirstTimePredicted || args.Target == null || args.Used == args.Target || Deleted(args.Target) || @@ -351,11 +354,20 @@ public abstract partial class SharedGunSystem bool suppressInsertionSound = false ) { - if (!CanInsertBallistic(entity, inserted)) + inserted = _stack.GetOne(inserted); + var ammoEv = new BeforeAmmoLoadedEvent(); + RaiseLocalEvent(inserted, ref ammoEv); + + if (!ammoEv.CanLoad) return false; - entity.Comp.Entities.Add(inserted); - Containers.Insert(inserted, entity.Comp.Container); + var ammo = ammoEv.AmmoOverride ?? inserted; + + if (!CanInsertBallistic(entity, ammo)) + return false; + + entity.Comp.Entities.Add(ammo); + Containers.Insert(ammo, entity.Comp.Container); if (!suppressInsertionSound) { Audio.PlayPredicted(entity.Comp.SoundInsert, entity, user); @@ -370,11 +382,8 @@ public abstract partial class SharedGunSystem public void UpdateBallisticAppearance(Entity ent) { - if (!Timing.IsFirstTimePredicted || !TryComp(ent, out var appearance)) - return; - - Appearance.SetData(ent, AmmoVisuals.AmmoCount, GetBallisticShots(ent.Comp), appearance); - Appearance.SetData(ent, AmmoVisuals.AmmoMax, ent.Comp.Capacity, appearance); + Appearance.SetData(ent, AmmoVisuals.AmmoCount, GetBallisticShots(ent.Comp)); + Appearance.SetData(ent, AmmoVisuals.AmmoMax, ent.Comp.Capacity); } public void SetBallisticUnspawned(Entity entity, int count) @@ -396,6 +405,21 @@ public abstract partial class SharedGunSystem PauseSelfRefill(entity, args.Duration); } + private void OnBallisticAmmoLoad(Entity ent, ref AfterInteractEvent args) + { + if (args.Handled || args.Target == null) + return; + + if (!TryComp(ent, out var ballisticAmmoProviderComp)) + return; + + if (TryBallisticInsert( + (ent, ballisticAmmoProviderComp), + args.Target.Value, + args.User)) + args.Handled = true; + } + private void UpdateBallistic(float frameTime) { var query = EntityQueryEnumerator(); diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs index 9f4cc9d6aa..a75af8947d 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs @@ -92,7 +92,7 @@ public abstract partial class SharedGunSystem /// /// Opens then closes the bolt, or just closes it if currently open. /// - private void UseChambered(EntityUid uid, ChamberMagazineAmmoProviderComponent component, EntityUid? user = null) + public void UseChambered(EntityUid uid, ChamberMagazineAmmoProviderComponent component, EntityUid? user = null) { if (component.BoltClosed == false) { @@ -310,7 +310,7 @@ public abstract partial class SharedGunSystem return true; } - protected EntityUid? GetChamberEntity(EntityUid uid) + public EntityUid? GetChamberEntity(EntityUid uid) { if (!Containers.TryGetContainer(uid, ChamberSlot, out var container) || container is not ContainerSlot slot) diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs index e684d9aad1..6830c2504b 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs @@ -118,8 +118,7 @@ public partial class SharedGunSystem return false; } - var xformQuery = GetEntityQuery(); - var xform = xformQuery.GetComponent(insertEnt); + var xform = Transform(insertEnt); var ammo = new List<(EntityUid? Entity, IShootable Shootable)>(freeSlots); var ev = new TakeAmmoEvent(freeSlots, ammo, xform.Coordinates, user); RaiseLocalEvent(insertEnt, ev); diff --git a/Content.Shared/Weather/SharedWeatherSystem.cs b/Content.Shared/Weather/SharedWeatherSystem.cs index d2270cf5c0..08429fc958 100644 --- a/Content.Shared/Weather/SharedWeatherSystem.cs +++ b/Content.Shared/Weather/SharedWeatherSystem.cs @@ -22,20 +22,12 @@ public abstract class SharedWeatherSystem : EntitySystem [Dependency] private readonly SharedRoofSystem _roof = default!; [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - private EntityQuery _blockQuery; - private EntityQuery _weatherQuery; + [Dependency] private readonly EntityQuery _blockQuery = default!; + [Dependency] private readonly EntityQuery _weatherQuery = default!; public static readonly TimeSpan StartupTime = TimeSpan.FromSeconds(15); public static readonly TimeSpan ShutdownTime = TimeSpan.FromSeconds(15); - public override void Initialize() - { - base.Initialize(); - - _blockQuery = GetEntityQuery(); - _weatherQuery = GetEntityQuery(); - } - public bool CanWeatherAffect(Entity ent, TileRef tileRef) { if (tileRef.Tile.IsEmpty) diff --git a/Content.Shared/Whitelist/EntityWhitelistSystem.cs b/Content.Shared/Whitelist/EntityWhitelistSystem.cs index bb4abe3197..d94b48d9cb 100644 --- a/Content.Shared/Whitelist/EntityWhitelistSystem.cs +++ b/Content.Shared/Whitelist/EntityWhitelistSystem.cs @@ -9,13 +9,7 @@ public sealed class EntityWhitelistSystem : EntitySystem { [Dependency] private readonly TagSystem _tag = default!; - private EntityQuery _itemQuery; - - public override void Initialize() - { - base.Initialize(); - _itemQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _itemQuery = default!; /// public bool IsValid(EntityWhitelist list, [NotNullWhen(true)] EntityUid? uid) diff --git a/Content.Shared/Wieldable/SharedWieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs index 49db4ff86c..bb8abffe7e 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -209,7 +209,7 @@ public abstract class SharedWieldableSystem : EntitySystem private void OnBlockerEquipped(Entity ent, ref GotEquippedEvent args) { if (ent.Comp.BlockEquipped) - UnwieldAll(args.Equipee, force: true); + UnwieldAll(args.EquipTarget, force: true); } private void OnBlockerEquippedHand(Entity ent, ref GotEquippedHandEvent args) diff --git a/Content.Shared/Wires/SharedWiresComponent.cs b/Content.Shared/Wires/SharedWiresComponent.cs index d691e854ea..8fdb3de0b1 100644 --- a/Content.Shared/Wires/SharedWiresComponent.cs +++ b/Content.Shared/Wires/SharedWiresComponent.cs @@ -242,7 +242,7 @@ namespace Content.Shared.Wires WireLetter.γ => "wire-letter-name-gamma", WireLetter.δ => "wire-letter-name-delta", WireLetter.ε => "wire-letter-name-epsilon", - WireLetter.ζ => "wire-letter-name-zeta ", + WireLetter.ζ => "wire-letter-name-zeta", WireLetter.η => "wire-letter-name-eta", WireLetter.θ => "wire-letter-name-theta", WireLetter.ι => "wire-letter-name-iota", diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index c4f860e165..b812115211 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -4,7 +4,10 @@ using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Tools.Systems; using Content.Shared.UserInterface; +using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; +using Robust.Shared.Player; +using Robust.Shared.Utility; namespace Content.Shared.Wires; @@ -15,6 +18,7 @@ public abstract class SharedWiresSystem : EntitySystem [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedToolSystem Tool = default!; + [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; public override void Initialize() { @@ -24,6 +28,7 @@ public abstract class SharedWiresSystem : EntitySystem SubscribeLocalEvent(OnPanelDoAfter); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent>(OnGetVerbs); SubscribeLocalEvent(OnAttemptOpenActivatableUI); SubscribeLocalEvent(OnActivatableUIPanelChanged); @@ -96,6 +101,32 @@ public abstract class SharedWiresSystem : EntitySystem } } + private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!IsPanelOpen(ent.Owner)) + return; + + var actor = args.User; + var verb = new AlternativeVerb + { + Text = Loc.GetString("wires-panel-verb-view-panel"), + Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/screwdriver.png")), + Act = () => OpenUserInterface(ent, actor), + }; + + args.Verbs.Add(verb); + } + + public void OpenUserInterface(EntityUid uid, EntityUid actor) + { + UI.OpenUi(uid, WiresUiKey.Key, actor); + } + + public void OpenUserInterface(EntityUid uid, ICommonSession player) + { + UI.OpenUi(uid, WiresUiKey.Key, player); + } + public void ChangePanelVisibility(EntityUid uid, WiresPanelComponent component, bool visible) { component.Visible = visible; diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 09a1e0bfea..9a7b0d8ee3 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -12,15 +12,12 @@ public abstract partial class SharedXenoArtifactSystem { [Dependency] private readonly EntityTableSystem _entityTable = default!; - private EntityQuery _xenoArtifactQuery; - private EntityQuery _nodeQuery; + [Dependency] private readonly EntityQuery _xenoArtifactQuery = default!; + [Dependency] private readonly EntityQuery _nodeQuery = default!; private void InitializeNode() { SubscribeLocalEvent(OnNodeMapInit); - - _xenoArtifactQuery = GetEntityQuery(); - _nodeQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index fedb7f1f60..f6d9331a12 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -12,12 +12,10 @@ public abstract partial class SharedXenoArtifactSystem { [Dependency] private readonly SharedAudioSystem _audio = default!; - private EntityQuery _unlockingQuery; + [Dependency] private readonly EntityQuery _unlockingQuery = default!; private void InitializeUnlock() { - _unlockingQuery = GetEntityQuery(); - SubscribeLocalEvent(OnUnlockingStarted); } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEShuffleSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEShuffleSystem.cs index 9eb627b4f6..4feb2b1649 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEShuffleSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEShuffleSystem.cs @@ -15,19 +15,11 @@ public sealed class XAEShuffleSystem : BaseXAESystem [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly IGameTiming _timing = default!; - private EntityQuery _mobState; + [Dependency] private readonly EntityQuery _mobState = default!; /// Pre-allocated and re-used collection. private readonly HashSet _entities= new(); - /// - public override void Initialize() - { - base.Initialize(); - - _mobState = GetEntityQuery(); - } - /// protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) { diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseQueryUpdateXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseQueryUpdateXATSystem.cs index 0abad7bdd5..ca4c645ba1 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseQueryUpdateXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseQueryUpdateXATSystem.cs @@ -8,15 +8,7 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAT; /// Type of XAT component that system will work with. public abstract class BaseQueryUpdateXATSystem : BaseXATSystem where T : Component { - protected EntityQuery _xenoArtifactQuery; - - /// - public override void Initialize() - { - base.Initialize(); - - _xenoArtifactQuery = GetEntityQuery(); - } + [Dependency] protected readonly EntityQuery _xenoArtifactQuery = default!; /// public override void Update(float frameTime) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs index d995a12d6a..48d0e01636 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -12,15 +12,7 @@ public abstract class BaseXATSystem : EntitySystem where T : Component [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] protected readonly SharedXenoArtifactSystem XenoArtifact = default!; - private EntityQuery _unlockingQuery; - - /// - public override void Initialize() - { - base.Initialize(); - - _unlockingQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _unlockingQuery = default!; /// /// Subscribes to event occurring on artifact (and by relaying - on node). diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs index 50a7b0200e..e37f44adf8 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs @@ -11,15 +11,13 @@ public sealed class XATDeathSystem : BaseXATSystem { [Dependency] private readonly SharedTransformSystem _transform = default!; - private EntityQuery _xenoArtifactQuery; + [Dependency] private readonly EntityQuery _xenoArtifactQuery = default!; /// public override void Initialize() { base.Initialize(); - _xenoArtifactQuery = GetEntityQuery(); - SubscribeLocalEvent(OnMobStateChanged); } diff --git a/Directory.Packages.props b/Directory.Packages.props index 3604412933..9e7322a529 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -8,11 +8,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/MSBuild/Content.props b/MSBuild/Content.props index de44d3ccc6..1ec3fe5cba 100644 --- a/MSBuild/Content.props +++ b/MSBuild/Content.props @@ -12,6 +12,6 @@ true - CS0618,CS0672,CS0612,CS1062,CS1064,NU1903 + CS0618,CS0672,CS0612,CS1062,CS1064,NU1901,NU1902,NU1903,NU1904 diff --git a/Resources/Audio/Effects/Footsteps/suitstep1.ogg b/Resources/Audio/Effects/Footsteps/suitstep1.ogg deleted file mode 100644 index 8de7412490..0000000000 Binary files a/Resources/Audio/Effects/Footsteps/suitstep1.ogg and /dev/null differ diff --git a/Resources/Audio/Effects/Footsteps/suitstep2.ogg b/Resources/Audio/Effects/Footsteps/suitstep2.ogg deleted file mode 100644 index 56396fb71e..0000000000 Binary files a/Resources/Audio/Effects/Footsteps/suitstep2.ogg and /dev/null differ diff --git a/Resources/Audio/Machines/airlock_close.ogg b/Resources/Audio/Machines/airlock_close.ogg index 5da73f770e..5b818eeefa 100644 Binary files a/Resources/Audio/Machines/airlock_close.ogg and b/Resources/Audio/Machines/airlock_close.ogg differ diff --git a/Resources/Audio/Machines/attributions.yml b/Resources/Audio/Machines/attributions.yml index 3455c5b4f4..62cd65bb7b 100644 --- a/Resources/Audio/Machines/attributions.yml +++ b/Resources/Audio/Machines/attributions.yml @@ -113,11 +113,16 @@ copyright: "cmorris035" source: "https://freesound.org/people/cmorris035/sounds/319152/" -- files: ["airlock_open.ogg", "airlock_close.ogg"] +- files: ["airlock_open.ogg", "airlock_deny.ogg"] license: "CC-BY-SA-3.0" copyright: "/tg/station" source: "https://github.com/tgstation/tgstation/tree/5f5002b21253354c20ea224be3f604d79299b37e/sound/machines" +- files: ["airlock_close.ogg"] + license: "CC-BY-SA-3.0" + copyright: "From eris/bay at commit 789ed19064ac42376d2355192cd1069a45a310f1, modified by mirrorcult to be shorter and match airlock timings" + source: "https://github.com/discordia-space/CEV-Eris/commit/789ed19064ac42376d2355192cd1069a45a310f1" + - files: ["machine_vend_hot_drink.ogg"] license: "CC0-1.0" copyright: "waxsocks on freesound.org" diff --git a/Resources/Audio/Misc/attributions.yml b/Resources/Audio/Misc/attributions.yml index 8fda833b1d..ada6b70983 100644 --- a/Resources/Audio/Misc/attributions.yml +++ b/Resources/Audio/Misc/attributions.yml @@ -87,3 +87,8 @@ license: "CC-BY-SA-3.0" copyright: "Taken from Citadel station" source: "https://github.com/Citadel-Station-13/Citadel-Station-13/blob/e31455667ebf3331255c1143e1e7215bc737a287/sound/misc/deltakalaxon.ogg" + +- files: ["camera_snap.ogg"] + license: "CC-BY-4.0" + copyright: "Taken from freesound, edited by ketufaispikinut(GitHub)" + source: "https://freesound.org/people/thecheeseman/sounds/51360/" diff --git a/Resources/Audio/Misc/camera_snap.ogg b/Resources/Audio/Misc/camera_snap.ogg new file mode 100644 index 0000000000..5730547ad4 Binary files /dev/null and b/Resources/Audio/Misc/camera_snap.ogg differ diff --git a/Resources/Audio/Misc/delta_alt.ogg b/Resources/Audio/Misc/delta_alt.ogg deleted file mode 100644 index b389910f13..0000000000 Binary files a/Resources/Audio/Misc/delta_alt.ogg and /dev/null differ diff --git a/Resources/Audio/Weapons/Guns/Empty/attributions.yml b/Resources/Audio/Weapons/Guns/Empty/attributions.yml new file mode 100644 index 0000000000..2789237e39 --- /dev/null +++ b/Resources/Audio/Weapons/Guns/Empty/attributions.yml @@ -0,0 +1,4 @@ +- files: ["empty_beep.ogg"] + license: "CC-BY-4.0" + copyright: "Created by altemark" + source: "https://freesound.org/people/altemark/sounds/39747" diff --git a/Resources/Audio/Weapons/Guns/Empty/empty_beep.ogg b/Resources/Audio/Weapons/Guns/Empty/empty_beep.ogg new file mode 100644 index 0000000000..512cd8c12f Binary files /dev/null and b/Resources/Audio/Weapons/Guns/Empty/empty_beep.ogg differ diff --git a/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml b/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml index e9154f9917..f3067ad07e 100644 --- a/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml +++ b/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml @@ -47,3 +47,8 @@ license: "CC-BY-4.0" copyright: "Created by UnderlinedDesigns and DrinkingWindGames (https://freesound.org/people/DrinkingWindGames/sounds/789647), modified by Halycon" source: "https://freesound.org/people/UnderlinedDesigns/sounds/188499/" + +- files: ["magnetic_shot.ogg"] + license: "CC-BY-4.0" + copyright: "Created by simeonradivoev" + source: "https://freesound.org/people/simeonradivoev/sounds/740218" diff --git a/Resources/Audio/Weapons/Guns/Gunshots/magnetic_shot.ogg b/Resources/Audio/Weapons/Guns/Gunshots/magnetic_shot.ogg new file mode 100644 index 0000000000..9f77a610bb Binary files /dev/null and b/Resources/Audio/Weapons/Guns/Gunshots/magnetic_shot.ogg differ diff --git a/Resources/Audio/Weapons/Guns/MagIn/attributions.yml b/Resources/Audio/Weapons/Guns/MagIn/attributions.yml new file mode 100644 index 0000000000..cb739cec28 --- /dev/null +++ b/Resources/Audio/Weapons/Guns/MagIn/attributions.yml @@ -0,0 +1,4 @@ +- files: ["tile_load.ogg"] + license: "CC-BY-NC-4.0" + copyright: "Created by LloydEvans09" + source: "https://freesound.org/people/LloydEvans09/sounds/321807" diff --git a/Resources/Audio/Weapons/Guns/MagIn/tile_load.ogg b/Resources/Audio/Weapons/Guns/MagIn/tile_load.ogg new file mode 100644 index 0000000000..c2346ad8c3 Binary files /dev/null and b/Resources/Audio/Weapons/Guns/MagIn/tile_load.ogg differ diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index dcaae63db7..96e27d879f 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1691,5 +1691,30 @@ Entries: id: 206 time: '2026-03-08T23:41:39.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/41427 +- author: SlamBamActionman + changes: + - message: adduplink command now works properly, gives an uplink implant if there's + no PDA, and returns the uplink code in the command terminal. + type: Fix + id: 207 + time: '2026-04-04T20:14:15.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38712 +- author: mikeysaurus + changes: + - message: The debug SetOutfit command now also fills target's storage containers + based on the gear preset. + type: Tweak + id: 208 + time: '2026-04-09T05:05:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43346 +- author: UpAndLeaves + changes: + - message: Added container toolshed commands! + type: Add + - message: storage:query and inventory:query are now storage:contents and inventory:contents. + type: Tweak + id: 209 + time: '2026-04-19T17:47:31.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43098 Name: Admin Order: 3 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ac34e50e97..237a128e69 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,522 +1,4 @@ Entries: -- author: jessicamaybe - changes: - - message: Added swabs and an emag inventory to the biogenerator - type: Tweak - id: 9078 - time: '2025-10-12T00:15:18.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39037 -- author: SuperGDPWYL - changes: - - message: Added the Syndicate ID Card to the uplink for 1 TC. - type: Add - id: 9079 - time: '2025-10-12T00:56:09.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/38381 -- author: aada - changes: - - message: Cannabis no longer stacks infinitely. Sorry, botanists! - type: Fix - id: 9080 - time: '2025-10-12T01:29:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/38412 -- author: GovnokradZXC - changes: - - message: New hair named Pigtail (Over Eye) - type: Add - id: 9081 - time: '2025-10-12T05:45:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39850 -- author: Princess-Cheeseballs - changes: - - message: Meat Kudzu gasps less often. - type: Tweak - id: 9082 - time: '2025-10-12T10:47:30.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39304 -- author: PJB3005 - changes: - - message: The patrons list in the in-game credits works again. - type: Fix - id: 9083 - time: '2025-10-12T11:02:33.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40840 -- author: DrSmugleaf - changes: - - message: Fixed species not being ordered alphabetically in the character customization - UI. - type: Fix - id: 9084 - time: '2025-10-12T11:14:46.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39359 -- author: Callmore - changes: - - message: Bullet casings can now be picked up again. - type: Fix - id: 9085 - time: '2025-10-12T18:45:39.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40829 -- author: CoconutThunder - changes: - - message: Fixed lubed items being thrown when looking at the pickup verb. - type: Fix - - message: Fixed multihanded items showing a popup when looking at the pickup verb. - type: Fix - - message: Fixed a bug with lubed handcuffs. - type: Fix - id: 9086 - time: '2025-05-25T05:10:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/38705 -- author: GitHubUser53123 - changes: - - message: Zombies that aren't supposed to spread the zombie virus now don't do - so on mobs in critical state. - type: Fix - id: 9087 - time: '2025-10-13T01:24:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40857 -- author: PotentiallyTom - changes: - - message: Added a page in the guidebook listing common AI and silicon lawsets. - type: Add - id: 9088 - time: '2025-10-13T11:55:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/38225 -- author: meganerobot - changes: - - message: SmartFridges are now airtight. - type: Tweak - id: 9089 - time: '2025-10-13T16:19:03.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40196 -- author: TrixxedHeart - changes: - - message: Renamed "trash" reagent to "reprocessed material" - type: Tweak - id: 9090 - time: '2025-10-13T17:18:40.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39761 -- author: ScarKy0 - changes: - - message: Station AI can no longer control bolts, emergency access and electrify - status on doors it has no access to. - type: Fix - id: 9091 - time: '2025-10-13T20:53:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/38444 -- author: Myra - changes: - - message: The built-in soundfont for MIDI's should sound better, especially for - songs with percussion. - type: Tweak - id: 9092 - time: '2025-10-13T21:22:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40888 -- author: KittyCat432 - changes: - - message: Slime guidebook is more representative of their actual strengths. - type: Tweak - id: 9093 - time: '2025-10-14T04:21:07.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40842 -- author: MilenVolf - changes: - - message: Fixed some physics bugs that could cause the grappling hook to teleport - entities. - type: Fix - id: 9094 - time: '2025-10-14T10:35:38.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40899 -- author: SirWarock - changes: - - message: Made Bodybags slightly harder to drag, and rollerbeds slightly easier. - They have wheels. - type: Tweak - id: 9095 - time: '2025-10-14T11:41:30.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40880 -- author: aada - changes: - - message: Geiger counters can now be placed on a utility belt. - type: Fix - id: 9096 - time: '2025-10-14T12:12:06.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40898 -- author: kontakt - changes: - - message: Singulo now consumes carpet tiles. - type: Fix - id: 9097 - time: '2025-10-14T18:27:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40896 -- author: B_Kirill - changes: - - message: Fixed blocked rotation of zombie NPCs. - type: Fix - id: 9098 - time: '2025-10-14T19:12:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40812 -- author: temm1ie, aksosotl - changes: - - message: Added new botany-themed poster. - type: Add - id: 9099 - time: '2025-10-14T19:42:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40908 -- author: ArtisticRoomba - changes: - - message: Atmospherics Delta-Pressure now properly computes delta pressure for - structures that can hold air in their tile while still being airtight (ex. directional - windows, diagonal windows). - type: Fix - id: 9100 - time: '2025-10-14T22:46:46.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40435 -- author: SlamBamActionman - changes: - - message: Lying trait now features more grammatically correct lying. - type: Fix - id: 9101 - time: '2025-10-14T23:13:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39370 -- author: Winkarst-cpu - changes: - - message: Now doors play an animation while being emagged. - type: Fix - id: 9102 - time: '2025-10-14T23:31:16.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40350 -- author: slarticodefast - changes: - - message: Zombified arachnids can no longer spam infinite silk due to having no - hunger. - type: Fix - id: 9103 - time: '2025-10-14T23:31:16.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40279 -- author: telavivgamers - changes: - - message: You may put ashes and matchsticks into ashtrays. - type: Tweak - id: 9104 - time: '2025-10-15T12:38:14.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40926 -- author: slarticodefast, Davyei - changes: - - message: You can now use parcelwrap on humanoids. - type: Add - id: 9105 - time: '2025-10-15T20:30:45.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40911 -- author: aada - changes: - - message: Grenade penguins have new AI. They won't bite your hand while holding - them, and hone in on a single target when released. - type: Tweak - id: 9106 - time: '2025-10-15T23:59:05.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34935 -- author: DinnerCalzone - changes: - - message: ID card sprites have been tweaked to unsquish their job icons. - type: Tweak - id: 9107 - time: '2025-10-16T03:35:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40414 -- author: redmushie - changes: - - message: Fixed power sensors not respecting their configured network setting - type: Fix - id: 9108 - time: '2025-10-16T12:38:04.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40934 -- author: Quasr - changes: - - message: Sky blue fancy tables and curtains are now construct-able - type: Add - - message: Sky blue carpet is now printable - type: Fix - id: 9109 - time: '2025-10-16T13:37:25.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40867 -- author: Worldwaker - changes: - - message: Fixed parcels being indestructible for all damage types except Slash - damage. - type: Fix - id: 9110 - time: '2025-10-16T16:28:48.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40940 -- author: FairlySadPanda - changes: - - message: Two new instrument options for the microphone, Wa and Wah. - type: Add - - message: Fixed the gilded bike horn's instrument not playing properly, causing - listening to it to be even worse than it is supposed to be. - type: Fix - - message: Fixed the microphone's Kweh instrument not playing properly, causing - listening it to be even worse than it is supposed to be. - type: Fix - id: 9111 - time: '2025-10-16T17:03:08.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39210 -- author: MilenVolf - changes: - - message: Go go hat's activation phrase can be reset to the default without having - to record it manually to reset the phrase. - type: Tweak - id: 9112 - time: '2025-10-16T18:47:31.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/35636 -- author: Wolfkey-SomeoneElseTookMyUsername - changes: - - message: The recipes for grilled cheese, cotton buns, cotton cakes, and cotton - grilled cheese are now in the correct category in the guidebook - type: Fix - id: 9113 - time: '2025-10-17T18:56:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40949 -- author: TrixxedHeart - changes: - - message: Added Enter/Leave Genpop access to Security by default, allowing them - to be able to fix Genpop turnstiles with the access configurator if they are - destroyed. - type: Tweak - id: 9114 - time: '2025-10-18T00:28:44.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39515 -- author: TrixxedHeart - changes: - - message: Added Vox Chitter and Clicking emotes - type: Add - id: 9115 - time: '2025-10-18T00:28:44.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40878 -- author: Kittygyat - changes: - - message: Added a new generic Artistry borg module! - type: Add - id: 9116 - time: '2025-10-18T07:13:42.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39679 -- author: Hitlinemoss - changes: - - message: Folders and clipboards now recycle into sensible material components, - rather than only cardboard. - type: Fix - - message: Clipboards and plastic clipboards require slightly more steel to produce - in autolathes. - type: Tweak - id: 9117 - time: '2025-10-18T09:25:00.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40954 -- author: PicklOH - changes: - - message: Rags can no longer be used to remove evidence. - type: Remove - id: 9118 - time: '2025-10-18T13:49:54.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40818 -- author: MissKay1994 - changes: - - message: Vox organs now have unique sprites. - type: Add - id: 9119 - time: '2025-10-18T17:07:52.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40555 -- author: Hitlinemoss - changes: - - message: Cargo orders containing beverages now ship in freezers. - type: Tweak - id: 9120 - time: '2025-10-18T17:20:44.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40955 -- author: perryprog - changes: - - message: Drag-and-dropping liquids between containers has been adjusted. - type: Tweak - - message: You can no longer insert liquids into foods by clicking. Using syringes - still works as before. - type: Tweak - id: 9121 - time: '2025-10-18T17:58:32.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/38871 -- author: Mehnix - changes: - - message: All pens now embed when thrown. - type: Tweak - id: 9122 - time: '2025-10-18T21:39:32.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/39104 -- author: JackRyd3r - changes: - - message: You can now put the Energy Magnum to into jackboots/combat boots and - security belts/carriers - type: Fix - id: 9123 - time: '2025-10-19T06:55:36.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40974 -- author: AlwyAnri - changes: - - message: Added a ninja headset. - type: Add - id: 9124 - time: '2025-10-19T11:45:14.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40054 -- author: Pgriha - changes: - - message: Cotton seeds added to seeds crate from Cargo. - type: Add - id: 9125 - time: '2025-10-19T16:02:52.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40970 -- author: SpaceLizard24 - changes: - - message: Creating crystal chemical reaction now can produce yellow and black crystals! - type: Fix - id: 9126 - time: '2025-10-19T18:37:46.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40985 -- author: Kittygyat - changes: - - message: Monkeys, Kobolds & Scurrets can now shove/disarm like regular species! - type: Tweak - id: 9127 - time: '2025-10-19T19:09:04.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/38542 -- author: PotentiallyTom - changes: - - message: AI Law boards now have a link to the lawsets guide page - type: Tweak - - message: Added the My, Robot book. - type: Add - id: 9128 - time: '2025-10-19T21:10:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40944 -- author: aspiringLich - changes: - - message: UIs have been brought more in line with each other with a standardized - palette, as well as other changes resulting from a new styling mechanism. - type: Tweak - id: 9129 - time: '2025-10-19T21:23:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/29903 -- author: ArtisticRoomba, seam_less - changes: - - message: Job-themed lizard plushies! You can equip them in your loadout if you - have 20 hours on the job. - type: Add - id: 9130 - time: '2025-10-19T22:28:55.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/34127 -- author: AsnDen - changes: - - message: Large thruster (2x2) was added. - type: Add - id: 9131 - time: '2025-10-19T23:51:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/37681 -- author: ArtisticRoomba - changes: - - message: Slimes can now metabolize their own slime to restore their blood level. - type: Add - - message: Slime is no longer effective at satiating hunger when metabolized by - a Slime. - type: Tweak - id: 9132 - time: '2025-10-20T00:40:40.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32537 -- author: Kittygyat - changes: - - message: Added a cosmetic carp suit to the AutoDrobe's contraband inventory! - type: Add - id: 9133 - time: '2025-10-20T02:56:43.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40995 -- author: Princess-Cheeseballs - changes: - - message: Zombies can no longer hurt Initial Infected - type: Tweak - id: 9134 - time: '2025-10-20T18:06:54.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41007 -- author: B_Kirill - changes: - - message: Hostile and Eliminated criminal statuses - type: Add - id: 9135 - time: '2025-10-21T08:56:16.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36244 -- author: michaelchessall - changes: - - message: Matches can now be placed into ashtrays. - type: Tweak - id: 9136 - time: '2025-10-21T09:37:43.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41016 -- author: SnappingOpossum - changes: - - message: Upgraded solar panels can now take structural damage. - type: Fix - id: 9137 - time: '2025-10-21T09:50:17.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40992 -- author: qwerltaz - changes: - - message: Goats and cows eat kudzu again. - type: Fix - id: 9138 - time: '2025-10-21T10:02:30.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40220 -- author: Krzeszny - changes: - - message: Updated the controls guide. - type: Tweak - id: 9139 - time: '2025-10-21T10:14:59.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40978 -- author: Quasr - changes: - - message: The "Friendly Fauna" artifact node no longer spawns hostile mobs - type: Tweak - id: 9140 - time: '2025-10-21T10:27:30.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40979 -- author: Fildrance - changes: - - message: Door remotes now have a radial menu for changing modes. - type: Tweak - id: 9141 - time: '2025-10-21T12:28:04.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/36378 -- author: TrixxedHeart - changes: - - message: Vox can now have up to 4 head markings. - type: Fix - id: 9142 - time: '2025-10-21T14:16:02.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40542 -- author: Princess-Cheeseballs - changes: - - message: You can now get drunk again. - type: Fix - id: 9143 - time: '2025-10-21T20:24:06.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41002 -- author: SlamBamActionman - changes: - - message: The hypopen now requires a short delay before being filled. - type: Tweak - id: 9144 - time: '2025-10-21T22:17:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/40538 -- author: SolidSyn - changes: - - message: Changed the cooldown on wizards mind swap spell from 5 minutes to 3 minutes. - type: Tweak - id: 9145 - time: '2025-10-21T22:47:48.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/41027 - author: ToastEnjoyer changes: - message: A security flashlight now spawns in the HoS's locker roundstart. @@ -4033,3 +3515,540 @@ Entries: id: 9589 time: '2026-03-25T13:49:55.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/43331 +- author: salarua + changes: + - message: Chat dialogue now appears in curly quotes rather than straight quotes. + type: Tweak + id: 9590 + time: '2026-03-27T17:53:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43350 +- author: Velken + changes: + - message: Mail no longer goes all to one person per batch. + type: Fix + id: 9591 + time: '2026-03-27T23:00:25.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43361 +- author: Gentleman-Bird + changes: + - message: Clockwork Glass is now grindable + type: Fix + id: 9592 + time: '2026-03-28T15:14:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43371 +- author: Minerva + changes: + - message: Singularity distortion effects now respect the reduced motion accessibility + option. + type: Tweak + id: 9593 + time: '2026-03-28T16:01:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43362 +- author: M4rchy-S + changes: + - message: '"Reduce motion of visual effects" now works for drunk and blood loss + status' + type: Add + id: 9594 + time: '2026-03-28T22:19:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/41878 +- author: insoPL, Quantum-cross, ArtisticRoomba + changes: + - message: Hot gasses now have a visible distortion effect. + type: Add + id: 9595 + time: '2026-03-29T02:36:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/42973 +- author: HoofedEar + changes: + - message: Lathe machine interfaces now display their proper names + type: Tweak + id: 9596 + time: '2026-03-29T23:26:46.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43392 +- author: aada + changes: + - message: The value of tier 1 tech disks has been lowered. + type: Tweak + id: 9597 + time: '2026-03-29T23:41:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43395 +- author: MissKay1994 + changes: + - message: Salt is now significantly less common in ore generation + type: Tweak + id: 9598 + time: '2026-03-31T00:31:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43012 +- author: Tayrtahn + changes: + - message: Ghost time of death display is now accurate. + type: Fix + id: 9599 + time: '2026-03-31T03:41:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43411 +- author: ThatGuyUSA + changes: + - message: Wizard robes and hats have been given proper in-hand sprites. + type: Tweak + id: 9600 + time: '2026-04-02T22:11:23.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43429 +- author: Booblesnoot42 + changes: + - message: 'EXPERIMENTAL: Player-controlled spiders and slimes are now forced to + attack if they are passive near enemies for too long.' + type: Tweak + id: 9601 + time: '2026-04-03T06:18:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/42399 +- author: alexalexmax + changes: + - message: Added a new uplink category for objective items. + type: Add + - message: 'Added a new objective for traitors: Hijack the Automated Trade Station. + Your uplink will have a hijack beacon for you to use if you have this objective' + type: Add + id: 9602 + time: '2026-04-03T10:35:56.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/42135 +- author: Minemoder + changes: + - message: Humanoids can now break out of locked genpop lockers + type: Tweak + id: 9603 + time: '2026-04-03T18:01:11.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/41633 +- author: sudobeans + changes: + - message: Crew Manifest PDA program now displays a message when it fails to load. + type: Tweak + id: 9604 + time: '2026-04-03T18:16:55.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43400 +- author: slarticodefast + changes: + - message: You can now throw banana cream pies at the station AI. + type: Add + id: 9605 + time: '2026-04-03T18:35:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43388 +- author: Samuka + changes: + - message: When reloading guns or magazines with stack items, it only reloads one + at the time, instead of inserting the whole stack. + type: Fix + id: 9606 + time: '2026-04-03T21:44:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/41503 +- author: 0-Anon + changes: + - message: Rebalanced the syndicate reinforcement specializations to be more effective + at their roles. + type: Tweak + id: 9607 + time: '2026-04-04T04:06:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43057 +- author: spanky-spanky + changes: + - message: Cell rechargers, weapon rechargers and turbo rechargers can now be picked + up to transport them when unanchored. + type: Tweak + id: 9608 + time: '2026-04-04T07:15:23.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/40645 +- author: ThatGuyUSA + changes: + - message: Midround Wizards now show up on the end results screen. + type: Fix + id: 9609 + time: '2026-04-04T15:51:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/42766 +- author: Zekins3366 + changes: + - message: Fixed being able to set multiple conflicting head-top visuals on humans + at the same time. + type: Fix + id: 9610 + time: '2026-04-04T16:48:03.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43465 +- author: SlamBamActionman + changes: + - message: Player sell costs are now randomized when scanned with an appraisal tool. + type: Tweak + id: 9611 + time: '2026-04-04T17:04:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43445 +- author: themias + changes: + - message: Instead of disappearing, space dragons now burst into giblets if they + go too long without summoning a rift. + type: Tweak + id: 9612 + time: '2026-04-04T18:35:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43296 +- author: VanderslootAssgiraffe + changes: + - message: Added new lobby art depicting a mime mocking nukies trapped behind an + invisible wall as the mime defuses the nuke + type: Add + id: 9613 + time: '2026-04-04T19:38:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/42947 +- author: 0-Anon + changes: + - message: Rat Kings can now pull objects. + type: Tweak + id: 9614 + time: '2026-04-04T19:58:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/42701 +- author: SlamBamActionman + changes: + - message: Uplink PDA codes are now universal. This means any PDA can open an uplink + store, as long as they have the correct code. + type: Tweak + - message: PDA ringtones now feature more notes. + type: Tweak + id: 9615 + time: '2026-04-04T20:14:15.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38712 +- author: Naxel + changes: + - message: Heavily optimized the radiation system to prevent severe server lag when + a large number of radiation sources are active. + type: Fix + id: 9616 + time: '2026-04-05T04:53:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/39173 +- author: Princess-Cheeseballs + changes: + - message: Random pill bottles can no longer spawn with ambuzol or ambuzol+ + type: Remove + id: 9617 + time: '2026-04-05T05:36:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43458 +- author: K-Dynamic + changes: + - message: Clowns may change their loadout to start with the honkmother mitre. + type: Add + - message: Renames robes of the honkmother to honkmother coat. + type: Tweak + id: 9618 + time: '2026-04-05T10:17:20.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35237 +- author: ScarKy0 + changes: + - message: Cameras show as in-use when AI is nearby. + type: Add + - message: AI can no longer enable lights on cameras. + type: Remove + id: 9619 + time: '2026-04-05T15:17:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43466 +- author: Princess-Cheeseballs + changes: + - message: Web vests have new more visible sprites. + type: Add + id: 9620 + time: '2026-04-05T21:58:02.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43486 +- author: Princess-Cheeseballs + changes: + - message: Uplink implant now links to the same store as your PDA ringtone. + type: Add + id: 9621 + time: '2026-04-05T22:16:46.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43485 +- author: AndrewFenriz + changes: + - message: Added a wide variety of new tile recipes to the Cutter Machine. + type: Add + id: 9622 + time: '2026-04-05T22:33:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43431 +- author: psykana + changes: + - message: Nuclear operatives round end summary now shows the Disk's location. If + it wasn't atomized, that is. + type: Add + id: 9623 + time: '2026-04-06T21:41:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/39767 +- author: Princess-Cheeseballs + changes: + - message: Added a new Traitor objective to kill the Station AI! + type: Add + - message: Traitor kill objectives will now show stage names instead of character + names if applicable + type: Fix + id: 9624 + time: '2026-04-06T22:23:53.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43462 +- author: SuperGDPWYL + changes: + - message: Traitors can now be tasked with causing anomalies to go supercritical. + type: Add + id: 9625 + time: '2026-04-07T18:59:14.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43505 +- author: ketufaispikinut + changes: + - message: Added the tourist's travel camera along with very basic textual photographs. + type: Add + id: 9626 + time: '2026-04-07T20:33:23.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43322 +- author: alexalexmax + changes: + - message: Items that can be stuck to walls are now able to be unstuck properly. + type: Fix + id: 9627 + time: '2026-04-08T04:13:18.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43514 +- author: TriviaSolari + changes: + - message: The Station AI is now notified when their core is taking damage. + type: Add + id: 9628 + time: '2026-04-08T05:40:02.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43513 +- author: themias + changes: + - message: Wizards can hear the sound of their fireball spell again + type: Fix + id: 9629 + time: '2026-04-09T21:42:37.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43535 +- author: AndrewFenriz + changes: + - message: 'Added visual overlays to the janibelt for: Soap, Cleaner Grenade, Plungers, + Light Replacer, Wire Brush, Wet Floor Signs, and Holosign Projector.' + type: Add + - message: Added missing sprites for trash bags when equipped on the belt. + type: Add + id: 9630 + time: '2026-04-10T06:12:36.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43492 +- author: SirWarock + changes: + - message: Fixed the Prediction Issue when trying to forcefeed someone with a pulled-down + mask! + type: Fix + id: 9631 + time: '2026-04-10T13:24:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43543 +- author: aada + changes: + - message: The wire brush can now scrub away spiderwebs and posters. + type: Add + id: 9632 + time: '2026-04-10T16:38:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43537 +- author: ahandleman + changes: + - message: Kudzu Gene Removal Effect to Plant-B-Gone! + type: Add + - message: Removed Magic Shared Seed Kudzu Removal! + type: Fix + id: 9633 + time: '2026-04-10T20:22:18.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43385 +- author: ArtisticRoomba + changes: + - message: Fixed the pneumatic valve overshooting gas equalization between the two + connected pipenets. + type: Fix + id: 9634 + time: '2026-04-10T20:41:53.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43542 +- author: Buunie099 + changes: + - message: Revenants will passively chill their environments based on how much essence + they have + type: Add + id: 9635 + time: '2026-04-11T10:49:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43500 +- author: ProPeperos + changes: + - message: Fixed stacks of concrete and astro mowed/jungle grass splitting in to + wrong items + type: Fix + id: 9636 + time: '2026-04-11T19:05:06.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43547 +- author: BeatusCrow + changes: + - message: NPCs no longer detect invisible targets. + type: Add + id: 9637 + time: '2026-04-11T21:20:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43562 +- author: AndrewFenriz + changes: + - message: Fixed Cutter Machine rotation to be consistent with other lathes. + type: Fix + id: 9638 + time: '2026-04-11T21:35:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43494 +- author: Princess-Cheeseballs + changes: + - message: Paradox clones will no longer be slow when spawning. + type: Fix + id: 9639 + time: '2026-04-12T19:15:04.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43528 +- author: Princess-Cheeseballs + changes: + - message: Target station map at the nuclear operative outpost will now show the + target station. + type: Fix + id: 9640 + time: '2026-04-13T13:40:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43583 +- author: Glissadia + changes: + - message: The ChemMaster UI now updates automatically when reagents are dragged + and dropped into the ChemMaster. + type: Fix + id: 9641 + time: '2026-04-13T20:08:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43584 +- author: peaceful-joules + changes: + - message: Airlocks that were hacked via access breaker no longer have buggy examine + window. + type: Fix + id: 9642 + time: '2026-04-16T17:25:23.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43564 +- author: perryprog + changes: + - message: Door wire panels are now viewed via Alt + E, or via right clicking on + the door. Left clicking with a multitool or wirecutters still works as before. + Left clicking with other items now opens the door instead of the wire panel. + type: Tweak + id: 9643 + time: '2026-04-16T22:37:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/38733 +- author: 11BelowStudio + changes: + - message: Closed the legal loophole which allowed security officers to use web + vests. + type: Fix + - message: Bartenders may only use their own armour vests. + type: Tweak + id: 9644 + time: '2026-04-17T02:53:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/42862 +- author: ThatGuyUSA + changes: + - message: elite webvest has pockets again. + type: Fix + id: 9645 + time: '2026-04-17T03:41:49.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43615 +- author: jessicamaybe + changes: + - message: Changed the sound for closing airlocks. + type: Tweak + - message: Fixed door animations being interrupted during normal use. + type: Fix + id: 9646 + time: '2026-04-17T10:31:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43613 +- author: Boaz1111 + changes: + - message: Fixes the trit-frezon ratio in the guidebook being incorrect. + type: Fix + id: 9647 + time: '2026-04-17T19:51:53.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43622 +- author: piskaczek + changes: + - message: Reduced the amount of wirebrushes in the janicloset and janicrate. + type: Tweak + id: 9648 + time: '2026-04-18T10:56:44.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43629 +- author: SlamBamActionman + changes: + - message: Wizard's Knock spell no longer unlocks cryosleep units, magic lamps and + the locker trap spell. + type: Fix + id: 9649 + time: '2026-04-18T16:58:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43641 +- author: jessicamaybe + changes: + - message: Monkeys will now put people in critical condition into disposal bins + type: Add + id: 9650 + time: '2026-04-18T22:00:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/37983 +- author: Princess-Cheeseballs + changes: + - message: Gas Canisters can now explode from overpressure. + type: Add + - message: Max Caps have been entirely reworked with different fuse lengths and + explosive strengths. + type: Tweak + - message: Max Cap intensity limits have been removed. + type: Tweak + - message: Tritium fire energy production has been divided by 10, returning to its + intended value. + type: Tweak + - message: The TEG now produces 25% more power. + type: Tweak + id: 9651 + time: '2026-04-19T05:36:23.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43281 +- author: ArtisticRoomba + changes: + - message: Passive gates now have better equalization behavior and have less of + a tendency to over-equalize. + type: Fix + id: 9652 + time: '2026-04-19T05:51:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43552 +- author: AffleWaffle + changes: + - message: Intercom Assembly is now breakable. + type: Fix + id: 9653 + time: '2026-04-20T02:39:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43647 +- author: B_Kirill + changes: + - message: NPCs can now wield and activate melee weapons. + type: Add + - message: NPCs can now wield guns and close their bolts. + type: Add + - message: NPCs can now reload guns without a cartridge in the chamber, provided + there are cartridges in the clip. + type: Add + id: 9654 + time: '2026-04-21T04:37:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/40900 +- author: themias, jessicamaybe + changes: + - message: Added a traitor objective to break into letters and packages + type: Add + id: 9655 + time: '2026-04-22T00:58:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43560 +- author: themias + changes: + - message: Stunning with ninja gloves now causes the swirling stars effect on the + victim + type: Fix + id: 9656 + time: '2026-04-22T04:58:52.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43279 +- author: jkwookee + changes: + - message: Both wizard rods & normal rods now destroy full sized grilles. + type: Fix + id: 9657 + time: '2026-04-23T03:07:21.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/40714 diff --git a/Resources/Changelog/Maps.yml b/Resources/Changelog/Maps.yml index b971abf643..df01e9ce16 100644 --- a/Resources/Changelog/Maps.yml +++ b/Resources/Changelog/Maps.yml @@ -1078,4 +1078,13 @@ id: 132 time: '2026-03-24T21:43:08.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/43046 +- author: ProPeperos + changes: + - message: On Fland Evac shuttle (Delta) Added missing APC in the upper left room + type: Tweak + - message: On Fland Evac shuttle (Delta) Moved air canister connector + type: Tweak + id: 133 + time: '2026-04-06T18:34:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43481 Order: 2 diff --git a/Resources/ConfigPresets/WizardsDen/wizardsDen.toml b/Resources/ConfigPresets/WizardsDen/wizardsDen.toml index bcd4ee648f..680b5d0455 100644 --- a/Resources/ConfigPresets/WizardsDen/wizardsDen.toml +++ b/Resources/ConfigPresets/WizardsDen/wizardsDen.toml @@ -52,7 +52,7 @@ alert.min_players_sharing_connection = 2 allow_multi_server_play = false [atmos] -max_explosion_range = 10 +max_explosion_range = 0 [status] privacy_policy_link = "https://spacestation14.com/about/privacy/#game-server-privacy-policy" diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index fd60cbee29..01ff4724d6 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0-Anon, 0leshe, 0tito, 0x6273, 11BelowStudio, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 27alaing, 2DSiggy, 3nderall, 4310v343k, 4dplanner, 5tickman, 612git, 778b, 96flo, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, adm2play, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aearo-Deepwater, Aerocrux, Aeshus, Aexolott, Aexxie, AffleWaffle, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, ahandleman, Ahion, aiden, Aidenkrz, aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexalexmax, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, Alkheemist, alliephante, ALMv1, Alpaccalypse, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, Andrew-Fall, AndrewEyeke, AndrewFenriz, AndreyCamper, anri, Anzarot121, ApolloVector, Appiah, april-gras, ar4ill, Arcane-Waffle, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, areyouconfused, arimah, Arkanic, ArkiveDev, armoks, Arteben, arthropodia, ArthurMousatov, ArtisticRoomba, artur, Artxmisery, ArZarLordOfMango, as334, AshBats, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, Atakku, Ataman, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, AwareFoxy, Awlod, Axionyxx, azloserbits, AzzyIsNotHere, azzyisnothere, B-Kirill, B3CKDOOR, baa14453, BackeTako, BadaBoomie, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, bea, bebr3ght, beck-thompson, beesterman, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blitzthesquishy, Blobadoodle, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, BombasterDS, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, breeplayx3, BriBrooo, BRINGit34, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, buunie099, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, Camdot, cammusubi, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, CawsForConcern, CDWimmer, Centronias, Chaboricks, chairbender, chaisftw, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, cloudyias, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, CoolioDudio, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, CraftyRenter, crazybrain23, Crazydave91920, CrazyPhantom779, creadth, CrigCrag, CroilBird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DaCookieCakes, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dan, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkie, DaturoDewitt, david, DawBla, Daxxi3, dch-GH, ddeegan, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, debugok, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, devinschubert14, dexlerxd, dffdff2423, DieselMohawk, DieselMohawkTheSequel, digitalic, Dimastra, dimmoon1, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, Dragonjspider, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, EchoOfNothing, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, emberwinters, Emisse, emmafornash, EmoGarbage404, Endecc, EnrichedCaramel, Entvari, eoineoineoin, ephememory, eris, erohrs2, erorr404v1, Errant-4, ertanic, esguard, estacaoespacialpirata, eternally-confused, eugene, ewokswagger, exincore, exp111, f0x-n3rd, F1restar4, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, fillervk, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FlipBrooke, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Fruitsalad, Funce, FungiFellow, FunkySphere, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, GitHubUser53123, gituhabu, GlassEclipse, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, GR1231, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, happyrobot33, Hardly3D, harikattar, Hayden, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, Hi-Im-Shot, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, hoshizora-sayo, Hreno, Hrosts, htmlsystem, Huaqas, hubismal, Hugal31, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, insoPL, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, JackRyd3r, jacksonzck, JackspajfMain, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap-havok, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, JesterX666, Jewelots, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, johnjjohn, JohnJJohn, johnku1, Jophire, Jopogrechkin, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KeTuFaisPiKiNut, KIBORG04, KieueCaprie, Kimpes, kin98, KingFroozy, kipdotnet, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit, Kit0vras, KittenColony, Kittygyat, klaypexx, kleinerstation13, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kontakt, korczoczek, koteq, kotobdev, Kowlin, KrasnoshchekovPavel, Krosus777, Krunklehorn, Kryyto, Kupie, kxvvv, Kyoth25f, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, Le-Arctic-Fox, leahcat, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, LetterN, lettern, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, liem161, LightVillet, lilazero, liltenhead, linkbro1, linkuyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, lolman360, Lomcastar, Lordbrandon12, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luegamer, luizwritescode, LukaSlade, luminight, lunarcomets, Lusatia, Luxeator, lvvova1, Lyndomen, lyroth001, lyxcaster, lzimann, lzk228, M1tht1c, M3739, M4rchy-S, M87S, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, magnnusson, magnuscrowe, maland1, malchanceux, MaloTV, manelnavola, ManelNavola, Mangohydra, marboww, Markek1, MarkerWicker, marlyn, mastermiller01, matt, Matz05, max, MaxNox7, maylokana, mdrkrg, MDuch369, meara1179, meganerobot, MehimoNemo, Mehnix, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, mikeysaurus, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, misandrie, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, mnva0, moderatelyaware, modern-nm, mohamedwidar, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mqole, mr-bo-jangles, Mr0maks, MrFippik, MrPersival, mrrobdemo, mtrs163, muburu, MureixloI, murolem, murphyneko, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, NazrinNya, neborsh, nekokiwa, neomoth, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, NoreUhh, not-gavnaed, notafet, notquitehadouken, notsodana, noudoit, noverd, Nox38, NuclearWinter, Nuggets219, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, Ohelig, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnsenCapy, OnyxTheBrave, opl-, Orange-Winds, OrangeMoronage9622, OrbitSystem07, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, PAFFhassoocks, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, pavlockblaine03, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, pgraycs, PGrayCS, Pgriha, phantom-lily, Pharaz4, pheenty, philingham, Phill101, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, Pissachu, pissdemon, Pixel8-dev, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, Pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, Prole0, ProPandaBear, ProPeperos, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, Pyrovi, qrtDaniil, qrwas, Quantum-cross, quasr-9, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, Redrover1760, redspyy, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rewafflution, rhailrake, rhsvenson, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, rlebell33, RobbyTheFish, robinthedragon, robinthegirlthing, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, roryflowers, rosieposieeee, Roudenn, router, ruddygreat, rumaks-xyz, RumiTiger, Ruzihm, rwrv, S1rFl0, S1ss3l, Saakra, SabreML, Sadie-silly, saga3152, saintmuntzer, salarua, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, ScholarNZL, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, ser1-1y, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, Shegare, shepardtothestars, shibechef, Siginanto, signalsender, SignalWalker, siigiil, silicon14wastaken, Silverfur-underscore, Simyon264, sirdragooon, Sirionaut, SirWarock, Sk1tch, SkaldetSkaeg, Skarletto, skeeka-dev, skrybl, Skybailey-dev, skye, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, Smugman, SnappingOpossum, snebl, snicket, sniperchance, Snowni, snowsignal, SolidSyn, SolidusSnek, solstar2, SomegnihT, SonarZeBat, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, SpaceLizard24, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, steel, Steffo99, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, SuperGDPWYL, superjj18, Supernorn, SurrealShibe, SweetAplle, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Synthestra, Szunti, t, Tainakov, takemysoult, taonewt, tap, TaralGit, Taran, taserthefox, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telavivgamers, telyonok, temm1ie, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, thanosdegraf, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, theexetron, TheFlyingSentry, thefoty, TheGrimbeeper, TheIntoxicatedCat, thekilk, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheSecondLord, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, tornado-technology, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, Tr1bute, travis-g-reid, treytipton, TriviaSolari, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, tuchila-adi-bogdan, Tunguso4ka, TurboTrackerss14, TVK-04, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, Uberration, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, UpAndLeaves, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, verslebas, vexerot, vgskye, viceemargo, VigersRay, violet754, Visne, vitopigno, vitusveit, vlad, vlados1408, VMSolidus, vmzd, VoidMeticulous, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wachte1, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, Worldwaker, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, xsainteer, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, YuriyKiss, yuriykiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex +0-Anon, 0leshe, 0tito, 0x6273, 11BelowStudio, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 27alaing, 2DSiggy, 3nderall, 4310v343k, 4dplanner, 5tickman, 612git, 778b, 96flo, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, actioninja, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, adm2play, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aearo-Deepwater, Aerocrux, Aeshus, Aexolott, Aexxie, AffleWaffle, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, ahandleman, Ahion, aiden, aidenkrz, Aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexalexmax, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, Alkheemist, alliephante, ALMv1, Alpaccalypse, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, Andrew-Fall, AndrewEyeke, AndrewFenriz, AndreyCamper, Androclast, anri, Anzarot121, ApolloVector, Appiah, april-gras, ar4ill, Arcane-Waffle, arcanevaliance, archee1, ArchPigeon, ArchRBX, areitpog, Arendian, areyouconfused, arimah, Arkanic, ArkiveDev, armoks, Arteben, arthropodia, ArthurMousatov, ArtisticRoomba, artur, Artxmisery, ArZarLordOfMango, as334, AshBats, AsikKEsel, AsnDen, asperger-sind, aspiringLich, astriloqua, Atakku, Ataman, august-sun, AutoOtter, AverageNotDoingAnythingEnjoyer, avghdev, AwareFoxy, Awlod, Axionyxx, azloserbits, AzzyIsNotHere, azzyisnothere, B-Kirill, B3CKDOOR, baa14453, BackeTako, BadaBoomie, Bakke, BananaFlambe, Baptr0b0t, BarryNorfolk, BasedUser, baynarikattu, bea, BeatusCrow, bebr3ght, beck-thompson, beesterman, bellwetherlogic, ben, benbryant0, benev0, benjamin-burges, BGare, bhespiritu, bibbly, BigfootBravo, BIGZi0348, bingojohnson, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blitzthesquishy, Blobadoodle, bloodrizer, Bloody2372, blueDev2, Boaz1111, BobdaBiscuit, BobTheSleder, boiled-water-tsar, Bokser815, bolantej, BombasterDS, Booblesnoot42, Boolean-Buckeye, botanySupremist, brainfood1183, BramvanZijp, Brandon-Huu, breeplayx3, BriBrooo, BRINGit34, brndd, bryce0110, BubblegumBlue, buletsponge, buntobaggins, buunie099, bvelliquette, BWTCK, byondfuckery, c0rigin, c4llv07e, CaasGit, Caconym27, Calecute, Callmore, Camdot, cammusubi, capnsockless, CaptainMaru, captainsqrbeard, Carbonhell, Carolyn3114, Carou02, carteblanche4me, catdotjs, catlord, Catofquestionableethics, CatTheSystem, CawsForConcern, CDWimmer, Centronias, Chaboricks, chairbender, chaisftw, Chaoticaa, Charlese2, charlie, chartman, ChaseFlorom, chavonadelal, Cheackraze, CheddaCheez, cheesePizza2, CheesePlated, Chief-Engineer, chillyconmor, christhirtle, chromiumboy, Chronophylos, Chubbicous, Chubbygummibear, Ciac32, ciaran, citrea, civilCornball, claustro305, Clement-O, cloudyias, clyf, Clyybber, CMDR-Piboy314, cnv41, coco, cohanna, Cohnway, Cojoke-dot, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, Compilatron144, CookieMasterT, coolboy911, CoolioDudio, coolmankid12345, Coolsurf6, cooperwallace, corentt, CormosLemming, CrafterKolyan, CraftyRenter, crazybrain23, Crazydave91920, CrazyPhantom779, creadth, CrigCrag, CroilBird, Crotalus, CrudeWax, cryals, CrzyPotato, cubixthree, cutemoongod, Cyberboss, d34d10cc, DaCookieCakes, DadeKuma, Daemon, daerSeebaer, dahnte, dakamakat, DamianX, dangerrevolution, daniel-cr, DanSAussieITS, Daracke, Darkie, DaturoDewitt, david, DawBla, Daxxi3, dch-GH, ddeegan, de0rix, Deahaka, dean, DEATHB4DEFEAT, Deatherd, deathride58, debugok, Decappi, Decortex, Deeeeja, deepdarkdepths, DeepwaterCreations, Deerstop, degradka, Delete69, deltanedas, DenisShvalov, DerbyX, derek, dersheppard, Deserty0, Detintinto, DevilishMilk, devinschubert14, dexlerxd, dffdff2423, DieselMohawk, DieselMohawkTheSequel, digitalic, Dimastra, dimmoon1, DinnerCalzone, DinoWattz, Disp-Dev, DisposableCrewmember42, dissidentbullet, DjfjdfofdjfjD, doc-michael, docnite, Doctor-Cpu, DogZeroX, dolgovmi, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DR-DOCTOR-EVIL-EVIL, Dragonjspider, dragonryan06, drakewill-CRL, Drayff, dreamlyjack, DrEnzyme, dribblydrone, DrMelon, drongood12, DrSingh, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, DuckManZach, Duddino, dukevanity, duskyjay, Dutch-VanDerLinde, dvir001, dylanstrategie, dylanwhittingham, Dynexust, Easypoller, echo, EchoOfNothing, eclips_e, eden077, EEASAS, Efruit, efzapa, Ekkosangen, ElectroSR, elsie, elthundercloud, Elysium206, emberwinters, Emisse, emmafornash, EmoGarbage404, Endecc, EnrichedCaramel, Entvari, eoineoineoin, ephememory, eris, erohrs2, erorr404v1, Errant-4, ertanic, esguard, estacaoespacialpirata, eternally-confused, eugene, eveloop, ewokswagger, exincore, exp111, f0x-n3rd, F1restar4, FacePluslll, Fahasor, FairlySadPanda, farrellka-dev, FATFSAAM2, Feluk6174, ficcialfaint, Fiftyllama, Fildrance, fillervk, FinnishPaladin, firenamefn, Firewars763, FirinMaLazors, Fishfish458, fl-oz, Flareguy, flashgnash, FlipBrooke, FluffiestFloof, FluffMe, FluidRock, flymo5678, foboscheshir, FoLoKe, fooberticus, ForestNoises, forgotmyotheraccount, forkeyboards, forthbridge, Fortune117, foxhorn, freeman2651, freeze2222, frobnic8, Froffy025, Fromoriss, froozigiusz, FrostMando, FrostRibbon, Fruitsalad, Funce, FungiFellow, FunkySphere, FunTust, Futuristic-OK, GalacticChimp, gamer3107, Gamewar360, gansulalan, GaussiArson, Gaxeer, gbasood, gcoremans, Geekyhobo, gem, genderGeometries, GeneralGaws, Genkail, Gentleman-Bird, geraeumig, Ghagliiarghii, Git-Nivrak, githubuser508, GitHubUser53123, gituhabu, GlassEclipse, Glissadia, GnarpGnarp, GNF54, godisdeadLOL, goet, GoldenCan, Goldminermac, Golinth, golubgik, GoodWheatley, Gorox221, GR1231, gradientvera, graevy, GraniteSidewalk, GreaseMonk, greenrock64, GreyMario, GrownSamoyedDog, GTRsound, gusxyz, Gyrandola, h3half, hamurlik, Hanzdegloker, HappyRoach, happyrobot33, Hardly3D, harikattar, Hayden, he1acdvv, Hebi, Helix-ctrl, helm4142, Henry, HerCoyote23, Hi-Im-Shot, HighTechPuddle, Hitlinemoss, hiucko, hivehum, Hmeister-fake, Hmeister-real, Hobbitmax, hobnob, HoidC, Holinka4ever, holyssss, HoofedEar, Hoolny, hord-brayden, hoshizora-sayo, Hreno, Hrosts, htmlsystem, Huaqas, hubismal, Hugal31, Hyenh, hyperb1, hyperDelegate, hyphenationc, i-justuser-i, iaada, iacore, IamVelcroboy, Ian321, icekot8, icesickleone, iczero, iglov, IgorAnt028, igorsaux, ike709, illersaver, Illiux, Ilushkins33, Ilya246, IlyaElDunaev, imatsoup, IMCB, impubbi, imrenq, imweax, indeano, Injazz, Insineer, insoPL, IntegerTempest, Interrobang01, Intoxicating-Innocence, IProduceWidgets, itsmethom, Itzbenz, iztokbajcar, Jackal298, Jackrost, JackRyd3r, jacksonzck, JackspajfMain, Jacktastic09, Jackw2As, jacob, jamessimo, janekvap-havok, Jark255, Jarmer123, Jaskanbe, JasperJRoth, jbox144, JCGWE30, jerryimmouse, JerryImMouse, Jessetriesagain, jessicamaybe, JesterX666, Jewelots, Jezithyr, jicksaw, JiimBob, JimGamemaster, jimmy12or, JIPDawg, jjtParadox, jkwookee, jmcb, JohnGinnane, JohnJJohn, johnjjohn, johnku1, Jophire, Jopogrechkin, joshepvodka, JpegOfAFrog, jproads, JrInventor05, Jrpl, jukereise, juliangiebel, JustArt1m, JustCone14, justdie12, justin, justintether, JustinTrotter, JustinWinningham, justtne, K-Dynamic, k3yw, Kadeo64, Kaga-404, kaiserbirch, KaiShibaa, kalane15, kalanosh, KamTheSythe, Kanashi-Panda, katzenminer, kbailey-git, Keelin, Keer-Sar, KEEYNy, keikiru, Kelrak, kerisargit, keronshb, KeTuFaisPiKiNut, KIBORG04, KieueCaprie, Kimpes, kin98, KingFroozy, kipdotnet, kira-er, kiri-yoshikage, Kirillcas, Kirus59, Kistras, Kit, Kit0vras, KittenColony, Kittygyat, klaypexx, kleinerstation13, Kmc2000, Ko4ergaPunk, kognise, kokoc9n, komunre, KonstantinAngelov, kontakt, korczoczek, koteq, kotobdev, Kowlin, KrasnoshchekovPavel, kresny, Krosus777, Krunklehorn, Kryyto, Kupie, kxvvv, Kyoth25f, kyupolaris, kzhanik, LaCumbiaDelCoronavirus, lajolico, Lamrr, lanedon, LankLTE, laok233, lapatison, larryrussian, lawdog4817, Lazzi0706, Le-Arctic-Fox, leahcat, leander-0, leonardo-dabepis, leonidussaks, leonsfriedrich, LeoSantich, lettern, LetterN, Level10Cybermancer, LEVELcat, lever1209, LevitatingTree, Lgibb18, lgruthes, liem161, LightVillet, lilazero, liltenhead, linkbro1, LinkUyx, Litraxx, little-meow-meow, LittleBuilderJane, LittleNorthStar, LittleNyanCat, lizelive, ljm862, lmsnoise, localcc, lokachop, lolman360, Lomcastar, Lordbrandon12, LordCarve, LordEclipse, lucas, LucasTheDrgn, luckyshotpictures, LudwigVonChesterfield, luegamer, luizwritescode, LukaSlade, luminight, lunarcomets, Lusatia, Luxeator, lvvova1, Lyndomen, lyroth001, lyxcaster, lzimann, lzk228, M1tht1c, M3739, M4rchy-S, M87S, mac6na6na, MACMAN2003, Macoron, magicalus, magmodius, magnnusson, magnuscrowe, maland1, malchanceux, MaloTV, ManelNavola, manelnavola, Mangohydra, marboww, Markek1, MarkerWicker, marlyn, mastermiller01, matt, Matz05, max, MaxNox7, maylokana, mdrkrg, MDuch369, meara1179, meganerobot, MehimoNemo, Mehnix, MeltedPixel, memeproof, MendaxxDev, Menshin, Mephisto72, MerrytheManokit, Mervill, metalgearsloth, MetalSage, MFMessage, mhamsterr, michaelcu, micheel665, mifia, mikeysaurus, MilenVolf, MilonPL, Minemoder5000, Minty642, minus1over12, Mirino97, mirrorcult, MishaUnity, MissKay1994, MisterImp, MisterMecky, Mith-randalf, Mixelz, mjarduk, MjrLandWhale, mkanke-real, MLGTASTICa, mnva0, moderatelyaware, modern-nm, mohamedwidar, mokiros, momo, Moneyl, monotheonist, Moomoobeef, moony, Morb0, MossyGreySlope, mqole, mr-bo-jangles, Mr0maks, MrFippik, MrPersival, mrrobdemo, mtrs163, muburu, MureixloI, murolem, murphyneko, musicmanvr, MWKane, Myakot, Myctai, N3X15, nabegator, nails-n-tape, Nairodian, Naive817, NakataRin, namespace-Memory, Nannek, naxel11, NazrinNya, neborsh, nekokiwa, neomoth, neutrino-laser, NickPowers43, nikitosych, nikthechampiongr, Nimfar11, ninruB, Nirnael, NIXC, nkokic, NkoKirkto, nmajask, noctyrnal, noelkathegod, noirogen, nok-ko, NonchalantNoob, NoobyLegion, Nopey, NoreUhh, Not-A-Chair, not-gavnaed, NotActuallyMarty, notafet, notquitehadouken, notsodana, noudoit, noverd, Nox38, NuclearWinter, Nuggets219, nukashimika, nuke-haus, NULL882, nullarmo, nyeogmi, Nylux, Nyranu, Nyxilath, och-och, OctoRocket, Ohelig, OldDanceJacket, OliverOtter, onesch, OneZerooo0, OnsenCapy, OnyxTheBrave, opl-, Orange-Winds, OrangeMoronage9622, OrbitSystem07, Orsoniks, osjarw, Ostaf, othymer, OttoMaticode, Owai-Seek, packmore, PAFFhassoocks, paige404, paigemaeforrest, pali6, Palladinium, Pangogie, panzer-iv1, partyaddict, patrikturi, PaulRitter, pavlockblaine03, peccneck, Peptide90, peptron1, perryprog, PeterFuto, PetMudstone, pewter-wiz, pgraycs, PGrayCS, Pgriha, phantom-lily, Pharaz4, philingham, Phill101, Phonix, Phooooooooooooooooooooooooooooooosphate, phunnyguy, PicklOH, PilgrimViis, Pill-U, pinkbat5, Piras314, Pireax, piskaczek, Pissachu, pissdemon, Pixel8-dev, PixeltheAertistContrib, PixelTheKermit, PJB3005, Plasmaguy, plinyvic, Plykiya, poeMota, pofitlo, pointer-to-null, Pok27, poklj, PolterTzi, PoorMansDreams, PopGamer45, portfiend, potato1234x, PotentiallyTom, PotRoastPiggy, Princess-Cheeseballs, ProfanedBane, Prole0, ProPandaBear, ProPeperos, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykana, psykzz, PuceTint, pumkin69, PuroSlavKing, PursuitInAshes, Putnam3145, py01, Pyrovi, qrtDaniil, qrwas, Quantum-cross, quasr-9, quatre, QueerNB, QuietlyWhisper, qwerltaz, Radezolid, RadioMull, Radosvik, Radrark, Rainbeon, Rainfey, RainyGale, Raitononai, Ramlik, RamZ, randy10122, Rane, Ranger6012, Rapidgame7, ravage123321, rbertoche, RedBookcase, Redfire1331, Redict, RedlineTriad, redmushie, RednoWCirabrab, Redrover1760, redspyy, ReeZer2, RemberBM, RemieRichards, RemTim, rene-descartes2021, Renlou, retequizzle, rewafflution, rhailrake, rhsvenson, rich-dunne, RieBi, riggleprime, RIKELOLDABOSS, rinary1, Rinkashikachi, riolume, rlebell33, RobbyTheFish, robinthedragon, robinthegirlthing, Rockdtben, Rohesie, rok-povsic, rokudara-sen, rolfero, RomanNovo, roryflowers, rosieposieeee, Roudenn, router, ruddygreat, rumaks-xyz, RumiTiger, Ruzihm, rwrv, S1rFl0, S1ss3l, Saakra, SabreML, Sadie-silly, saga3152, saintmuntzer, salarua, Salex08, sam, samgithubaccount, Samuka-C, SaphireLattice, SapphicOverload, sarahon, sativaleanne, SaveliyM360, sBasalto, ScalyChimp, ScarKy0, ScholarNZL, schrodinger71, scrato, Scribbles0, scrivoy, scruq445, scuffedjays, ScumbagDog, SeamLesss, Segonist, semensponge, sephtasm, ser1-1y, Serkket, sewerpig, SG6732, sh18rw, Shaddap1, ShadeAware, ShadowCommander, shadowtheprotogen546, shaeone, shampunj, shariathotpatrol, SharkSnake98, Shegare, shepardtothestars, shibechef, Siginanto, signalsender, SignalWalker, siigiil, silicon14wastaken, Silverfur-underscore, Simyon264, sirdragooon, Sirionaut, SirWarock, Sk1tch, SkaldetSkaeg, Skarletto, skeeka-dev, skrybl, Skybailey-dev, skye, Skyedra, SlamBamActionman, slarticodefast, Slava0135, sleepyyapril, slimmslamm, Slyfox333, Smugman, SnappingOpossum, snebl, snicket, sniperchance, Snowni, snowsignal, SolidSyn, SolidusSnek, solstar2, SomegnihT, SonarZeBat, SonicHDC, SoulFN, SoulSloth, Soundwavesghost, soupkilove, southbridge-fur, sowelipililimute, Soydium, SpaceLizard24, SpaceLizardSky, SpaceManiac, SpaceRox1244, SpaceyLady, Spangs04, spanky-spanky, Sparlight, spartak, SpartanKadence, spderman3333, SpeltIncorrectyl, Spessmann, SphiraI, SplinterGP, spoogemonster, sporekto, sporkyz, ssdaniel24, stalengd, stanberytrask, Stanislav4ix, StanTheCarpenter, starbuckss14, Stealthbomber16, steel, Steffo99, stellar-novas, stewie523, stomf, Stop-Signs, stopbreaking, stopka-html, StrawberryMoses, Stray-Pyramid, strO0pwafel, Strol20, StStevens, Subversionary, sunbear-dev, SuperGDPWYL, superjj18, Supernorn, SurrealShibe, SweetAplle, SweptWasTaken, SyaoranFox, Sybil, SYNCHRONIC, Synthestra, Szunti, t, Tainakov, takemysoult, taonewt, tap, TaralGit, Taran, taserthefox, taurie, Tayrtahn, tday93, teamaki, TeenSarlacc, TekuNut, telavivgamers, telyonok, temm1ie, TemporalOroboros, tentekal, terezi4real, Terraspark4941, texcruize, Tezzaide, TGODiamond, TGRCdev, tgrkzus, thanosdegraf, ThatGuyUSA, ThatOneGoblin25, thatrandomcanadianguy, TheArturZh, TheBlueYowie, thecopbennet, TheCze, TheDarkElites, thedraccx, TheEmber, theexetron, TheFlyingSentry, thefoty, TheGrimbeeper, TheIntoxicatedCat, thekilk, themias, theomund, TheProNoob678, TherapyGoth, ThereDrD0, TheSecondLord, TheShuEd, thetolbean, thevinter, TheWaffleJesus, Thinbug0, ThunderBear2006, timothyteakettle, TimrodDX, timurjavid, tin-man-tim, TiniestShark, Titian3, tk-a369, tkdrg, tmtmtl30, ToastEnjoyer, Toby222, TokenStyle, Tollhouse, Toly65, tom-leys, tomasalves8, Tomeno, Tonydatguy, topy, tornado-technology, TornadoTechnology, tosatur, TotallyLemon, ToxicSonicFan04, Tr1bute, travis-g-reid, treytipton, TriviaSolari, trixxedbit, TrixxedHeart, tropicalhibi, truepaintgit, Truoizys, Tryded, TsjipTsjip, tuchila-adi-bogdan, Tunguso4ka, TurboTrackerss14, TVK-04, tyashley, Tyler-IN, TytosB, Tyzemol, UbaserB, Uberration, ubis1, UBlueberry, uhbg, UKNOWH, UltimateJester, Unbelievable-Salmon, underscorex5, UnicornOnLSD, Unisol, Unkn0wnGh0st333, unusualcrow, UpAndLeaves, Uriende, UristMcDorf, user424242420, Utmanarn, Vaaankas, valentfingerov, valquaint, VanderslootAssgiraffe, Varen, Vasilis, VasilisThePikachu, veliebm, Velken, VelonacepsCalyxEggs, veprolet, VerinSenpai, veritable-calamity, Veritius, Vermidia, vero5123, verslebas, vexerot, viceemargo, VigersRay, violet754, Visne, vitopigno, vitusveit, vlad, vlados1408, VMSolidus, vmzd, VoidMeticulous, voidnull000, volotomite, volundr-, Voomra, Vordenburg, vorkathbruh, Vortebo, vulppine, wachte1, wafehling, walksanatora, Warentan, WarMechanic, Watermelon914, weaversam8, wertanchik, whateverusername0, whatston3, widgetbeck, Will-Oliver-Br, Willhelm53, WilliamECrew, willicassi, Winkarst-cpu, wirdal, wixoaGit, WlarusFromDaSpace, Wolfkey-SomeoneElseTookMyUsername, Worldwaker, wrexbe, wtcwr68, xeri7, xkreksx, xprospero, xRiriq, xsainteer, YanehCheck, yathxyz, Ygg01, YotaXP, youarereadingthis, YoungThugSS14, Yousifb26, youtissoum, yunii, yuriykiss, YuriyKiss, zach-hill, Zadeon, Zalycon, zamp, Zandario, Zap527, Zealith-Gamer, zekins3366, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex diff --git a/Resources/Locale/en-US/administration/commands/add-uplink-command.ftl b/Resources/Locale/en-US/administration/commands/add-uplink-command.ftl index 40d0c5fa1b..a0cee58050 100644 --- a/Resources/Locale/en-US/administration/commands/add-uplink-command.ftl +++ b/Resources/Locale/en-US/administration/commands/add-uplink-command.ftl @@ -5,4 +5,6 @@ add-uplink-command-completion-1 = Username (defaults to self) add-uplink-command-completion-2 = Uplink uid (default to PDA) add-uplink-command-completion-3 = Is uplink discount enabled add-uplink-command-error-1 = Selected player doesn't control any entity -add-uplink-command-error-2 = Failed to add uplink to the player \ No newline at end of file +add-uplink-command-error-2 = Failed to add uplink to the player +add-uplink-command-success-pda = Uplink added to player PDA with code {$code} +add-uplink-command-success-implant = Uplink added to player as an implant diff --git a/Resources/Locale/en-US/administration/smites.ftl b/Resources/Locale/en-US/administration/smites.ftl index 0702afb33c..2de4e79081 100644 --- a/Resources/Locale/en-US/administration/smites.ftl +++ b/Resources/Locale/en-US/administration/smites.ftl @@ -107,7 +107,6 @@ admin-smite-disarm-prone-description = Makes them get disarmed 100% of the time admin-smite-garbage-can-description = Turn them into a garbage bin to emphasize what they remind you of. admin-smite-super-bonk-description = Slams them on every single table on the Station and beyond. admin-smite-super-bonk-lite-description= Slams them on every single table on the Station and beyond. Stops when the target is dead. -admin-smite-terminate-description = Creates a Terminator ghost role with the sole objective of killing them. admin-smite-super-slip-description = Slips them really, really hard. admin-smite-omni-accent-description = Makes the target speak with almost every accent available. admin-smite-crawler-description = Makes the target fall down and be unable to stand up. Remove their hands too for added effect! diff --git a/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl b/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl index a2cb5301b2..57ed361248 100644 --- a/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl +++ b/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl @@ -11,7 +11,6 @@ gas-analyzer-window-tab-title-capitalized = {CAPITALIZE($title)} gas-analyzer-window-refresh-button = Refresh gas-analyzer-window-no-data = No Data gas-analyzer-window-no-gas-text = No Gases -gas-analyzer-window-error-text = Error: {$errorText} gas-analyzer-window-volume-text = Volume: gas-analyzer-window-volume-val-text = {$volume} L gas-analyzer-window-pressure-text = Pressure: diff --git a/Resources/Locale/en-US/atmos/gases.ftl b/Resources/Locale/en-US/atmos/gases.ftl index 5c540c46df..b5d9ed6aae 100644 --- a/Resources/Locale/en-US/atmos/gases.ftl +++ b/Resources/Locale/en-US/atmos/gases.ftl @@ -1,10 +1,18 @@ -gas-ammonia-abbreviation = NH₃ -gas-carbon-dioxide-abbreviation = CO₂ -gas-frezon-abbreviation = F -gas-nitrogen-abbreviation = N₂ -gas-nitrous-oxide-abbreviation = N₂O +gas-oxygen = Oxygen gas-oxygen-abbreviation = O₂ +gas-nitrogen = Nitrogen +gas-nitrogen-abbreviation = N₂ +gas-carbon-dioxide = Carbon Dioxide +gas-carbon-dioxide-abbreviation = CO₂ +gas-plasma = Plasma gas-plasma-abbreviation = P +gas-tritium = Tritium gas-tritium-abbreviation = T +gas-water-vapor = Water Vapor gas-water-vapor-abbreviation = H₂O -gas-unknown-abbreviation = X +gas-ammonia = Ammonia +gas-ammonia-abbreviation = NH₃ +gas-nitrous-oxide = Nitrous Oxide +gas-nitrous-oxide-abbreviation = N₂O +gas-frezon = Frezon +gas-frezon-abbreviation = F diff --git a/Resources/Locale/en-US/cartridge-loader/cartridges.ftl b/Resources/Locale/en-US/cartridge-loader/cartridges.ftl index 11b2fb9402..7dd7c779ac 100644 --- a/Resources/Locale/en-US/cartridge-loader/cartridges.ftl +++ b/Resources/Locale/en-US/cartridge-loader/cartridges.ftl @@ -7,6 +7,7 @@ news-read-program-name = Station news crew-manifest-program-name = Crew manifest crew-manifest-cartridge-loading = Loading ... +crew-manifest-cartridge-loading-failed = Failed to load crew manifest! net-probe-program-name = NetProbe net-probe-scan = Scanned {$device}! diff --git a/Resources/Locale/en-US/changeling/changeling.ftl b/Resources/Locale/en-US/changeling/changeling.ftl index 57ad3550bf..4c639ec77b 100644 --- a/Resources/Locale/en-US/changeling/changeling.ftl +++ b/Resources/Locale/en-US/changeling/changeling.ftl @@ -1,6 +1,11 @@ -roles-antag-changeling-name = Changeling +# antag selection +roles-antag-changeling-name = Changeling roles-antag-changeling-objective = A intelligent predator that assumes the identities of its victims. +# devour +changeling-devour-attempt-failed-cannot-devour = We cannot devour this! +changeling-devour-attempt-failed-already-devoured = We already consumed this body! +changeling-devour-attempt-failed-not-dead = This body yet lives! We cannot consume it alive! changeling-devour-attempt-failed-rotting = This corpse has only rotted biomass. changeling-devour-attempt-failed-protected = This victim's biomass is protected by armor! @@ -8,12 +13,23 @@ changeling-devour-begin-windup-self = Our uncanny mouth reveals itself with othe changeling-devour-begin-windup-others = { CAPITALIZE(POSS-ADJ($user)) } uncanny mouth reveals itself with otherworldly hunger. changeling-devour-begin-consume-self = The uncanny mouth digs deep into its victim. changeling-devour-begin-consume-others = { CAPITALIZE(POSS-ADJ($user)) } uncanny mouth digs deep into { POSS-ADJ($user) } victim. - -changeling-devour-consume-failed-not-dead = This body yet lives! We cannot consume it alive! changeling-devour-consume-complete-self = Our uncanny mouth retreats, biomass consumed. changeling-devour-consume-complete-others = { CAPITALIZE(POSS-ADJ($user)) } uncanny mouth retreats. +# transformation changeling-transform-attempt-self = Our bones snap, muscles tear, one flesh becomes another. changeling-transform-attempt-others = { CAPITALIZE(POSS-ADJ($user)) } bones snap, muscles tear, body shifts into another. +changeling-flesh-clothing-removed-popop = {CAPITALIZE(THE($item))} falls apart into fleshy remains! +changeling-flesh-clothing-examine-wearer = [color=crimson]This item is a camouflaged part of your body. It will disappear if you unequip it![/color] +changeling-flesh-clothing-alert-name = Flesh Clothing Ability +changeling-flesh-clothing-alert-desc = Whether clothing transformation is enabled. Click to toggle. +# transformation BUI +changeling-transform-bui-select-entity = {$entity} +changeling-transform-bui-drop-identity-menu = Drop a devoured identity from your memory. +changeling-transform-bui-drop-identity-entity = Drop {$entity} +changeling-transform-bui-drop-identity-entity-popup = You dropped {$entity} from your memory. +changeling-transform-bui-drop-identity-cannot-drop = You cannot drop your current identity. + +# other changeling-paused-map-name = Changeling identity storage map diff --git a/Resources/Locale/en-US/chat/emotes.ftl b/Resources/Locale/en-US/chat/emotes.ftl index cabb808728..c71fade285 100644 --- a/Resources/Locale/en-US/chat/emotes.ftl +++ b/Resources/Locale/en-US/chat/emotes.ftl @@ -58,7 +58,7 @@ chat-emote-msg-salute = salutes. chat-emote-msg-gasp = gasps. chat-emote-msg-deathgasp = seizes up and falls limp, {POSS-ADJ($entity)} eyes dead and lifeless... chat-emote-msg-deathgasp-monkey = lets out a faint chimper as {SUBJECT($entity)} collapses and stops moving... -chat-emote-msg-deathgasp-scurret = lets out a final 'wa' and falls still... +chat-emote-msg-deathgasp-scurret = lets out a final ‘wa’ and falls still... chat-emote-msg-buzz = buzzes! chat-emote-msg-weh = wehs! chat-emote-msg-hew = hews! diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 93f149bed9..5cc28824ea 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -22,11 +22,11 @@ chat-manager-server-wrap-message = [bold]{$message}[/bold] chat-manager-sender-announcement = Central Command chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12] {$message}[/bold][/font] -chat-manager-entity-say-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent]{$message}[/BubbleContent]"[/font] -chat-manager-entity-say-bold-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent][bold]{$message}[/bold][/BubbleContent]"[/font] +chat-manager-entity-say-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]“[BubbleContent]{$message}[/BubbleContent]”[/font] +chat-manager-entity-say-bold-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]“[BubbleContent][bold]{$message}[/bold][/BubbleContent]”[/font] -chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers,"[BubbleContent]{$message}[/BubbleContent]"[/italic][/font] -chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, "[BubbleContent]{$message}[/BubbleContent]"[/italic][/font] +chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers,“[BubbleContent]{$message}[/BubbleContent]”[/italic][/font] +chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, “[BubbleContent]{$message}[/BubbleContent]”[/italic][/font] # THE() is not used here because the entity and its name can technically be disconnected if a nameOverride is passed... chat-manager-entity-me-wrap-message = [italic]{ PROPER($entity) -> diff --git a/Resources/Locale/en-US/clothing/components/fleeting-clothing-coponent.ftl b/Resources/Locale/en-US/clothing/components/fleeting-clothing-coponent.ftl new file mode 100644 index 0000000000..f66a8a012a --- /dev/null +++ b/Resources/Locale/en-US/clothing/components/fleeting-clothing-coponent.ftl @@ -0,0 +1,2 @@ +fleeting-clothing-component-default-popup = {CAPITALIZE(THE($item))} crumbles into dust. +fleeting-clothing-component-default-examine = This is a fleeting item. It will diseappear when unequipped. diff --git a/Resources/Locale/en-US/commands/toolshed/container-command.ftl b/Resources/Locale/en-US/commands/toolshed/container-command.ftl new file mode 100644 index 0000000000..077c1439a3 --- /dev/null +++ b/Resources/Locale/en-US/commands/toolshed/container-command.ftl @@ -0,0 +1,14 @@ +command-description-container-contents = + Gets all entities inside a container on an entity via the container's ID. +command-description-container-get = + Gets a container on an entity via the container's ID. +command-description-container-insert = + Puts an entity inside the piped container. +command-description-container-insertmultiple = + Put multiple entities inside the piped container. +command-description-container-list = + Gets the IDs of all containers in an entity. +command-description-container-getall = + Gets all containers in an entity. +command-description-container-id = + Gets the string id of the piped in containers. diff --git a/Resources/Locale/en-US/commands/toolshed/inventory-command.ftl b/Resources/Locale/en-US/commands/toolshed/inventory-command.ftl index f430197495..667db5166f 100644 --- a/Resources/Locale/en-US/commands/toolshed/inventory-command.ftl +++ b/Resources/Locale/en-US/commands/toolshed/inventory-command.ftl @@ -18,5 +18,5 @@ command-description-inventory-ensure = Puts a given entity on the first piped entity that has a slot matching the given flag if none exists, passing through the UID of whatever is in the slot by the end. command-description-inventory-ensurespawn = Spawns a given prototype on the first piped entity that has a slot matching the given flag if none exists, passing through the UID of whatever is in the slot by the end. -command-description-inventory-query = +command-description-inventory-contents = Gets the entities in the inventory slots of the piped entities and passes them along. diff --git a/Resources/Locale/en-US/commands/toolshed/storage-command.ftl b/Resources/Locale/en-US/commands/toolshed/storage-command.ftl index 154196fbb1..c912df4d76 100644 --- a/Resources/Locale/en-US/commands/toolshed/storage-command.ftl +++ b/Resources/Locale/en-US/commands/toolshed/storage-command.ftl @@ -2,5 +2,5 @@ command-description-storage-fasttake = Takes the most recently placed item from the piped storage entity. command-description-storage-insert = Inserts the piped entity into the given storage entity. -command-description-storage-query = +command-description-storage-contents = Gets the entities in the storagebase of the piped entities and passes them along. diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl index 1343aaec7e..da1794badb 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl @@ -1,4 +1,4 @@ -nukeops-title = Nuclear Operatives +nukeops-title = Nuclear Operatives nukeops-description = Nuclear operatives have targeted the station. Try to keep them from arming and detonating the nuke by protecting the nuke disk! nukeops-welcome = @@ -24,6 +24,17 @@ nukeops-cond-allnukiesdead = All nuclear operatives have died. nukeops-cond-somenukiesalive = Some nuclear operatives died. nukeops-cond-allnukiesalive = No nuclear operatives died. +nukeops-disk-location-title = Final location of Disk: +nukeops-disk-carried-by = {" "}carried by [color=White]{$name}[/color], [color=orange]{$job}[/color], {$location} { $user -> + [unknown] { "" } + *[other] ([color=gray]{$user}[/color]) +} + +storage-hierarchy-list = { $items-left -> + [0] { $existing-text } { $item }, + *[other] { $existing-text } { $item }, in +} + nukeops-list-start = The nuclear operatives were: nukeops-list-name = - [color=White]{$name}[/color] nukeops-list-name-user = - [color=White]{$name}[/color] ([color=gray]{$user}[/color]) diff --git a/Resources/Locale/en-US/gases/gas-tank.ftl b/Resources/Locale/en-US/gases/gas-tank.ftl new file mode 100644 index 0000000000..03af8cb7a3 --- /dev/null +++ b/Resources/Locale/en-US/gases/gas-tank.ftl @@ -0,0 +1 @@ +gas-max-pressure-alert = The pressure relief valve bursts open! diff --git a/Resources/Locale/en-US/gases/gases.ftl b/Resources/Locale/en-US/gases/gases.ftl deleted file mode 100644 index e41aa4fc99..0000000000 --- a/Resources/Locale/en-US/gases/gases.ftl +++ /dev/null @@ -1,9 +0,0 @@ -gases-oxygen = Oxygen -gases-nitrogen = Nitrogen -gases-co2 = Carbon Dioxide -gases-plasma = Plasma -gases-tritium = Tritium -gases-water-vapor = Water Vapor -gases-ammonia = Ammonia -gases-n2o = Nitrous Oxide -gases-frezon = Frezon diff --git a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl index 3a453b3404..6e7fae92ae 100644 --- a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl +++ b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl @@ -495,6 +495,12 @@ entity-effect-guidebook-plant-phalanximine = *[other] restore } viability to a plant rendered nonviable by a mutation +entity-effect-guidebook-plant-remove-kudzu = + { $chance -> + [1] Removes + *[other] remove + } kudzu weed growth from a plant + entity-effect-guidebook-plant-diethylamine = { $chance -> [1] Increases diff --git a/Resources/Locale/en-US/headset/headset-component.ftl b/Resources/Locale/en-US/headset/headset-component.ftl index d61fb8edb2..f9f9c17411 100644 --- a/Resources/Locale/en-US/headset/headset-component.ftl +++ b/Resources/Locale/en-US/headset/headset-component.ftl @@ -1,6 +1,6 @@ # Chat window radio wrap (prefix and postfix) -chat-radio-message-wrap = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}]"{$message}"[/font][/color] -chat-radio-message-wrap-bold = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}][bold]"{$message}"[/bold][/font][/color] +chat-radio-message-wrap = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}]“{$message}”[/font][/color] +chat-radio-message-wrap-bold = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}][bold]“{$message}”[/bold][/font][/color] examine-headset-default-channel = Use {$prefix} for the default channel ([color={$color}]{$channel}[/color]). diff --git a/Resources/Locale/en-US/hijack-beacon/hijack-beacon.ftl b/Resources/Locale/en-US/hijack-beacon/hijack-beacon.ftl new file mode 100644 index 0000000000..a98370e891 --- /dev/null +++ b/Resources/Locale/en-US/hijack-beacon/hijack-beacon.ftl @@ -0,0 +1,16 @@ +hijack-beacon-announcement-sender = Automated Trade Station +hijack-beacon-announcement-activated = Attention! An Attempted Breach of the Automated Trade Station's firewall has been detected! Estimated {$time} seconds until firewall breach! +hijack-beacon-announcement-deactivated = Firewall breach failed. Firewall integrity partially restored. Have a nice day! +hijack-beacon-announcement-success = Successfully disengaged Automated Trade Station firewall. {$fine} spessos has been transferred from station funds to [%ERROR%]. Your trade station warranty is now void. This incident has been reported. + +hijack-beacon-examine-await-activate = The beacon is [color=green]ready to activate[/color]. +hijack-beacon-examine-await-cooldown = The beacon is [color=red]on cooldown[/color]. +hijack-beacon-examine-await-hijack-complete = The beacon is [color=red]spent[/color]. + +hijack-beacon-popup-anchor = The beacon anchors itself into the ground! +hijack-beacon-popup-unanchor = The beacon unanchors itself from the ground. + +hijack-beacon-verb-activate-text = Activate +hijack-beacon-verb-activate-message = The beacon can only be armed on the Automated Trade Station, on an unoccupied tile. +hijack-beacon-verb-deactivate-text = Deactivate +hijack-beacon-verb-deactivate-message = The beacon isn't going to deactivate itself, you know. diff --git a/Resources/Locale/en-US/lathe/lathe-categories.ftl b/Resources/Locale/en-US/lathe/lathe-categories.ftl index 209daf1ad3..54841b2101 100644 --- a/Resources/Locale/en-US/lathe/lathe-categories.ftl +++ b/Resources/Locale/en-US/lathe/lathe-categories.ftl @@ -31,8 +31,12 @@ lathe-category-faux-tile = Faux lathe-category-maints-tile = Maints lathe-category-marble = Marble lathe-category-steel-tile = Steel +lathe-category-shuttle-tile = Shuttle lathe-category-white-tile = White lathe-category-wood-tile = Wood +lathe-category-plastic-tile = Plastic +lathe-category-precious-tile = Precious +lathe-category-industrial-tile = Industrial # Science lathe-category-mechs = Mechs diff --git a/Resources/Locale/en-US/lobby/lobby-state-background.ftl b/Resources/Locale/en-US/lobby/lobby-state-background.ftl index c0046ca693..7191ebbe12 100644 --- a/Resources/Locale/en-US/lobby/lobby-state-background.ftl +++ b/Resources/Locale/en-US/lobby/lobby-state-background.ftl @@ -1,6 +1,9 @@ lobby-state-background-warden-title = Warden lobby-state-background-warden-artist = Solbusaur +lobby-state-background-invisiblewall-title = Invisible Wall +lobby-state-background-invisiblewall-artist = Vandersloot + lobby-state-background-pharmacy-title = Pharmacy lobby-state-background-pharmacy-artist = Solbusaur diff --git a/Resources/Locale/en-US/objectives/conditions/anomaly-supercrit.ftl b/Resources/Locale/en-US/objectives/conditions/anomaly-supercrit.ftl new file mode 100644 index 0000000000..358b9e8673 --- /dev/null +++ b/Resources/Locale/en-US/objectives/conditions/anomaly-supercrit.ftl @@ -0,0 +1 @@ +objective-condition-supercrit-anomalies-title = Cause {$count} anomalies to go supercritical diff --git a/Resources/Locale/en-US/objectives/conditions/kill-person.ftl b/Resources/Locale/en-US/objectives/conditions/kill-person.ftl index aad31d26f9..c5ea6f47fc 100644 --- a/Resources/Locale/en-US/objectives/conditions/kill-person.ftl +++ b/Resources/Locale/en-US/objectives/conditions/kill-person.ftl @@ -1,3 +1,4 @@ objective-condition-kill-person-title = Kill or maroon {$targetName}, {CAPITALIZE($job)} objective-condition-kill-maroon-title = Kill and maroon {$targetName}, {CAPITALIZE($job)} +objective-condition-kill-station-ai = Destroy {$targetName}, {CAPITALIZE($job)} and ensure they remain out of commission. objective-condition-maroon-person-title = Prevent {$targetName}, {CAPITALIZE($job)} from reaching CentComm. diff --git a/Resources/Locale/en-US/objectives/conditions/mail-fraud.ftl b/Resources/Locale/en-US/objectives/conditions/mail-fraud.ftl new file mode 100644 index 0000000000..99d8aeaead --- /dev/null +++ b/Resources/Locale/en-US/objectives/conditions/mail-fraud.ftl @@ -0,0 +1 @@ +objective-condition-mail-fraud-title = Cut into {$count} letters or packages not addressed to you. diff --git a/Resources/Locale/en-US/photography/photography.ftl b/Resources/Locale/en-US/photography/photography.ftl new file mode 100644 index 0000000000..1876fb194d --- /dev/null +++ b/Resources/Locale/en-US/photography/photography.ftl @@ -0,0 +1,7 @@ +# TODO: Make this a fluent function in RT +photograph-name-text = This is a photograph of { PROPER($entity) -> + *[false] { INDEFINITE($entity) } { $entity } + [true] { $entity } + }. +photograph-name-text-empty = This is a photograph. +photograph-name-text-photograph = This is a photograph of another photograph. diff --git a/Resources/Locale/en-US/round-end/round-end-system.ftl b/Resources/Locale/en-US/round-end/round-end-system.ftl index 30069f7171..6068e4385e 100644 --- a/Resources/Locale/en-US/round-end/round-end-system.ftl +++ b/Resources/Locale/en-US/round-end/round-end-system.ftl @@ -7,5 +7,11 @@ round-end-system-shuttle-recalled-announcement = The emergency shuttle has been round-end-system-shuttle-sender-announcement = Station round-end-system-round-restart-eta-announcement = Restarting the round in {$time} {$units}... -eta-units-minutes = minutes -eta-units-seconds = seconds +eta-units-minutes = {$amount -> + [one] minute + *[other] minutes +} +eta-units-seconds = {$amount -> + [one] second + *[other] seconds +} diff --git a/Resources/Locale/en-US/silicons/station-ai.ftl b/Resources/Locale/en-US/silicons/station-ai.ftl index cbe3ef6ec0..1f35af41a8 100644 --- a/Resources/Locale/en-US/silicons/station-ai.ftl +++ b/Resources/Locale/en-US/silicons/station-ai.ftl @@ -8,6 +8,7 @@ station-ai-has-no-power-for-upload = Upload failed - the AI core is unpowered. station-ai-is-too-damaged-for-upload = Upload failed - the AI core must be repaired. station-ai-core-losing-power = Your AI core is now running on reserve battery power. station-ai-core-critical-power = Your AI core is critically low on power. External power must be re-established or severe data corruption may occur! +station-ai-core-taking-damage = Your AI core is sustaining physical damage. # Ghost role station-ai-ghost-role-name = Station AI diff --git a/Resources/Locale/en-US/store/categories.ftl b/Resources/Locale/en-US/store/categories.ftl index 4469a576cd..9ecf4f91d2 100644 --- a/Resources/Locale/en-US/store/categories.ftl +++ b/Resources/Locale/en-US/store/categories.ftl @@ -12,6 +12,7 @@ store-category-allies = Allies store-category-job = Job store-category-wearables = Wearables store-category-pointless = Pointless +store-category-objective = Objective store-discounted-items = Discounts # Revenant diff --git a/Resources/Locale/en-US/store/changeling-catalog.ftl b/Resources/Locale/en-US/store/changeling-catalog.ftl index eb7795ff36..07dee0aa33 100644 --- a/Resources/Locale/en-US/store/changeling-catalog.ftl +++ b/Resources/Locale/en-US/store/changeling-catalog.ftl @@ -1,2 +1,5 @@ -changeling-arm-blade-name = Retractable Arm Blade -changeling-arm-blade-desc = Transform your arm into a terrifying flesh blade. Can be toggled. +changeling-catalog-arm-blade-name = Retractable Arm Blade +changeling-catalog-arm-blade-desc = Transform your arm into a terrifying flesh blade. Can be toggled. + +changeling-catalog-flesh-clothing-name = Flesh Clothing +changeling-catalog-flesh-clothing-desc = Your body's surface will adapt to mirror the clothing of any person you are transforming into. However, these clothing items are non-functional and will make you easy to identify as a changeling if someone tries to remove them. Can be toggled. diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 4f67fae1cd..60f13dbc42 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -409,6 +409,9 @@ uplink-soap-desc = An untrustworthy bar of soap. Smells of fear. uplink-ultrabright-lantern-name = Extra-Bright Lantern uplink-ultrabright-lantern-desc = This ultra-bright lantern can be used to blind people, similar to a flash. +uplink-travel-camera-name = Travel Camera +uplink-travel-camera-desc = Stun people with your photography skills and the conveniently legal camera flash. Makes you look like a tourist. + uplink-combat-medkit-name = Combat Medical Kit uplink-combat-medkit-desc = A medkit made for fixing combat injuries. @@ -520,3 +523,7 @@ uplink-briefcase-gun-desc = An indistinct briefcase with a highly compact C-20K uplink-energycrossbow-name = Mini Energy Crossbow uplink-energycrossbow-desc = The go-to sidearm of any operative who prefers their victims not to be moving. Fires regenerating toxic arrows that floors victims in an instant. + +#Objective items +uplink-hijack-beacon-name = Hijack Beacon +uplink-hijack-beacon-desc = A syndicate-brand hijack beacon designed to get around the firewalls of Nanotrasen-brand Automated Trade Stations. They take 200 seconds to work and Trade Stations will announce they are being hacked, so prepare accordingly. diff --git a/Resources/Locale/en-US/tips.ftl b/Resources/Locale/en-US/tips.ftl index c02e72da19..37a52a8d4d 100644 --- a/Resources/Locale/en-US/tips.ftl +++ b/Resources/Locale/en-US/tips.ftl @@ -6,9 +6,9 @@ tips-dataset-5 = Artifacts have the ability to gain permanent effects for some t tips-dataset-6 = You can avoid slipping on most puddles by walking. However, some strong chemicals like space lube will slip people anyway. tips-dataset-7 = Some plants, such as galaxy thistle, can be ground up into extremely useful and potent medicines. tips-dataset-8 = Mopping up puddles and draining them into other containers conserves the reagents found in the puddle. -tips-dataset-9 = Floor drains, usually found in the chef's freezer or janitor's office, rapidly consume reagent found in puddles around them--including blood. +tips-dataset-9 = Floor drains, usually found in the chef's freezer or janitor's office, rapidly consume reagent found in puddles around them — including blood. tips-dataset-10 = Cognizine, a hard to manufacture chemical, makes animals sentient when they are injected with it. -tips-dataset-11 = Loaded mousetraps are incredibly effective at dealing with all manner of low-mass mobs--including Rat Servants. +tips-dataset-11 = Loaded mousetraps are incredibly effective at dealing with all manner of low-mass mobs — including Rat Servants. tips-dataset-12 = Fire extinguishers can be loaded with any reagent in the game. tips-dataset-13 = Some reagents, like chlorine trifluoride, have unique effects when applied by touch, such as through a spray bottle or foam. tips-dataset-14 = Remember to touch grass in between playing Space Station 14 every once in a while. @@ -19,7 +19,7 @@ tips-dataset-18 = When running the Singularity, make sure to check on it periodi tips-dataset-19 = If the Singularity is up, make sure to refuel the radiation collectors once in a while. tips-dataset-20 = Chemicals don't react while inside the ChemMaster's buffer. tips-dataset-21 = Don't anger the bartender by throwing their glasses! Politely place them on the table by tapping Q. -tips-dataset-22 = You can hold SPACE by default to slow the movement of the shuttle when piloting, to allow for precise movements--or even coming to a complete stop. +tips-dataset-22 = You can hold SPACE by default to slow the movement of the shuttle when piloting, to allow for precise movements — or even coming to a complete stop. tips-dataset-23 = Dexalin, Dexalin Plus, and Epinephrine will all purge heartbreaker toxin from your bloodstream while metabolizing. tips-dataset-24 = Every crewmember comes with an emergency medipen in their survival box containing epinephrine and tranexamic acid. tips-dataset-25 = The AME is a high-priority target and is easily sabotaged. Make sure to set up the Singularity or Solars so that you don't run out of power if it blows. @@ -29,15 +29,15 @@ tips-dataset-28 = Riot armor is significantly more powerful against opponents th tips-dataset-29 = As a ghost, you can use the Verb Menu to orbit around and follow any entity in game automatically. tips-dataset-30 = As a Traitor, you may sometimes be assigned to hunt other traitors, and in turn be hunted by others. tips-dataset-31 = As a Traitor, the syndicate encryption key can be used to communicate through a secure channel with any other traitors who have purchased it. -tips-dataset-32 = As a Traitor, compromising important communications channels like security or engineering can give valuable intelligence. Be aware that this goes in both ways - security can compromise syndicate communications as well! +tips-dataset-32 = As a Traitor, compromising important communications channels like security or engineering can give valuable intelligence. Be aware that this goes in both ways — security can compromise syndicate communications as well! tips-dataset-33 = As a Traitor, the syndicate toolbox is extremely versatile. For only 2 telecrystals, you can get a full set of tools to help you in an emergency, insulated combat gloves and a syndicate gas mask. tips-dataset-34 = As a Traitor, never underestimate the web vest. It may not provide space protection, but its cheap cost and robust protection makes it handy for protecting against trigger-happy foes. tips-dataset-35 = As a Traitor, any purchased grenade penguins won't attack you, and will explode if killed. tips-dataset-36 = As a Traitor, be careful when using vestine from the chemical synthesis kit. If someone checks your station, they could easily out you. tips-dataset-37 = As a Traitor, remember that power sinks will create a loud sound and alert the crew after running for long enough. Try to hide them in a tricky-to-find spot, or reinforce the area around them so that they're harder to reach. -tips-dataset-38 = As a Traitor, plasma gas is an excellent way to create chaos. It can be ignited to make an area extra-uninhabitable, and can cause toxin damage to those that inhale it. +tips-dataset-38 = As a Traitor, plasma gas is an excellent way to create chaos. It can be ignited to make an area extra uninhabitable, and can cause toxin damage to those that inhale it. tips-dataset-39 = As a Traitor, dehydrated carps are useful for killing a large hoard of people. As long as you pat it before rehydrating it, it can be used as a great distraction. -tips-dataset-40 = As a Traitor, have you tried injecting plasma into batteries? In the case of a defibrillator, it explodes on use; hurting the user and the patient! +tips-dataset-40 = As a Traitor, have you tried injecting plasma into batteries? In the case of a defibrillator, it explodes on use, hurting the user and the patient! tips-dataset-41 = As a Nuclear Operative, stick together! While your equipment is robust, your fellow operatives are much better at saving your life: they can drag you away from danger while stunned and provide cover fire. tips-dataset-42 = As a Nuclear Operative, communication is key! Use your radio to speak to your fellow operatives and coordinate an attack plan. tips-dataset-43 = As a Nuclear Operative, remember that stealth is an option. It'll be hard for the captain to fight back if he gets caught off guard by what he thinks is just a regular passenger! @@ -49,7 +49,7 @@ tips-dataset-48 = As a Salvage Specialist, never forget to mine ore! Ore can be tips-dataset-49 = As a Salvage Specialist, try asking science for a tethergun. It can be used to grab items off of salvage wrecks extremely efficiently! tips-dataset-50 = As a Salvage Specialist, try asking science for a grappling hook. It can be used to propel yourself onto wrecks, or if stuck in space you don't have to rely on the proto-kinetic accelerator. tips-dataset-51 = Tip #51 does not exist and has never existed. Ignore any rumors to the contrary. -tips-dataset-52 = As a Salvage Specialist, consider cooperating with the Cargo Technicians. They can order you a wide variety of useful items, including ones that may be hard to get otherwise, such laser guns and shuttle building materials. +tips-dataset-52 = As a Salvage Specialist, consider cooperating with the Cargo Technicians. They can order you a wide variety of useful items, including ones that may be hard to get otherwise, such as laser guns and shuttle building materials. tips-dataset-53 = As a Cargo Technician, consider asking science for a Ripley APLU. When paired with a hydraulic clamp, you can grab valuable maintenance objects like fuel tanks much more easily, and make deliveries in a swift manner. tips-dataset-54 = As a Cargo Technician, try to maintain a surplus of materials. They are extremely useful for Scientists and Station Engineers to have immediate access to. tips-dataset-55 = As a Cargo Technician, if you have a surplus of cash try gambling! Sometimes you gain more money than you begin with. @@ -70,28 +70,28 @@ tips-dataset-69 = As an Atmospheric Technician, your ATMOS holofan projector blo tips-dataset-70 = As an Atmospheric Technician, try to resist the temptation of making canister bombs for Nuclear Operatives, unless you're in a last-ditch scenario. They often lead to large amounts of unnecessary friendly fire! tips-dataset-71 = As an Engineer, you can repair cracked windows by using a lit welding tool on them while not in combat mode. tips-dataset-72 = As an Engineer, you can electrify grilles by placing powered cables beneath them. -tips-dataset-73 = As an Engineer, always double check when you're setting up the singularity. It is easier than you think to loose it! +tips-dataset-73 = As an Engineer, always double-check when you're setting up the singularity. It is easier than you think to loose it! tips-dataset-74 = As an Engineer, you can use plasma glass to reinforce an area and prevent radiation. Uranium glass can also be used to prevent radiation. -tips-dataset-75 = As the Captain, you are one of the highest priority targets on the station. Everything from revolutions, to nuclear operatives, to traitors that need to rob you of your unique laser pistol or your life are things to worry about. +tips-dataset-75 = As the Captain, you are one of the highest-priority targets on the station. Everything from revolutions, to Nuclear Operatives, to traitors that need to rob you of your unique laser pistol or your life are things to worry about. tips-dataset-76 = As the Captain, always take the nuclear disk and pinpointer with you every shift. It's a good idea to give one of these to another head you can trust with keeping it safe. tips-dataset-77 = As the Captain, you have absolute access and control over the station, but this does not mean that being a horrible person won't result in mutiny. tips-dataset-78 = As the Captain, try to be active and patrol the station. Staying in the bridge might be tempting, but you'll just end up putting a bigger target on your back! tips-dataset-79 = As a Scientist, you can use the node scanner to see when an artifact is able to react to triggers, and which triggers it is currently reacting to; each number it shows corresponds to a different trigger! tips-dataset-80 = As a Scientist, you can utilize upgraded versions of machines to increase its effectiveness. This can make certain machines significantly better; salvage will love you if you upgrade their ore processor! -tips-dataset-81 = As a Scientist, you can build cyborgs using positronic brains and a chassis, they are just as useful as a new crew member. +tips-dataset-81 = As a Scientist, you can build cyborgs using positronic brains and a chassis; they are just as useful as a new crew member. tips-dataset-82 = As a Medical Doctor, try to be wary of overdosing your patients, especially if someone else has already been on the scene. Overdoses are often lethal to patients in crit! tips-dataset-83 = As a Medical Doctor, don't underestimate your cryo pods! They heal almost every type of damage, making them very useful when you are overloaded or need to heal someone in a pinch. tips-dataset-84 = As a Medical Doctor, exercise caution when putting reptilians in cryopods. They will take a lot of extra cold damage, but you can mitigate this with some burn medicine or leporazine. tips-dataset-85 = As a Medical Doctor, remember that the health analyzer can be used if you lose your PDA. tips-dataset-86 = As a Chemist, once you've made everything you've needed to, don't be afraid to make more silly reagents. Have you tried desoxyephedrine or licoxide? -tips-dataset-87 = As a Medical Doctor, Chemist, or Chief Medical Officer, you can use chloral hydrate to non-lethally sedate unruly patients. +tips-dataset-87 = As a Medical Doctor, Chemist, or Chief Medical Officer, you can use chloral hydrate to nonlethally sedate unruly patients. tips-dataset-88 = Don't be afraid to ask for help, whether from your peers in character or through LOOC, or from admins! -tips-dataset-89 = You'll quickly lose your interest in the game if you play to win and kill. If you find yourself doing this, take a step back and talk to people--it's a much better experience! +tips-dataset-89 = You'll quickly lose your interest in the game if you play to win and kill. If you find yourself doing this, take a step back and talk to people — it's a much better experience! tips-dataset-90 = If there's something you need from another department, try asking! This game isn't singleplayer and you'd be surprised what you can get accomplished together! tips-dataset-91 = The station's nuke is invincible. Go find the disk instead of trying to destroy it. tips-dataset-92 = Maintenance is full of equipment that is randomized every round. Look around and see if anything is worth using. -tips-dataset-93 = We were all new once, be patient and guide new players, especially those playing intern roles, in the right direction. -tips-dataset-94 = Firesuits, winter coats and emergency EVA suits offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all. +tips-dataset-93 = We were all new once. Be patient and guide new players, especially those playing intern roles, in the right direction. +tips-dataset-94 = Firesuits, winter coats, and emergency EVA suits offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all. tips-dataset-95 = In an emergency, you can always rely on firesuits and emergency EVA suits; they will always spawn in their respective lockers. They might be awkward to move around in, but can easily save your life in a dangerous situation. tips-dataset-96 = In an emergency, remember that you can craft improvised weapons! A baseball bat or spear could easily mean the difference between deterring an attacker or perishing from the hands of one. tips-dataset-97 = Spears can be tipped with chemicals, and will inject a few units every time you hit someone with them directly. @@ -112,7 +112,7 @@ tips-dataset-111 = You can move an item out of the way by dragging it, and then tips-dataset-112 = When dealing with security, you can often get your sentence negated entirely through cooperation and deception. tips-dataset-113 = Fire can spread to other players through touch! Be careful around flaming bodies or large crowds with people on fire in them. tips-dataset-114 = Hull breaches take a few seconds to fully space an area. You can use this time to patch up the hole if you're confident enough, or just run away. -tips-dataset-115 = Burn damage, such as that from a welding tool or lightbulb, can be used to cauterize wounds and stop bleeding. +tips-dataset-115 = Burn damage, such as from a welding tool or lightbulb, can be used to cauterize wounds and stop bleeding. tips-dataset-116 = Bleeding is no joke! If you've been shot or acquired any other major injury, make sure to treat it quickly. tips-dataset-117 = In an emergency, you can butcher a jumpsuit with a sharp object to get cloth, which can be crafted into gauze. tips-dataset-118 = You can use sharp objects to butcher clothes or animals in the right click context menu. This includes glass shards. @@ -121,7 +121,7 @@ tips-dataset-120 = You can stun grenade penguins, which can bide valuable time f tips-dataset-121 = You can click on the names of items to pick them up in the right click menu, instead of hovering over the item and then selecting pick up. tips-dataset-122 = Space Station 14 is open source! If there's a change you want to make, or a simple item you want to add, then try contributing to the game. It's not as hard as you'd think it is. tips-dataset-123 = In a pinch, you can throw drinks or other reagent containers behind you to create a spill that can slip people chasing you. -tips-dataset-124 = Some weapons, such as knives & shivs, have a fast attack speed. +tips-dataset-124 = Some weapons, such as knives and shivs, have a fast attack speed. tips-dataset-125 = The jaws of life can be used to open powered doors. tips-dataset-126 = If you're not a human, you can drink blood to heal back some of your blood volume, albeit very inefficiently. tips-dataset-127 = If you're a human, don't drink blood! It makes you sick and you'll begin to take damage. @@ -135,5 +135,5 @@ tips-dataset-134 = You can tell if an area with firelocks up is spaced by lookin tips-dataset-135 = Instead of picking it up, you can alt-click food to eat it. This also works for mice and other creatures without hands. tips-dataset-136 = If you're trapped behind an electrified door, disable the APC or throw your ID at the door to avoid getting shocked! tips-dataset-137 = If the AI electrifies a door and you have insulated gloves, snip and mend the power wire to reset their electrification! -tips-dataset-138 = If you want to stop your prisoner from escaping from the cell right after being uncuffed, turn on combat mode while uncuffing - this will shove the prisoner down. +tips-dataset-138 = If you want to stop your prisoner from escaping from the cell right after being uncuffed, turn on combat mode while uncuffing — this will shove the prisoner down. tips-dataset-139 = Make sure to clean your illegal implanters with a soap after you use them! Detectives can scan used implanters for incriminating DNA evidence, but not if they've been wiped clean. diff --git a/Resources/Locale/en-US/warps/warp-point-component.ftl b/Resources/Locale/en-US/warps/warp-point-component.ftl index 988c172363..9878d0bf3d 100644 --- a/Resources/Locale/en-US/warps/warp-point-component.ftl +++ b/Resources/Locale/en-US/warps/warp-point-component.ftl @@ -1 +1 @@ -warp-point-component-on-examine-success = This one's location ID is {$location} \ No newline at end of file +warp-point-component-on-examine-success = This one's location ID is '{$location}' diff --git a/Resources/Locale/en-US/wires/components/wires-panel-component.ftl b/Resources/Locale/en-US/wires/components/wires-panel-component.ftl index ffe9741c28..a982eeaf6f 100644 --- a/Resources/Locale/en-US/wires/components/wires-panel-component.ftl +++ b/Resources/Locale/en-US/wires/components/wires-panel-component.ftl @@ -1,6 +1,8 @@ wires-panel-component-on-examine-open = The [color=lightgray]maintenance panel[/color] is [color=red]open[/color]. wires-panel-component-on-examine-closed = The [color=lightgray]maintenance panel[/color] is [color=darkgreen]closed[/color]. +wires-panel-verb-view-panel = View maintenance panel + # wire colors wire-name-color-red = Red diff --git a/Resources/Locale/en-US/worldgen/applyworldgenconfig.ftl b/Resources/Locale/en-US/worldgen/applyworldgenconfig.ftl deleted file mode 100644 index a2144d08b2..0000000000 --- a/Resources/Locale/en-US/worldgen/applyworldgenconfig.ftl +++ /dev/null @@ -1,4 +0,0 @@ -cmd-applyworldgenconfig-description = Applies the given worldgen configuration to a map, setting it up for chunk loading/etc. -cmd-applyworldgenconfig-help = applyworldgenconfig -cmd-applyworldgenconfig-prototype = worldgen config prototype -cmd-applyworldgenconfig-success = Config applied successfully. Do not rerun this command on this map. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl index 39ed5243e9..46433b5b23 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl @@ -19,6 +19,7 @@ guide-entry-sop-engineering = Инженерный отдел guide-entry-sop-security = Служба безопасности guide-entry-sop-command = Командование guide-entry-sop-centcomm = Центральное Командование +guide-entry-sop-dso = Департамент Специальных Операций guide-entry-sop-legal = Юридический департамент guide-entry-sop-general = Общее guide-entry-sop-codes = Уровни угроз diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl index 07d07dc36c..f5c74ce08e 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl @@ -12,10 +12,11 @@ guidebook-SOP-codes-green = {"[bold]Устанавливается по решению:[/bold] капитана или главы службы безопасности."} guidebook-SOP-codes-green-terms = - На станции отсутствует угроза безопасности — штатный режим работы. + - На станции отсутствуют угрозы безопасности. guidebook-SOP-codes-green-norms = - Экипаж действует в соответствии со стандартными рабочими процедурами. + - Экипаж может свободно передвигаться по станции в соответствии с уровнем доступа. + - Ношение ID-карты в специальном слоте комбинезона по усмотрению. # Синий код guidebook-SOP-codes-blue = @@ -24,10 +25,25 @@ guidebook-SOP-codes-blue = {"[bold]Устанавливается по решению:[/bold] капитана или главы службы безопасности."} guidebook-SOP-codes-blue-terms = - На станции существует угроза безопасности I уровня. + На станции существует угроза безопасности I уровня — угроза, не требующая содействия всего экипажа, которая может быть устранена силами службы безопасности. + + Примеры: + - Вторжение агрессивной фауны, за исключением космического дракона. + - Кража особо ценного имущества, исключая диск ядерной авторизации. + - Взлом защищенной стратегической точки. + - Угон приписанного к станции шаттла. + - Взлом станционного ИИ. + - Убийство должностного лица. + - Убийство 3-4 членов экипажа. + - Пропажа нескольких человек и т.д. guidebook-SOP-codes-blue-norms = - Экипаж должен следовать указаниям службы безопасности и докладывать о подозрительной активности. + - Экипаж может свободно передвигаться по станции в соответствии с уровнем доступа, капитан или глава службы безопасности уполномочены утвердить зоны, закрытые для посещения. + - Экипаж должен докладывать о любой подозрительной активности службе безопасности лично или в общий голосовой канал. + - Все члены экипажа обязаны носить ID-карты в соответствующем слоте комбинезона (внутри КПК, либо отдельно). + - Члены экипажа обязаны отреагировать на звук свистка и прекратить движение в ожидании распоряжений сотрудников службы безопасности. + - Разрешено продолжить движение в случае отсутствия указаний от сотрудников службы безопасности в течение 15 секунд. + - Сотрудникам медицинского и инженерного отделов разрешено игнорировать свисток при выполнении срочных задач. # Красный код guidebook-SOP-codes-red = diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl index 225ae583f7..f2b3942796 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl @@ -114,7 +114,7 @@ guidebook-SOP-command-procedure-appoint-acting-captain = ## Процедура отстранения капитана от командования guidebook-SOP-command-procedure-remove-captain = 1. Капитан может быть уволен по собственному желанию, по приказу Центрального Командования или при решении не анонимного голосования глав в соответствии с причинами отстранения от должности. - 1. ВрИО капитан обязан вернуться на свою изначальную должность после прибытия нового капитана с Центрального Командования. + - ВрИО капитан обязан вернуться на свою изначальную должность после прибытия нового капитана с Центрального Командования. 1. При голосовании необходимо большинство голосов глав отделов «ЗА» отстранение капитана. - При равном количестве голосов «ЗА» и «ПРОТИВ» отстранение капитана отменяется. - Воздержание от голоса, отказ голосовать и попытка сорвать голосование считается голосом «ЗА». @@ -229,7 +229,7 @@ guidebook-SOP-command-procedure-reasons-for-removal = ## Протоколы эвакуации ### Окончание смены guidebook-SOP-command-protocol-evacuation-end-of-shift = - 1. После окончания смены начинается стандартный процесс отправки экипажа на станцию центрального командования для последующего распределения и отдыха. Отбытие производится спустя 2 часа после начала смены или после выполнения дополнительной цели смены. + После окончания смены начинается стандартный процесс отправки экипажа на станцию центрального командования для последующего распределения и отдыха. Отбытие производится спустя 2 часа после начала смены или после выполнения дополнительной цели смены. - Капитан обязан составить письменный отчёт о выполнении основной или дополнительной цели и отправить его на центральное командование. - Капитан обязан уведомить персонал соответствующим сообщением для подготовки к отбытию. - Службе безопасности необходимо организовать транспортировку всех заключённых; во время перелёта заключённые должны находиться в отсеке для заключённых. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/dso.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/dso.ftl new file mode 100644 index 0000000000..083c8cbc4b --- /dev/null +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/dso.ftl @@ -0,0 +1,248 @@ +# Общие СРП +guidebook-SOP-dso-general-must = + 1. Переключить датчики на комбинезоне в режим «координаты». При возможности держаться группой, если не дано других распоряжений от лидера. + 1. Исполнять приказы старшего по званию, соблюдать субординацию и использовать уставную речь. + - Допускается пренебрежение уставной речью при чрезвычайной ситуации. + 1. В случае пропажи лидера отряда, старший по званию обязан занять его место. + 1. Следить за своим снаряжением и не допускать его попадания в руки экипажа станции или противника. + 1. Выполнять свои обязанности по своей специализации, пока лидер ОБР не выдал других распоряжений. + +guidebook-SOP-dso-general-right = + 1. Отдавать приказы всем членам экипажа станции. + 1. Принимать любые меры для выполнения поставленной задачи. + 1. Нарушать стандартные процедуры экипажа при чрезвычайной ситуации. + 1. В полной мере руководить экипажем станции, если это необходимо для выполнения цели миссии, даже если исполнение приказа приведет к нарушению СРП со стороны члена экипажа. + +guidebook-SOP-dso-general-prohibited = + 1. Забирать снаряжение станции Центрального Командования, которое не находится в оружейной, лазарете или на складе материалов. + 1. Выполнять задания в нетрезвом виде. + 1. Менять свою рабочую форму. + +# Должностные СРП +## Командир РХБЗЗ +guidebook-SOP-CBURNCommander-must = + 1. Эффективно руководить отрядом для выполнения поставленной задачи. + 1. Поддерживать дисциплину среди оперативников и пресекать нарушения, вплоть до применения летальной силы. + 1. Инициировать и координировать карантинные меры на станции при подозрении на биологическую угрозу, включая закрытие секций станции и установление зон полной изоляции. + +guidebook-SOP-CBURNCommander-right = + 1. Взять управление объектом на себя. + 1. Единолично снять неприкосновенность с члена экипажа. + 1. Установить карантин на станции при любой угрозе биологического или радиологического характера, включая полную изоляцию членов экипажа. + 1. Переместить любого сотрудника станции в карантин с последующей передачей его на ЦК. + +guidebook-SOP-CBURNCommander-prohibited = + 1. Игнорировать или откладывать приказы Департамента Специальных Операций. + 1. Покидать сектор станции до завершения поставленной миссии. + 1. Превышать свои полномочия/использовать их для собственной выгоды. + +## Агент РХБЗЗ +guidebook-SOP-CBURN-must = + 1. Следовать специальным рабочим процедурам, пока командованием ДСО не даны иные указания. + 1. Проводить процедуру задержания с соблюдением уровней применения силы. + - Пункт об уровнях применения силы не актуален, если на станции действует режим чрезвычайной ситуации. + 1. Исполнять приказы командира, если это не противоречит указаниям командования ДСО. + 1. Выполнять поставленную задачу. + +guidebook-SOP-CBURN-right = + 1. Использовать любое, в том числе и вражеское, вооружение вне зависимости от уровня угрозы на станции. + 1. Использовать любые доступные средства для локализации биологических угроз. + +guidebook-SOP-CBURN-prohibited = + 1. Нарушать герметичность скафандра путём отсоединения шлема где-либо, кроме шаттла отряда. + 1. Эвакуировать персонал станции на шаттл отряда. + - Приказ командира может отменить данный запрет. + +## Лидер ОБР +guidebook-SOP-ERTLeader-must = + 1. Следить за состоянием всего отряда и оперативно отдавать приказы для достижения поставленной задачи. + 1. Поддерживать связь с Центральным Командованием/Департаментом Специальных Операций любыми доступными способами. + 1. Оповестить экипаж станции о прибытии шаттла ОБР в сектор станции. + - Данный пункт теряет свою актуальность, если вредит/идёт вразрез интересам миссии. + 1. Проверить нахождение каждого члена отряда на шаттле перед отправкой на станцию. + 1. Следить за функционированием своего шаттла и поддержанием на нём чистоты и порядка. + +guidebook-SOP-ERTLeader-right = + 1. Изъять диск ядерной аутентификации, если на станции чрезвычайная ситуация. + 1. Единолично снять неприкосновенность с члена экипажа. + 1. Взять на себя командование станцией на время проведения операции. + 1. При нарушении КЗ, ОПРС и СРП ОБР разжаловать и арестовать любого оперативника отряда с обязательной доставкой на станцию Центрального Командования. + - Во время действия режима чрезвычайной ситуации допускается расстрел на месте. + 1. Принять решение об окончании смены и отправке шаттла эвакуации, если ситуация на станции критическая. + +guidebook-SOP-ERTLeader-prohibited = + 1. Игнорировать или откладывать приказы Департамента Специальных Операций. + 1. Покидать сектор станции до завершения поставленной миссии. + 1. Превышать свои полномочия/использовать их для собственной выгоды. + +## Священник ОБР +guidebook-SOP-ERTChaplain-must = + 1. Оказывать духовную поддержку членам отряда, помогая им сохранять моральный дух в стрессовых ситуациях. + 1. Выполнять обряды, способствующие защите от сверхъестественных угроз. + 1. Поддерживать религиозное оборудование и предметы в исправном состоянии и готовности к использованию. + +guidebook-SOP-ERTChaplain-right = + 1. Использовать религиозные предметы и реликвии для защиты от сверхъестественных и других угроз, если это потребуется. + 1. Проводить обряды и ритуалы по запросу членов отряда или в случаях, когда ситуация на станции требует духовного вмешательства. + 1. Запрашивать поддержку от других подразделений для обеспечения безопасности при проведении обрядов. + +guidebook-SOP-ERTChaplain-prohibited = + 1. Навязывать религиозные взгляды членам отряда или экипажа станции. + 1. Использовать религиозные предметы или обряды для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + 1. Игнорировать запросы на духовную поддержку или проведение обрядов от членов отряда. + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + +## Инженер ОБР +guidebook-SOP-ERTEngineer-must = + 1. Обеспечивать техническую поддержку отряда, ремонтируя оборудование и восстанавливая инфраструктуру станции. + 1. Работать над обеспечением безопасности инженерных систем и предотвращением аварийных ситуаций. + 1. Участвовать в оперативных задачах, связанных с восстановлением повреждённых объектов и систем. + +guidebook-SOP-ERTEngineer-right = + 1. Использовать инженерные инструменты и оборудование для выполнения своих задач. + 1. Запрашивать ресурсы и помощь от других подразделений для выполнения технических задач. + 1. Вносить предложения по улучшению инженерных систем и процедур на станции. + +guidebook-SOP-ERTEngineer-prohibited = + 1. Самовольно отключать или изменять настройки критически важных систем станции без согласования с лидером ОБР или ОСО. + 1. Игнорировать аварийные ситуации, связанные с инженерными системами станции. + 1. Использовать инженерные ресурсы для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + +## Медик ОБР +guidebook-SOP-ERTMedical-must = + 1. Оказывать медицинскую помощь членам отряда и экипажу станции в случае ранений или заболеваний. + 1. Обязаны отдавать приоритет лечению в следующем порядке: оказание помощи себе > члены отряда и иные сотрудники ДСО > члены экипажа станции. + 1. Поддерживать медицинское оборудование и медикаменты в исправном состоянии и готовности к использованию. + 1. Работать в тесном взаимодействии с медиками станции для обеспечения своевременного лечения. + +guidebook-SOP-ERTMedical-right = + 1. Использовать медицинские ресурсы для оказания помощи членам отряда и экипажу. + 1. Запрашивать помощь от других подразделений и отрядов для проведения медицинских операций. + 1. Принимать решения по эвакуации и лечению пострадавших в случае критической угрозы. + +guidebook-SOP-ERTMedical-prohibited = + 1. Пренебрегать оказанием помощи раненым и больным членам отряда или экипажу станции. + 1. Использовать медицинские ресурсы для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + 1. Игнорировать медицинские протоколы и процедуры, установленные для станции. + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + +## Офицер Безопасности ОБР +guidebook-SOP-ERTSecurity-must = + 1. Обеспечивать безопасность отряда и защищать станцию от внутренних и внешних угроз. + 1. Проводить патрулирование и мониторинг ключевых зон станции для предотвращения нарушений и своевременного реагирования на угрозы. + 1. Работать в тесном взаимодействии с другими отрядами и службами безопасности станции для поддержания порядка и устранения угроз. + +guidebook-SOP-ERTSecurity-right = + 1. Использовать оружие и оборудование для защиты станции и членов отряда от любых угроз. + 1. Запрашивать помощь от других подразделений и отрядов в случае серьёзной угрозы безопасности. + 1. Принимать решения по задержанию, нейтрализации и ликвидации опасных элементов на станции. + +guidebook-SOP-ERTSecurity-prohibited = + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + 1. Игнорировать угрозы безопасности станции или членов отряда, включая угрозы от зомби и агрессивной фауны. + 1. Использовать оружие и оборудование для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + +## Уборщик ОБР +guidebook-SOP-ERTJanitor-must = + 1. Обеспечивать чистоту и порядок в зонах, где работает отряд, включая уборку биологических, химических и иных загрязнений, которые могут представлять угрозу для станции. + 1. Работать над предотвращением распространения загрязнения на станции во избежание создания опасных ситуаций для членов отряда и экипажа станции. + 1. Поддерживать уборочное оборудование, включая специализированные устройства для очистки особо опасных зон, в исправном состоянии и готовности к использованию. + +guidebook-SOP-ERTJanitor-right = + 1. Использовать специализированное оборудование и химикаты для очистки зон, подверженных загрязнению. + 1. Запрашивать ресурсы и помощь от других подразделений для выполнения задач по уборке. + 1. Принимать меры по изоляции и очистке зон с высоким уровнем загрязнения, в том числе биологических и химических угроз. + +guidebook-SOP-ERTJanitor-prohibited = + 1. Пренебрегать обязанностями по поддержанию чистоты и порядка в зонах, где работает отряд. + 1. Использовать химикаты и оборудование для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + 1. Игнорировать правила безопасности при работе с опасными веществами и биологическими угрозами. + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + +# Лидер Эскадрона Смерти — Foxtrot +guidebook-SOP-DeathSquadFoxtrotLeader-must = + 1. Руководить операцией по спасению экипажа и устранению угроз с максимальной решимостью и ответственностью. + 1. Координировать действия бойцов, обеспечивая их взаимодействие и максимальную эффективность в бою. + 1. Поддерживать связь с ДСО для получения указаний и предоставления отчётов о ходе операции. + 1. Обеспечить сохранение ключевых объектов станции, предотвращая их разрушение. + 1. Проводить эвакуацию экипажа и важного имущества станции в случае, если ситуация выходит из-под контроля. + 1. Лично принимать важные стратегические решения в сложных и критических ситуациях, касающихся спасения персонала. + +guidebook-SOP-DeathSquadFoxtrotLeader-right = + 1. Принимать на себя командование станцией и отдавать приказы всем членам экипажа для координации действий по спасению. + 1. Координировать эвакуацию и принимать решение об отправке шаттла для спасения экипажа. + 1. Использовать любые доступные ресурсы станции для выполнения миссии, в том числе оборудование, защищённое или заблокированное для обычных сотрудников. + 1. Принимать решения по эвакуации экипажа и активации аварийных протоколов, если ситуация критическая. + 1. Использовать любые методы для устранения угроз, при этом минимизируя потери среди экипажа. + +guidebook-SOP-DeathSquadFoxtrotLeader-prohibited = + 1. Игнорировать приказы ДСО или ставить личные цели выше миссии спасения. + 1. Вступать в контакт с врагом, если это ставит под угрозу успех операции или жизнь экипажа. + +# Оперативник Эскадрона Смерти — Foxtrot +guidebook-SOP-DeathSquadFoxtrot-must = + 1. Исполнять приказы Лидера Отряда "Фокстрот" и строго следовать инструкциям по защите экипажа и уничтожению угроз. + 1. Обеспечивать безопасность и спасение экипажа станции любой ценой. + 1. Вступать в бой с любой угрозой, не колеблясь, и уничтожать её. + 1. Эвакуировать пострадавших и обеспечивать их безопасность до полной ликвидации угрозы. + 1. Следить за своим снаряжением и вооружением, обеспечивая его готовность к любым возможным ситуациям. + 1. Принимать участие в эвакуации членов экипажа и сохранении имущества станции в случае экстренных ситуаций. + +guidebook-SOP-DeathSquadFoxtrot-right = + 1. Отдавать приказы любому члену экипажа станции, если это необходимо для успешного выполнения задачи спасения. + 1. Использовать любое доступное оборудование или вооружение, включая закрытые зоны станции, для ликвидации угроз. + 1. Применять силу, если это необходимо для защиты членов экипажа или в случае прямой угрозы. + +guidebook-SOP-DeathSquadFoxtrot-prohibited = + 1. Покидать зону операции без разрешения Лидера Отряда. + 1. Отступать или отказываться от выполнения миссии по спасению, если существует хоть малейший шанс спасти кого-то из экипажа. + 1. Использовать боевое снаряжение для целей, не связанных с миссией спасения. + 1. Нарушать субординацию и не выполнять приказы Лидера, если это не угрожает непосредственной безопасности миссии. + +# Лидер Эскадрона Смерти — Tango +guidebook-SOP-DeathSquadTangoLeader-must = + 1. Руководить операцией по ликвидации всего экипажа станции, строго следуя приказам, полученным от Департамента Специальных Операций NanoTrasen. + 1. Обеспечивать слаженность и координацию действий всех членов эскадрона, гарантируя выполнение поставленных задач в кратчайшие сроки. + 1. Контролировать оснащение и боевую готовность оперативников эскадрона, включая использование имплантов, стимуляторов и новейших вооружений. + 1. Поддерживать оперативную связь с ОСО для получения дальнейших указаний в случае непредвиденных обстоятельств. + 1. В случае особых условий принимать решение о временном контакте с экипажем, если это необходимо для его более эффективного устранения. + +guidebook-SOP-DeathSquadTangoLeader-right = + 1. Отдавать приказы всем оперативникам эскадрона и требовать их безусловного исполнения. + 1. Единолично принимать решения о ходе выполнения миссии, включая изменение планов и приоритетов, если это не идёт вразрез с приказами ОСО. + 1. Использовать любые доступные ресурсы и оборудование станции для выполнения задачи, включая взлом, проникновение или захват стратегически важных объектов. + 1. Принимать тактические решения на месте, изменяя план операции в зависимости от обстоятельств, но всегда соблюдая основную цель — ликвидацию всех разумных существ и синтетиков. + 1. Послать запрос ОСО на полное уничтожение объекта. + - Уничтожение объекта допускается исключительно по приказу ОСО, как крайняя мера, если выполнение задачи и обеспечение секретности невозможно иным способом, поскольку это несёт значительный ущерб имуществу NanoTrasen. + +guidebook-SOP-DeathSquadTangoLeader-prohibited = + 1. Устанавливать контакт с экипажем станции, кроме как для более эффективного его устранения. + 1. Нарушать полученные приказы от Департамента Специальных Операций без особых обстоятельств, угрожающих выполнению миссии. + 1. Проявлять инициативу в уничтожении станции без прямого приказа, если это не является крайней мерой и не согласовано с командованием. + 1. Злоупотреблять использованием оборудования или имплантов, которые не имеют прямой необходимости в операции. + +# Оперативник Эскадрона Смерти — Tango +guidebook-SOP-DeathSquadTango-must = + 1. Ликвидировать всех свидетелей на станции, включая экипаж, персонал, любые формы жизни, и удалить ИИ у всех синтетов на станции, если таковые имеются — не оставляя ни одного свидетеля. + 1. Использовать всё доступное снаряжение, включая импланты, стимуляторы и любые виды вооружения, для выполнения своей задачи. + 1. Постоянно докладывать командующему офицеру о ходе выполнения миссии и всех возникших осложнениях. + 1. Устранять любые следы присутствия или действий отряда на станции, включая уничтожение данных и улик. + 1. При обнаружении несанкционированного доступа на станцию или посторонних лиц, оперативники Эскадрона Смерти обязаны уничтожить нарушителей без предупреждения. + +guidebook-SOP-DeathSquadTango-right = + 1. Может вступать в контакт с членами экипажа. + +guidebook-SOP-DeathSquadTango-prohibited = + 1. Вступать в длительные переговоры или сотрудничество с экипажем станции, за исключением случаев, когда это прямо способствует их уничтожению. + 1. Покидать станцию до полного выполнения задачи по ликвидации всех членов экипажа. + 1. Раскрывать информацию о цели миссии или структуре отряда. + +# Infobox +guidebook-SOP-dso-infobox = + {"[bold]Информацией на данной странице не обладает ни один сотрудник станции.[/bold]"} diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl index b4bcc89f67..274cda8536 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl @@ -1,64 +1,104 @@ +# Общие СРП +guidebook-SOP-engineering-general-must = + 1. Соблюдать [tooltip="guidebook-SOP-engineering-tooltip-safety-precautions" text="технику безопасности"]. + 1. Обеспечить объект [tooltip="guidebook-SOP-engineering-tooltip-generator-main" text="основным"] источником электропитания в кратчайшие возможные сроки. + 1. Обеспечить объект [tooltip="guidebook-SOP-engineering-tooltip-generator-spare" text="резервным"] источником электропитания. + 1. В случае, если при эвакуации с объекта не останется сотрудников, способных обслуживать генераторы, основанные на Сингулярности или Тесле, провести их безопасное отключение с помощью замедлителя частиц или специального гранатомёта. + - При невозможности безопасного отключения, на объекте должен остаться минимум один сотрудник, способный обслуживать такой генератор. + - При присутствия на объекте критической угрозы безопасности, требующей эвакуации всего персонала, ЦК должно быть уведомлено об угрозе нарушения условий эксплуатации. + +guidebook-SOP-engineering-general-right = + 1. Заниматься индивидуальными проектами при отсутствии иных рабочих задач. + 1. В [color=yellow][bold]жёлтый код[/bold][/color] уровня угрозы получить базовый доступ во все отсеки станции. + 1. Взламывать двери, чтобы в интересах следствия получить несанкционированный доступ к отсеку, по устному запросу Главы Службы Безопасности. + +guidebook-SOP-engineering-general-prohibited = + 1. Строить дополнительные источники питания, пока по крайней мере один источник питания не будет правильно подключен и настроен. + # Должностные СРП ## Старший инженер guidebook-SOP-ChiefEngineer-must = - 1. Удостовериться, что гравитационный сингулярный двигатель, и/или двигатель на антиматерии (ДАМ), и/или солнечные панели исправно снабжают станцию электроэнергией, перед тем как инженерный отдел будет предпринимать дальнейшие действия. - 1. Контролировать кадровые и материальные ресурсы отдела. Распределять сотрудников отдела по заявкам, полученным в устной форме лично или по общей рации. Следить за выполнением этих заявок. - 1. Следить за исправностью систем, не допускать продолжительное игнорирование неполадок с этими системами и их выход из строя. - 1. Следить за исправностью основного двигателя станции. Любая халатность, связанная с остановкой работы основного двигателя станции, без обстоятельств непреодолимой силы, карается немедленным увольнением и дисциплинарным взысканием по окончании смены. - 1. Руководить работами по восстановлению подачи электроэнергии в порядке важности, начиная с: система жизнеобеспечения, мостик, медицинский отдел, отдел службы безопасности. + 1. Удостовериться, что [tooltip="guidebook-SOP-engineering-tooltip-generator-main" text="основной"] и [tooltip="guidebook-SOP-engineering-tooltip-generator-spare" text="резервный"] источники питания правильно подключены и настроены, перед тем как отдел будет предпринимать дальнейшие действия. + 1. Не допускать игнорирования проблем, решение которых находится в компетенции инженерного отдела. + 1. В случае поступления заявок на ремонт распределить сотрудников отдела для выполнения заявки. + 1. Руководить работами по восстановлению подачи электроэнергии в порядке важности, начиная с: систем жизнеобеспечения, хранилища, мостика, медицинского отдела, отдела службы безопасности, прочие отделы. 1. Убедиться, что на станции всегда есть хотя бы один незадействованный укрепленный инженерный скафандр, если нет чрезвычайной ситуации, требующей использования всех костюмов. - 1. Обеспечивать сохранность и стабильную работу генератора гравитации станции. В случае потери генератора — срочно сообщить всем главам и написать отчёт ЦК с описанием обстоятельств. - 1. Обеспечивать сохранность и стабильную работу серверов телекоммуникации на станции. В случае неполадок отдать распоряжение на устранение в течение 10 минут либо заняться этим самостоятельно. - 1. Держать материальную базу отдела на контроле. Всегда иметь по 30 единиц базовых материалов в хранилище отдела для экстренного ремонта во время ЧС. + 1. Обеспечивать сохранность и стабильную работу генератора гравитации. В случае потери генератора — срочно сообщить всем главам и написать отчёт ЦК с описанием обстоятельств. + 1. Обеспечивать сохранность и стабильную работу серверов телекоммуникации на станции. + 1. В отсутствии или при чрезмерной нагрузке инженеров или атмосферных техников — временно заменять их, соблюдая требования их СРП. guidebook-SOP-ChiefEngineer-right = 1. Выдавать разрешение на строительство членам экипажа. - 1. Обозначить зону "небезопасно", если повреждения не позволяют завершить ремонт в приемлемый срок либо зона представляет смертельную опасность для неподготовленного члена экипажа. - 1. Совершить взлом в запрещённую территорию по устному запросу ГСБ о помощи следствию и требовать содействия от подчинённых. - 1. Совершать контролируемые разгерметизации любого масштаба для устранения ЧС при условии, что система жизнеобеспечения способна быстро восстановить безопасность помещения. + 1. При наличии подозрений на незаконную перестройку отсеков, в устной форме запросить у ответственного за отсек сопровождение для личной проверки или любую необходимую информацию. + 1. Совершать контролируемые разгерметизации любого масштаба для устранения атмосферной угрозы. + 1. Обладать правами всех сотрудников своего отдела. guidebook-SOP-ChiefEngineer-prohibited = - 1. Извлекать пожарный топор из шкафчика, если нет непосредственной угрозы жизни или необходимости срочного доступа к месту происшествия. После ликвидации угрозы топор должен быть возвращён. - 1. Выходить за пределы станции, не оставив заместителя, способного управлять отделом из числа атмосферных техников или инженеров. + 1. Извлекать пожарный топор из шкафчика без непосредственной угрозы жизни или необходимости срочного доступа. + 1. Выходить за пределы станции, не оставив заместителя, способного управлять отделом из числа сотрудников отдела. + +## Ведущий Инженер +guidebook-SOP-SeniorEngineer-must = + 1. Обеспечить усвоение теоретических знаний и их закрепление на практике всеми стажёрами отдела. + 1. При отсутствии стажёров, в отсутствии или при чрезмерной нагрузке инженеров или атмосферных техников — временно заменять их, соблюдая требования их СРП. + 1. Замещать Старшего Инженера в вопросах руководства отделом. +guidebook-SOP-SeniorEngineer-right = + 1. Проводить дообучение персонала отдела. + 1. Отдавать приказы сотрудникам отдела при выполнении указаний Старшего Инженера или при ЧС. +guidebook-SOP-SeniorEngineer-prohibited = + 1. Извлекать пожарный топор из шкафчика без непосредственной угрозы жизни или необходимости срочного доступа. ## Инженер guidebook-SOP-StationEngineer-must = - 1. Как можно быстрее добираться до места вызова с целью устранения поломки. - 1. Удостовериться, что гравитационный сингулярный двигатель, и/или двигатель на антиматерии (ДАМ), и/или солнечные панели исправно снабжают станцию электроэнергией. - 1. Периодически проверять гравитационный сингулярный двигатель, если он является выбранным источником энергии. - 1. Поддерживать работоспособность сети электропитания станции. - 1. Оперативно реагировать на любые повреждения во внешней обшивке, независимо от их размера. - 1. Обеспечивать сохранность и стабильную работу серверов телекоммуникации на станции. + 1. Оперативно реагировать на любые повреждения оборудования, электросетей, утилизационной трубопроводной системы и внутренних конструкций объекта. + 1. Как минимум раз в 30 минут проводить проверку состояния запущенных двигателей, основанных на Сингулярности или Тесле. + 1. Выполнять запросы других отделов по перестройке и улучшению отсеков при наличии соответствующего письменного разрешения от Старшего Инженера и ответственного за отсек. + 1. В отсутствии или при чрезмерной нагрузке атмосферных техников — временно заменять их, соблюдая требования СРП атмосферного техника. guidebook-SOP-StationEngineer-right = 1. Взламывать двери, чтобы получить несанкционированный доступ к отсеку, если данный отсек требует срочного ремонта, предупредив ответственного за отсек по рации или лично. - 1. Заниматься перестройкой или индивидуальными проектами при отсутствии повреждений, требующих ремонта. guidebook-SOP-StationEngineer-prohibited = - 1. Строить дополнительные источники питания (гравитационный сингулярный двигатель, двигатель на антиматерии или солнечные панели), пока по крайней мере один источник питания не будет правильно подключен и настроен. + 1. Носить скафандр отдела без необходимости его применения. ## Атмосферный техник guidebook-SOP-AtmosphericTechnician-must = - 1. Как можно быстрее добираться до места вызова для устранения разгерметизации или утечки газа. + 1. Оперативно реагировать на разгерметизации, утечки газа, повреждения газовой трубопроводной системы, прочие атмосферные неполадки. 1. Периодически проверять состав газов в распределительной трубопроводной системе на предмет аномалий в составе или температурном режиме. - 1. В случае отсутствия доступных инженеров — временно заменять их, соблюдая требования СРП инженера. - 1. Сообщать о любых отклонениях в атмосферной составляющей станции старшему инженеру и лишь затем приступать к исправлению. - 1. Оперативно реагировать на повреждения трубопроводов и обшивки станции. - 1. Оцеплять все опасные зоны атмосферным голопроектором и оповещать экипаж по общей рации. + 1. Сообщать о любых отклонениях в атмосферной составляющей объекта Старшему Инженеру и лишь затем приступать к исправлению. + 1. Оцеплять все зоны атмосферной угрозы, оповещать экипаж в общий канал связи о проводимых работах. + 1. В отсутствии или при чрезмерной нагрузке инженеров — временно заменять их, соблюдая требования СРП инженера. guidebook-SOP-AtmosphericTechnician-right = 1. Полностью перестроить систему жизнеобеспечения при условии, что она не будет перекачивать вредные газы никуда, кроме камер фильтрации или открытого космоса. + 1. Создавать контролируемые разгерметизации для устранения атмосферной угрозы. + 1. Потребовать у любого члена экипажа покинуть зону атмосферной угрозы, не вмешиваться в работу пожарных шлюзов и прочих мер сдерживания атмосферной угрозы. + - Отказ члена экипажа приравнивается к нарушению статьи 142 Корпоративного Закона. + 1. Запросить у Старшего Инженера или Капитана установку [color=yellow][bold]жёлтого кода[/bold][/color] уровня угрозы. guidebook-SOP-AtmosphericTechnician-prohibited = 1. Извлекать пожарный топор из шкафчика без непосредственной угрозы жизни или необходимости срочного доступа. - 1. Создавать летучие смеси с плазмой и кислородом вне камеры смешивания и переносить канистры с такими газами без сопровождения минимум одного вооружённого офицера СБ. + 1. Создавать летучие смеси с плазмой и кислородом вне камеры смешивания и переносить канистры с такими газами вне отдела без сопровождения минимум одного вооружённого офицера СБ. + 1. Без разрешения Старшего Инженера, создавать разгерметизации, затрагивающие целые отделы объекта. ## Технический ассистент guidebook-SOP-TechnicalAssistant-must = - 1. Содержать станцию в хорошем состоянии. + 1. Быть приписанным к старшему сотруднику отдела для обучения и прохождения практики. guidebook-SOP-TechnicalAssistant-right = - 1. При нехватке знаний попросить инженера или атмосферного техника выполнить задачу. + 1. В пределах своих сил и знаний помогать сотрудникам отдела с выполнением рабочих задач. guidebook-SOP-TechnicalAssistant-prohibited = - 1. Выходить за пределы станции без разрешения старшего инженера или возникновения чрезвычайной ситуации. + 1. Самостоятельно производить ремонт критически важного оборудования. + 1. Выходить за пределы станции без разрешения приписанного сотрудника. + 1. Носить скафандр отдела без необходимости его применения. + +# Tooltip +guidebook-SOP-engineering-tooltip-safety-precautions = Техника безопасности инженерного отдела: + - При работе с электричеством носить изолированные перчатки; + - При работе, предусматривающей нахождение в агрессивной внешней среде, использовать защитную экипировку; + - При работе в открытом космосе иметь при себе установленное в КПК приложение АстроНав или Глобальную Систему Позиционирования (ГСП). + +guidebook-SOP-engineering-tooltip-generator-main = Источник, способный запитать весь объект. Например: генераторы на основе Сингулярности или Теслы, ТЭГ, Суперматерия. + +guidebook-SOP-engineering-tooltip-generator-spare = Источник, способные поддерживать минимальный функционал объекта. Например: солнечные панели, ДАМ. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/general.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/general.ftl index d9603048eb..550f561f1f 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/general.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/general.ftl @@ -18,7 +18,7 @@ guidebook-SOP-general-basic-rights = guidebook-SOP-general-guests = {"[italic]«Права и обязанности гостей станции и ее территории»[/italic]"} - Представленные обязанности должны исполнятся всеми разумными существами, прибывшими на территорию станции, NanoTrasen обеспечивает реализацию всех нижеизложенных прав при условии надлежащего исполнения обязанностей. Ненадлежащее исполнение обязанностей или их неисполнение может повлечь привлечение к ответственности по статье «Проникновение на территорию объекта NanoTrasen» Корпоративного закона. + Представленные обязанности должны исполнятся всеми разумными существами, прибывшими на территорию станции, NanoTrasen обеспечивает реализацию всех нижеизложенных прав при условии надлежащего исполнения обязанностей. Ненадлежащее исполнение обязанностей или их неисполнение может повлечь привлечение к ответственности по статье «Проникновение на закрытую корпоративную территорию» Корпоративного закона. guidebook-SOP-general-guests-duties = 1. По прибытии на территорию станции сообщить о своем прибытии в общий канал связи. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl index 51077456ef..2d7f6765b6 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl @@ -1,26 +1,72 @@ # Должностные СРП # Магистрат guidebook-SOP-Magistrate-must = - 1. Ответить на запрос, полученный от лица, имеющего право обращения к магистрату. - 1. При необходимости принять меры одним из следующих методов: - - С помощью факса. Предпочтительный метод. - - Прибыть на объект лично, если задача не может быть решена удалённо. Обеспечить решение в кратчайшие сроки. - 1. Обеспечивать защиту прав существ, обладающих ОПРС. + 1. Инициировать корпоративный суд при передаче службой безопасности дел с обвинениями по статьям категории XX2 и выше при действии зелёного или синего кода. В условиях красного кода инициирование корпоративного суда допускается только по делам с обвинениями категории XX3 и выше. + - В случае невозможности проведения суда по причине чрезмерной нагрузки дел, магистрат обязан передать дела с обвинениями в службу безопасности для вынесения вердикта. + - В случае отсутствия или недееспособности магистрата данные дела автоматически переходят в юрисдикцию службы безопасности. + - В условиях чрезвычайной ситуации инициирование корпоративного суда запрещено. + 1. Соблюдать установленные процедуры корпоративного суда и вынесения вердикта. + 1. Быть [tooltip="guidebook-SOP-Magistrate-tooltip-equanimity" text="беспристрастным"]. + 1. Обеспечить правильное применение Корпоративного закона и защиту прав существ, обладающих ОПРС, на объектах корпорации NanoTrasen + 1. На постоянной основе использовать [tooltip="guidebook-SOP-command-tooltip-business-speech" text="Корпоративно-деловой стиль речи"]. + - Данный пункт утрачивает свою актуальность в условиях чрезвычайной ситуации + 1. Соблюдать утверждённый [tooltip="guidebook-SOP-Magistrate-tooltip-dress-code" text="дресс-код"]. + 1. Осуществлять контроль за деятельностью адвоката в рамках его СРП. guidebook-SOP-Magistrate-right = - 1. Вольно трактовать Корпоративный Закон, выносить и отменять вердикты в отношении лиц, пребывающих под следствием или отбывающих своё наказание. - 1. Руководить агентами внутренних дел на объектах NanoTrasen. - 1. Инициировать проведение судебного заседания согласно соответствующей процедуре, при невозможности решения правового спора иными методами. - 1. Хранить при себе любые виды вооружения и средств самообороны вне зависимости от кода. - 1. Пересмотреть любой документ, оформленный и заверенный в пределах станции, при необходимости признать его недействительным. - 1. В одностороннем порядке предоставить лицу юридическую неприкосновенность либо лишить её. - - Магистрат не может использовать данное право в отношении самого себя. + 1. Для судебного разбирательства магистрат вправе выписывать ордера на арест, обыск личности или отдела, задержание, исключительно в целях обеспечения рассмотрения дела, как по собственной инициативе при наличии оснований, так и по обоснованному запросу службы безопасности. + 1. Отменять любое решение по вопросам, касающимся Корпоративного закона и ОПРС, включая УДО. + - Исключения: приказы Центрального командования и сотрудников ДСО. + 1. Хранить при себе вспышку и использовать её в целях самообороны. + 1. Во время судебного разбирательства, запросить у службы безопасности любые материалы по делу, включая отчёты, доказательства и свидетельские показания, а также материалы, относящиеся к уже заверенному приговору, для анализа и принятия решения. + - В условиях [bold][color=green]зелёного[/color][/bold] или [bold][color=dodgerblue]синего кода[/color][/bold] запросить составление документов по материалу делу. + 1. В условиях [bold][color=green]зелёного[/color][/bold] или [bold][color=dodgerblue]синего кода[/color][/bold], запросить для проверки юридической законности любой документ, составленный на объекте корпорации NanoTrasen. + 1. Отменить юридическую законность любого документа на объекте корпорации NanoTrasen, исключения документы, заверенные печатью Центком. + - В случае отмены документа магистрат обязан написать причину отзыва документа непосредственно в самом документе и заверить документ печатью магистрата. + 1. Проверять любой вынесенный на станции приговор и, при выявлении нарушений, отменять его с обязательным указанием причины + - В случае отмены магистрат обязан составить новый приговор от своего лица, основываясь на материалах дела, доказательств и соблюдении корпоративного закона. + 1. Публично объявить о предстоящем суде, называет место и время начала процедуры. + 1. Рассматривать обращения о досрочном освобождении и утверждать УДО заключённых в соответствии с установленной процедурой выдачи УДО. + 1. Редактировать документы по запросу персонала станции с помощью ручки юридического департамента. + - Редактирование собственных и чужих документов допустимо в случае необходимости исправления ошибок в тексте (без изменения исходного содержания). + - Редактирование чужих документов по аналогичной процедуре. + - В редактированном документе должно быть добавлено [tooltip="guidebook-SOP-legal-tooltip-edit-note" text="примечание о редактировании"], он должен быть заверен печатью магистрата. guidebook-SOP-Magistrate-prohibited = - 1. Принимать решения, противоречащие ОПРС. - 1. Выполнять функции службы безопасности, исключая функции, предусмотренные дозволениями. - 1. Без веских причин отменять действие документов. - 1. Отдавать приказы персоналу станции. Исключение — сотрудники юридического департамента. + 1. Принимать решения, противоречащие Корпоративному закону или ОПРС. + - Выносить некорректные или неправомерные вердикты. + 1. Вмешиваться в деятельность глав отделов, капитана или службы безопасности без [tooltip="guidebook-SOP-Magistrate-tooltip-legal-grounds" text="законных оснований"]. + 1. Проводить допросы по процедуре допроса. + 1. Злопотреблять доступом. + +# Адвокат +guidebook-SOP-Lawyer-must = + 1. Обеспечивать правовую защиту клиентов в рамках действующего Корпоративного Законодательства NanoTrasen, защищать права ОПРС. + 1. Соблюдать [tooltip="guidebook-SOP-service-tooltip-Lawyer-secret" text="адвокатскую тайну"] за исключением случаев, когда информация может нанести прямой ущерб NanoTrasen. + - Нарушения этого пункта расценивается как нарушения статьи КЗ: 144. + 1. Соблюдать утверждённый [tooltip="guidebook-SOP-service-tooltip-Lawyer-dress-code" text="Корпоративный дресс-код"]. + - Обязательные элементы униформы могут исключаться или заменяться в случае физиологических или видовых особенностей сотрудника. + - Запрещено передавать или обменивать служебную униформу третьим лицам. + 1. Вести общение с клиентами, сотрудниками службы безопасности и командованием станции в уважительной форме, соблюдая [tooltip="guidebook-SOP-service-tooltip-Lawyer-business-style" text="Корпоративно-деловой стиль речи"]. + - Данный пункт утрачивает свою актуальность в условиях ЧС. + 1. Оказывать консультации по правовым вопросам существам защищённым ОПРС. + 1. Содействовать клиентам в составлении, оформлении и заверении документации. + +guidebook-SOP-Lawyer-right = + 1. Присутствовать на территории брига, а также при проведении процедур службы безопасности, касающихся клиента. + - Адвокат имеет право явиться в бриг по вызову любого разумного существа, находящегося под арестом или в заключении, для оказания консультации, заключения контрактов и обеспечения юридической защиты. + 1. Заключать юридические контракты о предоставлении защиты с сотрудниками и иными лицами. + - Допускается заключение как устных, так и письменных контрактов. + - Письменный контракт имеет приоритет в случае разногласий. + - Письменный контракт должен содержать чётко определённые условия оказания защиты. + - Адвокат обязан хранить экземпляр письменного контракта (при его наличии) и предоставлять его по требованию службы безопасности. + 1. Запрашивать у службы безопасности любые материалы дела, относящиеся к защите клиента. + 1. Расторгнуть контракт о юридической защите в случае, когда клиент сознательно предоставляет ложные сведения или препятствует ведению защиты. + +guidebook-SOP-Lawyer-prohibited = + 1. Злоупотреблять доступом на территории брига или использовать его вне профессиональной деятельности. + - Нарушения этого пункта расценивается как нарушения статьи КЗ: 423. + 1. Использовать адвокатскую деятельность в целях личной выгоды, не связанных с защитой клиента. # АВД guidebook-SOP-IAA-must = @@ -57,10 +103,10 @@ guidebook-SOP-IAA-right = 1. Хранить при себе и использовать для исправления заверенных документов ручку «центком»: - Изменение собственных документов допустимо в случае необходимости исправления ошибок в тексте (без изменения исходного содержания). - Изменение чужих документов допустимо по аналогичной причине, но возможно лишь по прямому запросу лица, заверившего документ. - - В исправленный документ должно быть добавлено [tooltip="guidebook-SOP-legal-tooltip-iaa-edit-note" text="примечание о редактировании"], он должен быть заверен печатью АВД. + - В исправленный документ должно быть добавлено [tooltip="guidebook-SOP-legal-tooltip-edit-note" text="примечание о редактировании"], он должен быть заверен печатью АВД. 1. Запросить помощь ЦК/ДСО/юридического департамента при определённых условиях. - Запросить вызов ПЦК в случае бездействия руководства станции по факту выявленных нарушений. - - Запросить помощь магистрата в соответствии со специальной процедурой. + - Запросить помощь Магистрата в соответствии со специальной процедурой. - Запросить помощь подразделений ДСО, если в данный момент капитан и главы станции недоступны. 1. Отправлять Центральному Командованию отчёты о ситуации на станции с помощью факса, но не чаще чем один раз в 30 минут. @@ -92,94 +138,122 @@ guidebook-SOP-legal-procedure-internal-investigation = 1. По завершению расследования АВД обязан вернуть ключ шифрования отдела и сдать полученные доступы. 1. АВД должен составить письменный отчёт о проведённом расследовании и предоставить копии главе отдела и капитану для принятия мер по факту выявленных нарушений. -## Процедура обращения к магистрату -guidebook-SOP-legal-procedure-appeal-to-magistrate = - 1. Обращение к магистрату может быть направлено исключительно лицами, имеющими такое право, по одной из указанных причин: - - Возникновение ситуации, при которой Корпоративный Закон не способен защитить ущемление ОПРС какого-либо лица. - - Необходимость оспорить приговор, вынесенный капитаном. - - Необходимость отстранить от работы агента внутренних дел при нарушении им ОПРС, КЗ или СРП. - - Необходимость проведения судебной процедуры. - 1. Обращение должно быть оформлено соответствующим документом и отправлено по факсу, а оператор Центрального Командования должен быть проинформирован о его отправке. - 1. На основании обращения магистрат может поступить следующим образом: - - Принять решение удалённо, затребовав от АВД сбор необходимых материалов для вынесения вердикта. - - Лично прибыть на станцию для решения поставленной задачи. - -## Процедура суда +## Процедура корпоративного суда ### Общие положения guidebook-SOP-legal-court-general = + {"[italic]Корпоративный суд — это официальное разбирательство под руководством магистрата, проводимое в рамках законов NanoTrasen.[/italic]"} + 1. Судебный процесс не может быть инициирован во время чрезвычайной ситуации. + - В случае, если во время судебного заседания, была объявлена чрезвычайная ситуация, суд должен отложен до снятия ЧС. + 1. Процесс корпоративного суда влияет на срок отбывания наказания. Первые 10 минут судебного заседания не учитываются в срок заключения. По истечении 10 минут, дальнейшее время судебного разбирательства засчитывается в срок отбывания наказания. В случае если оставшийся срок заключения в ходе судебного заседания достигает 0, заключённый признаётся полностью отбывшим наказание и подлежит освобождению. + - Пример: общий срок заключения составляет 30 минут. Если судебное заседание длится 20 минут, в срок заключения засчитываются только 10 минут, и оставшийся срок составит 20 минут. + 1. Все судебные процессы должны быть проведены в зале суда. + - В случае если на объекте корпорации отсутствует зал суда или он повреждён, допустимо провести суд в другом публичном месте: церковь, сцена и т.д. + 1. Все суды должны быть открыты для зрителей. + 1. Суд не может длиться дольше 20 минут. По истечению срока магистрат обязан вынести вердикт. + 1. Все участники суда обязаны исполнять требования магистрата. + 1. Все участники суда должны обращаться к магистрату, используя обращение "Ваша честь, магистрат, судья, уважаемый суд". Нарушение данного требования может быть расценено как нарушение статьи 121 "Неуважение к суду" корпоративного закона. + - Магистрат вправе отменить данную форму обращения. + 1. Все участники суда должны говорить исключительно с разрешения магистрата. Нарушение данного требования может быть расценено как нарушение статьи 121 "Неуважение к суду" корпоративного закона. + 1. Все участники суда должны вести себя подобающим образом. Любые случаи неподобающего и вызывающего поведения могут быт расценены как нарушение статьи 121 "Неуважение к суду" корпоративного закона. + - Все участники обязуются говорить искренне, дача ложных показаний, либо намеренное искажение информации, являются нарушением статьи 223 "Дача ложных показаний" корпоративного закона + +### Общие нормы суда +guidebook-SOP-legal-court-norms = 1. Подсудимый считается невиновным, пока в его отношении не будет зачитан обвинительный приговор. - 1. Судебный процесс должен быть открытым либо проходить в закрытом формате. Формат суда определяется судьёй. - 1. Проведение суда возможно, если мерой наказания для задержанного могут быть избраны пожизненное заключение или высшая мера наказания. + 1. Проведение суда невозможна без судьи в лице магистрата. В случае отсутствия магистрата служба безопасности действует по протоколу вынесение вердикта на допросе. 1. Для полноценной реализации судебного процесса требуется наличие нескольких сторон. Без упомянутых лиц проведение процедуры суда невозможно: - - Судья — выносит решение по делу, руководит процессом суда. Таковым является магистрат; в случаях, при которых личное присутствие магистрата невозможно, в качестве судьи могут выступать капитан, Представитель ЦК, лидер ОБР. - - Сторона обвинения — выдвигает обвинение по делу, предоставляет доказательства, подтверждающие виновность подсудимого лица. В качестве данной стороны выступает глава Службы Безопасности. - - Сторона защиты — сторона, которой было выдвинуто обвинение. Представлена лицом, которому выдвинуто обвинение, а также адвокатом, если таковой имеется. - 1. Также в суде принимают участие лица, содействующие осуществлению правосудия, приглашённые стороной обвинения или защиты. Отказ данных лиц от участия в суде может быть расценён как нарушение статьи «Сопротивление органам власти» Корпоративного Закона. + - Судья — выносит решение по делу, руководит процессом суда. Таковым является магистрат. + - Сторона обвинения — выдвигает обвинение по делу, предоставляет доказательства, подтверждающие виновность подсудимого лица. В качестве данной стороны выступает: Капитан, Глава Службы Безопасности, Смотритель, прочие сотрудники службы безопасности с разрешения вышестоящих по должности в качестве обвинителя. + - Сторона защиты — сторона, которой было выдвинуто обвинение. Представлена лицом, которому выдвинуто обвинение, а также адвокатом. В качестве адвоката может выступать: адвокат, глава отдела, капитан. Присутствие адвоката необязательно. + - В суде принимают участие лица, содействующие осуществлению правосудия, приглашенные стороной обвинения или защиты. Отказ данных лиц от участия в суде может быть расценен как нарушение статьи 112 "Сопротивление органам власти" корпоративного закона. - Потерпевшие — лица, ставшие жертвами преступления и пострадавшие в ходе обстоятельств дела. - - Эксперты — лица, привлечённые к исследованию вещественных доказательств, физического состояния потерпевших и подсудимого, оценке ущерба от совершённого преступления и т.д. - - Свидетели — лица, обладающие информацией по делу, полученной при помощи органов чувств. + - Эксперты — лица, привлеченные и исследованию вещественных доказательств, физического состояния потерпевших и подсудимого, оценке ущерба от совершенного преступления и т.д. + - Свидетели — лица, обладающее информацией по делу, полученной при помощи органов чувств. -### Нормы поведения в суде -guidebook-SOP-legal-court-behavior = - 1. Все участники суда должны обращаться к судье, используя обращение «Ваша честь». Нарушение данного требования может быть расценено как нарушение статьи «Неуважение к суду» Корпоративного Закона. Судья вправе отменить данную форму обращения. - 1. Все участники суда должны говорить исключительно с разрешения судьи. Нарушение данного требования может быть расценено как нарушение статьи «Неуважение к суду» Корпоративного Закона. - 1. Все участники суда должны вести себя подобающим образом. Любые случаи неподобающего и вызывающего поведения могут быть расценены как нарушение статьи «Неуважение к суду» Корпоративного Закона. - 1. Все участники обязуются говорить искренне; дача ложных показаний либо намеренное искажение информации являются нарушением статьи «Крупное мошенничество» Корпоративного Закона. +### Обязанности сторон +guidebook-SOP-legal-court-responsibilities = + {"Магистрат обязан:"} + 1. Открыть заседание и объявить стороны процесса. + 1. Предоставить первое слово стороне обвинения. + 1. После изложения обвинения уточнить у подсудимого, согласен ли он с обвинением и изложенными фактами. + 1. Предоставить слово стороне защиты. + 1. Руководить допросом свидетелей и экспертов, контролируя порядок в суде. + 1. Предоставить сторонам время для прений: первой выступает сторона обвинения, затем защита. + 1. Предоставить подсудимому последнее слово. + 1. Вынести и огласить приговор. + 1. Объявить заседание закрытым. + {"Сторона обвинения обязана:"} + 1. Первой изложить обвинение и предоставить доказательства. + 1. По необходимости пригласить потерпевших и свидетелей. + 1. Сформулировать требуемую меру наказания в прениях. + {"Сторона защиты обязана:"} + 1. После обвинения изложить свою позицию. + 1. Предоставить доказательства в защиту подсудимого. + 1. Пригласить свидетелей защиты (при наличии). + 1. В прениях указать на смягчающие обстоятельства или требовать оправдания. + {"Подсудимый имеет право:"} + 1. Озвучить свою позицию после выступления стороны обвинения. + 1. Давать показания и представлять доказательства. + 1. Воспользоваться последним словом перед вынесением приговора. -### Досудебный этап -guidebook-SOP-legal-court-open = - 1. Открытый судебный процесс не может быть инициирован в условиях угрозы уровня [bold][color=red]красного кода[/color][/bold] или [color=gold][bold]гамма и выше[/bold][/color]. При объявлении [bold][color=red]красного кода[/color][/bold] во время процедуры суд должен быть перенесён до снижения кода угрозы до [bold][color=dodgerblue]синего[/color][/bold]. - 1. Судья публично объявляет о предстоящем суде, называет место и время начала процедуры. С момента объявления и до начала суда должно пройти не менее 10 минут. - 1. Подсудимый до начала суда должен находиться под стражей. Время пребывания под стражей должно быть учтено при вынесении приговора. - 1. Обязательные участники суда должны прибыть в зал суда за 5 минут до начала слушания. - 1. Лица, содействующие осуществлению правосудия, могут явиться до начала слушания без опозданий. - 1. В открытом судебном процессе допустимо присутствие зрителей в зале суда. +## Судебное разбирательство +### Судебное разбирательство +guidebook-SOP-legal-court-litigation = + {"[italic]Судебное разбирательство — процесс, в ходе которого магистрат изучает материалы дела, заслушивает показания сторон, свидетелей и экспертов, оценивает доказательства и принимает решение о виновности или невиновности подсудимого.[/italic]"} + 1. Для вынесения обвинительного приговора необходимо наличие доказательной базы, подтверждающей вину подсудимого. + 1. При рассмотрении дела учитываются как прямые, так и косвенные доказательства. + 1. Доказательства признаются состоятельными, если подтверждают факт совершения преступления и не противоречат друг другу. + 1. В случае противоречий материалы считаются недействительными. + 1. При отрицании подсудимым своей вины именно доказательства должны являться достаточными для её подтверждения. + 1. Если доказательств недостаточно — подсудимый признаётся невиновным. + 1. Прямые доказательства: + - Сам объект или субъект преступления (потерпевший). + - Вещественные доказательства: орудие преступления, украденное имущество и д.р. + - Показания минимум трёх свидетелей с совпадающими деталями. + - Показания боргов (ИИ, киборгов). + - Заключения экспертов. + - Явка с повинной. + - Биологические следы: отпечатки пальцев, образцы ДНК, следы крови и т.п. + - Аудиозаписи и фотографии. + 1. Косвенные доказательства: + - Наличие у подсудимого мотива. + - Отсутствие алиби. + - Негативные личные отношения с жертвой. + - Тяжёлое материальное положение. + - Наличие дисциплинарных взысканий, выговоров. + - Судимости. + - Состояние алкогольного или наркотического опьянения. + 1. Обвинение может быть снято магистратом на этапе судебного разбирательства при наличии одного или нескольких из следующих факторов: + - Недостаток прямых доказательств. + - Отсутствует субъект преступления или орудие преступления. + - Нет вещественных доказательств, подтверждающих факт нарушения. + - Показания свидетелей (минимум трёх) противоречивы или не соответствуют фактам. + - Биологические следы не подтверждают участие подсудимого. + - Сомнительность косвенных доказательств. + - Мотив не подтверждён другими фактами. + - Нет убедительных свидетельств отсутствия алиби. + - Негативные отношения с жертвой, материальное положение, дисциплинарные взыскания, судимости или опьянение не подтверждают факт совершения преступления. + - Противоречие между доказательствами. + - Прямые доказательства противоречат друг другу. + - Косвенные доказательства опровергают прямые факты. + - Явная невиновность подсудимого. + - Подсудимый признаёт факт, но доказательства указывают на то, что он не совершал преступления. + - Любой факт, который исключает вину подсудимого по сути или обстоятельствам дела. - {"[head=3]Подготовка к слушанию[/head]"} - 1. Если процедура проводится в условиях [bold][color=dodgerblue]синего кода[/color][/bold], то все участники суда, не имеющие юридической неприкосновенности, должны быть подвергнуты процедуре обыска перед посещением зала суда. - 1. Перед началом слушания все участники должны занять места в зале. - 1. Подсудимый может быть закован в наручники и должен находиться под контролем офицера СБ. - 1. Все участники суда должны исполнять требования судьи. - 1. Лица, содействующие осуществлению правосудия, и зрители могут быть исключены из зала суда по решению судьи. - - {"[head=3]Слушание[/head]"} - 1. Судья открывает слушание: зачитывает вступительную речь, информирует, в каком порядке пройдёт суд, оглашает правила поведения в суде. - 1. Сторона обвинения представляет материалы дела. - 1. Сторона защиты представляет свои интересы. - 1. Судья приглашает лиц, содействующих осуществлению правосудия, для дачи показаний. - 1. Судья уточняет, будут ли стороны приобщать дополнительные материалы к делу. - 1. Подсудимый говорит последнее слово. - - В своём последнем слове подсудимый может признаться в преступлении и просить снисхождения, настаивать на своей невиновности либо вовсе промолчать. - 1. Судья оглашает приговор. - - {"[head=3]Приговор[/head]"} - 1. При вынесении приговора судья должен руководствоваться материалами дела, чтобы установить виновность или невиновность подсудимого, и на основании полученных данных озвучить приговор. - 1. Приговор вступает в силу сразу после его оглашения. - - В случае обвинительного приговора подсудимый признаётся виновным в совершении преступления и отправляется отбывать наказание с учётом времени, проведённого в бриге до начала слушания. - - В случае оправдательного приговора подсудимый признаётся невиновным и должен быть немедленно освобождён, личное имущество подсудимого должно быть возвращено в кратчайшие сроки. - 1. После оглашения приговора суд считается завершённым. - - В течение 10 минут судья должен составить приговор в письменном виде, предоставить его копии стороне обвинения и защиты. - -### Досудебный этап -guidebook-SOP-legal-court-closed = - 1. Закрытый судебный процесс не может быть инициирован в условиях угрозы уровня [color=gold][bold]гамма кода[/bold][/color]. При объявлении гамма-кода во время процедуры суд должен быть перенесён до снижения кода угрозы до [bold][color=red]красного кода[/color][/bold]. - 1. Судья должен огласить место проведения слушания и время его начала участникам суда либо обеспечить получение информации данными лицами. Слушание может быть начато сразу же после объявления. - - {"[head=3]Слушание[/head]"} - 1. Вне зависимости от кода угрозы все участники суда, не имеющие юридической неприкосновенности, должны быть подвергнуты процедуре обыска. - 1. На постоянной основе в месте проведения суда находятся лишь обязательные участники слушания. - 1. Лица, содействующие осуществлению правосудия, вызываются в зал суда по одному; после дачи показаний они покидают суд. - 1. Судья проводит опрос сторон суда в удобном ему порядке. - 1. Судья объявляет о готовности вынести вердикт по делу. - - {"[head=3]Приговор[/head]"} - 1. При вынесении приговора судья должен руководствоваться материалами дела, чтобы установить виновность или невиновность подсудимого, и на основании полученных данных озвучить приговор. - 1. Приговор вступает в силу сразу после его оглашения. - - В случае обвинительного приговора подсудимый признаётся виновным в совершении преступления и отправляется отбывать наказание с учётом времени, проведённого в бриге до начала слушания. - - В случае оправдательного приговора подсудимый признаётся невиновным и должен быть немедленно освобождён, личное имущество подсудимого должно быть возвращено в кратчайшие сроки. - 1. После оглашения приговора суд считается завершённым. - - В течение 10 минут судья должен составить приговор в письменном виде, предоставить его копии стороне обвинения и защиты. +### Вынесение вердикта +guidebook-SOP-legal-court-verdict = + {"[italic]Вердикт — официальное решение магистрата о виновности или невиновности подсудимого, который может быть оспорен только Центральным Командованием.[/italic]"} + 1. Порядок: + - После анализа дела магистрат формирует вывод. + - Приговор оглашается в присутствии сторон. + - Оглашение должно содержать: квалификацию совершённого деяния (со статьями корпоративного закона), указание на смягчающие/отягчающие модификаторы при наличии. + 1. Приговор подлежит обязательному письменному составлением магистратом и передаче копии в службу безопасности. + - Исключение составляют случаи действия чрезвычайных ситуаций. # Tooltip -guidebook-SOP-legal-tooltip-iaa-edit-note = В документ над полем для печатей должна быть добавлена строка, в которой будет указано, что документ был отредактирован; в строке должны быть указаны ФИ АВД. +guidebook-SOP-legal-tooltip-edit-note = В документ над полем для печатей должна быть добавлена строка, в которой будет указано, что документ был отредактирован; в строке должны быть указаны ФИ редактора. guidebook-SOP-legal-tooltip-doc-forgery = Данный пункт подразумевает любое редактирование заверенного документа (с печатью), изменяющее его изначальный смысл, либо изменение документа без согласия лица, заверившего его своей печатью. + +guidebook-SOP-Magistrate-tooltip-equanimity = Рассматривать судебные дела объективно, не допуская личной заинтересованности или влияния внешних факторов на вынесение решения. В случае наличия личной заинтересованности, конфликта интересов или иных отягощающих факторов, магистрат обязан заявить самоотвод и передать дело иному уполномоченному лицу либо приостановить разбирательство до устранения указанных обстоятельств. +guidebook-SOP-Magistrate-tooltip-dress-code = Мантия судьи, парик, значок адвоката. +guidebook-SOP-Magistrate-tooltip-legal-grounds = Является необходимость проведения следствия или инспекция без вмешательства в ход работы. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl index 40773619f7..28c24ac427 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl @@ -10,6 +10,10 @@ guidebook-SOP-security-general-must = 1. Исполнять законные требования представителей юридического департамента и Центрального Командования, а также требования вышестоящих по иерархии лиц. 1. По требованию смотрителя или главы службы безопасности предъявить личные вещи для досмотра в любой момент времени. 1. В случае временного отстранения от службы прекратить исполнение текущей задачи и обеспечить скорейшее устранение фактора, повлекшего отстранение. В случае отстранения ввиду нарушения КЗ или СРП сотрудник обязан явиться в бриг в кратчайшие сроки. + 1. Передавать задержанных, обвиняемых по статьям категории XX2 и выше, в корпоративный суд. + - В условиях [color=red][bold]красного кода[/bold][/color] передавать задержанных в корпоративный суд по делам с обвинениями категории XX3 и выше. + - В условиях чрезвычайной ситуации передача задержанных в корпоративный суд не осуществляется. + - В отсутвие или недееспособности магистрата данный пункт игнорируется. guidebook-SOP-security-general-right = 1. Использовать снаряжение, разрешённое в рамках текущего уровня угрозы. @@ -386,6 +390,7 @@ guidebook-SOP-security-procedure-investigation-interrogation = - Сокращение срока не предусмотрено, если итоговый срок заключения, к которому может быть приговорён подозреваемый составляет более 75 минут. - Суммарное время допроса не может превышать срок, равный 10 минутам в сумме с потенциальным сроком заключения, к которому подозреваемый может быть приговорён при подтверждении его виновности. - Если в результате сокращения срока заключения фактический срок заключения равен 0, подозреваемый должен быть признан как отбывший срок заключения в полном объеме. + - В случае направления задержанного в корпоративный суд, допрос ограничивается сроком до 5 минут, после чего задержанный должен быть незамедлительно направлен в суд. Магистрат может отменить это требование в случае занятости судебными делами. 1. В процедуре допроса принимают участие задержанный, допрашивающий, а также адвокат, свидетели и эксперты, если таковые имеются. - В роли допрашивающего может выступать смотритель, помощник смотрителя, глава службы безопасности, капитан, либо иной сотрудник службы безопасности — с разрешения ранее упомянутых лиц. - При нарушении порядка проведения процедуры агент внутренних дел уполномочен отстранить допрашивающего от дальнейшего проведения допроса, в таком случае проведение допроса должно быть передано вышестоящему сотруднику, вплоть до капитана. Агент внутренних дел не может отстранить допрашивающего в [color=red][bold]красном коде[/bold][/color] и выше. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl index fec05b3140..5a2f7c0d99 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl @@ -40,35 +40,6 @@ guidebook-SOP-HeadOfPersonnel-prohibited = 1. Вне чрезвычайных ситуаций покидать рабочее место, если в очереди к нему стоит персонал. 1. Заниматься работой Службы Безопасности, кроме случая, описанного в пункте 7 дозволений. -## Адвокат -guidebook-SOP-Lawyer-must = - 1. Обеспечивать правовую защиту клиентов в рамках действующего Корпоративного Законодательства NanoTrasen, защищать права ОПРС. - 1. Соблюдать [tooltip="guidebook-SOP-service-tooltip-Lawyer-secret" text="адвокатскую тайну"] за исключением случаев, когда информация может нанести прямой ущерб NanoTrasen. - - Нарушения этого пункта расценивается как нарушения статьи КЗ: 144. - 1. Соблюдать утверждённый [tooltip="guidebook-SOP-service-tooltip-Lawyer-dress-code" text="Корпоративный дресс-код"]. - - Обязательные элементы униформы могут исключаться или заменяться в случае физиологических или видовых особенностей сотрудника. - - Запрещено передавать или обменивать служебную униформу третьим лицам. - 1. Вести общение с клиентами, сотрудниками службы безопасности и командованием станции в уважительной форме, соблюдая [tooltip="guidebook-SOP-service-tooltip-Lawyer-business-style" text="Корпоративно-деловой стиль речи"]. - - Данный пункт утрачивает свою актуальность в условиях ЧС. - 1. Оказывать консультации по правовым вопросам существам защищённым ОПРС. - 1. Содействовать клиентам в составлении, оформлении и заверении документации. - -guidebook-SOP-Lawyer-right = - 1. Присутствовать на территории брига, а также при проведении процедур службы безопасности, касающихся клиента. - - Адвокат имеет право явиться в бриг по вызову любого разумного существа, находящегося под арестом или в заключении, для оказания консультации, заключения контрактов и обеспечения юридической защиты. - 1. Заключать юридические контракты о предоставлении защиты с сотрудниками и иными лицами. - - Допускается заключение как устных, так и письменных контрактов. - - Письменный контракт имеет приоритет в случае разногласий. - - Письменный контракт должен содержать чётко определённые условия оказания защиты. - - Адвокат обязан хранить экземпляр письменного контракта (при его наличии) и предоставлять его по требованию службы безопасности. - 1. Запрашивать у службы безопасности любые материалы дела, относящиеся к защите клиента. - 1. Расторгнуть контракт о юридической защите в случае, когда клиент сознательно предоставляет ложные сведения или препятствует ведению защиты. - -guidebook-SOP-Lawyer-prohibited = - 1. Злоупотреблять доступом на территории брига или использовать его вне профессиональной деятельности. - - Нарушения этого пункта расценивается как нарушения статьи КЗ: 423. - 1. Использовать адвокатскую деятельность в целях личной выгоды, не связанных с защитой клиента. - ## Радиоведущий guidebook-SOP-RadioHost-must = 1. Обеспечивать стабильную и корректную работу радиоканала, информировать экипаж станции о событиях на объекте корпорации, проводить прямые радиоэфиры. @@ -96,16 +67,17 @@ guidebook-SOP-RadioHost-prohibited = ## Сервисный работник guidebook-SOP-ServiceWorker-must = - 1. Пополнять торговые автоматы в пределах сервисного отдела. - 1. Своевременно отвечать на вызовы по рации для пополнения автоматов. - 1. Оказывать помощь в работе другим сотрудникам сервисного отдела с полным соблюдением их СРП. + 1. Выполнять поручения и задачи в пределах сервисного отдела. + 1. По решению главы персонала, капитана или по собственной инициативе в случае отсутствия сотрудников бара, кухни — исполнять обязанности шеф-повара, бармена. + - Допускается замещение других должностей сервисного отдела при согласовании с главой персонала. + - При замещении должности исполнять все СРП соответствующей роли. + - При замещении должности шеф-повара или бармена, сервисный работник обязан выполнять указания штатных сотрудников этой должности, если они не противоречат СРП и КЗ. + 1. Покинуть зону работы по требованию основного сотрудника этой зоны или по приказу главы персонала. guidebook-SOP-ServiceWorker-right = - 1. Запросить у главы персонала дополнительные доступы сервисного отдела для повышения эффективности работы. - 1. В свободное от обязанностей время проходить обучение навыкам сервисного отдела для повышения эффективности работы. - -guidebook-SOP-ServiceWorker-prohibited = - 1. Хранить при себе какие-либо предметы, регулируемые КЗ, разрешенные для использования другим сотрудникам отдела. + 1. Согласовать с главой персонала получение доступов в отделы сервиса, необходимые для эффективного выполнения обязанностей. + 1. Использовать оборудование, снаряжение и инвентарь замещаемой должности в рамках КЗ и СРП этой должности. + 1. Получить приписку к своей должности в соответствии с выполняемой ролью. Пример: "Повар — Сервисный работник". ## Клоун guidebook-SOP-Clown-must = diff --git a/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl b/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl index 0ece5dd9b5..4ec478e50e 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl @@ -37,7 +37,7 @@ guidebook-corporatelaw-115-note = - Квалифицированным персоналом считаются: сотрудники службы безопасности, члены командования, иные члены экипажа — в зависимости от обстоятельств. - Актуальный список чрезвычайных ситуаций. -guidebook-corporatelaw-116-desc = [italic]Вооруженное выступление, возникшее стихийно или в результате заговора.[/italic] +guidebook-corporatelaw-116-desc = [italic]Вооруженное выступление группы людей, создающее реальную угрозу законной власти корпорации, возникшее стихийно или в результате заговора.[/italic] guidebook-corporatelaw-116-note = { -guidebook-corporatelaw-note } - Выступление считается вооруженным, если его участники имеют огнестрельное или холодное оружие, инструменты, способные быстро нанести серьезный вред здоровью. @@ -101,7 +101,6 @@ guidebook-corporatelaw-135-note = - Синдикат, - Пиратские объединения, - Петрищевцы, - - Лица, официально поддерживающие СНК, - Расистские объединения. guidebook-corporatelaw-136-desc = [italic]Нанесение значительного ущерба станции, сопровождающаяся повреждением или уничтожением оборудования, создание условий невыполнимости цели или полной неработоспособности станции.[/italic] @@ -231,11 +230,16 @@ guidebook-corporatelaw-314-note = - Для классификации редкости рекомендуется использовать критерии: возможность производства, заказа или добычи указанного предмета, сложность добычи. - Для классификации важности рекомендуется использовать критерии: наличие аналогов украденному предмету, возможность быстрой замены украденного предмета. -guidebook-corporatelaw-315-desc = [italic]Нападение в целях хищения чужого имущества, совершенное с применением насилия, опасного для жизни или здоровья, либо с угрозой применения такого насилия.[/italic] +guidebook-corporatelaw-315-desc = [italic]Незаконное присвоение особо ценного или же критически важного оборудования и имущества станции, шаттлов.[/italic] guidebook-corporatelaw-315-note = { -guidebook-corporatelaw-note } - - При факте разбоя не применяются статьи: 212, 213, 214. - - Любые насильственные действия, произошедшие после нарушения статьи "Разбой" рассматриваются как правонарушения, не связанные с разбоем, что позволяет применять статьи 2 раздела 1 главы. + - Перечень особо ценного имущества. + - Критически важным оборудованием считается оборудование, без которого станция не способна функционировать. + + { -guidebook-corporatelaw-article } + - ID-карты с доступом капитана или главы персонала, либо аналогичным доступом. + - Хищение особо ценного имущества. + - Хищение шаттлов. guidebook-corporatelaw-321-desc = [italic]Повреждение или порча имущества общего пользования, личных вещей, имущества отделов станции, травмирование домашнего животного.[/italic] guidebook-corporatelaw-321-note = @@ -263,17 +267,6 @@ guidebook-corporatelaw-324-note = - Для классификации редкости рекомендуется использовать критерии: возможность производства, заказа или добычи указанного предмета, сложность добычи. - Для классификации важности рекомендуется использовать критерии: наличие аналогов украденному предмету, возможность быстрой замены украденного предмета. -guidebook-corporatelaw-325-desc = [italic]Незаконное присвоение особо ценного или же критически важного оборудования и имущества станции, шаттлов.[/italic] -guidebook-corporatelaw-325-note = - { -guidebook-corporatelaw-note } - - Перечень особо ценного имущества. - - Критически важным оборудованием считается оборудование, без которого станция не способна функционировать. - - { -guidebook-corporatelaw-article } - - ID-карты с доступом капитана или главы персонала, либо аналогичным доступом. - - Хищение особо ценного имущества. - - Хищение шаттлов. - guidebook-corporatelaw-326-desc = [italic]Уничтожение особо ценного или же критически важного оборудования и имущества станции.[/italic] guidebook-corporatelaw-326-note = { -guidebook-corporatelaw-note } @@ -288,6 +281,7 @@ guidebook-corporatelaw-411-note = - Битье стаканов в баре. - Броски скользких предметов под ноги с целью опрокидывания. - Осквернение стен, пола при помощи мелков. + - Распространение ложной информации о наличии угрозы безопасности для объекта. guidebook-corporatelaw-413-desc = [italic]Хищение или приобретение права на имущества общего пользования, личных вещей, имущества отделов станции, домашних животных путем обмана или злоупотребления доверием. Незаконное создание, приобретение, хранение или сбыт поддельных документов с печатями, денежных средств. Незаконное использование или прослушивание закрытых каналов отделов.[/italic] guidebook-corporatelaw-413-note = @@ -304,7 +298,7 @@ guidebook-corporatelaw-413-note = guidebook-corporatelaw-415-desc = [italic]Хищение или приобретение права редкое или важного имущество путем обмана или злоупотребления доверием. Незаконное создание, приобретение, хранение или сбыт поддельных документов с печатями глав отделов, магистрата, центрального командования. Незаконное использование закрытых каналов отдела службы безопасности, командования, центрального командования.[/italic] guidebook-corporatelaw-415-note = { -guidebook-corporatelaw-note } - - При получении особо ценных предметов путем обмана должна применяться статья "Хищение особо ценного имущества" (325). + - При получении особо ценных предметов путем обмана должна применяться статья "Хищение особо ценного имущества" (315). - Редкость или важность имущества определяется с учётом обстоятельств: - Для классификации редкости рекомендуется использовать критерии: возможность производства, заказа или добычи указанного предмета, сложность добычи. - Для классификации важности рекомендуется использовать критерии: наличие аналогов украденному предмету, возможность быстрой замены украденного предмета. @@ -324,15 +318,14 @@ guidebook-corporatelaw-421-note = - Космосом считаются внешние структуры станции, открытый космос. - Допрашивающий определяет, была ли причина уважительной. -guidebook-corporatelaw-422-desc = [italic]Нахождение на территории технических помещений, космоса, не имея законного разрешения.[/italic] +guidebook-corporatelaw-422-desc = [italic]Нахождение на территории отдела, не имея законного разрешения.[/italic] guidebook-corporatelaw-422-note = { -guidebook-corporatelaw-note } - - Техническими помещениями считаются коридоры, требующие доступ "Техобслуживание". - - Космосом считаются внешние структуры станции, открытый космос. - Законным разрешением могут считаться: - - наличие законного доступа, при отсутствии нарушений условий предоставленного доступа; - - разрешение капитана; + - наличие законного доступа в соответствующий отдел, при отсутствии нарушений условий предоставленного доступа; + - разрешение сотрудника отдела; - иное законное разрешение. + - Отделом считаются все помещения, требующие доступа какого-либо отдела. guidebook-corporatelaw-423-desc = [italic]Нахождение на территории стратегической точки, не имея законного разрешения.[/italic] guidebook-corporatelaw-423-note = @@ -342,45 +335,39 @@ guidebook-corporatelaw-423-note = - разрешение сотрудника отдела; - иное законное разрешение. - Стратегическими точками считаются: - - Мостик - - Отсек с двигателем АМ - - Жилые каюты и рабочие кабинеты должностных лиц - - Химическая лаборатория - - Зоны службы безопасности (бриг, КПП, пермабриг) - - EVA - - Телекоммуникационные сервера - - Солнечные панели + - Мостик; + - Жилые каюты и рабочие кабинеты должностных лиц; + - Хранилище ВКД; + - Зоны службы безопасности (бриг, КПП, пермабриг); + - Химическая лаборатория; + - Хранилище плат; + - Солнечные панели; + - Отсек с двигателем АМ; + - Помещение с ТЭГ; + - Атмосферика; + - Токсикология. -guidebook-corporatelaw-424-desc = [italic]Нахождение на территории защищённой стратегической точки, не имея законного разрешения.[/italic] -guidebook-corporatelaw-424-note = +guidebook-corporatelaw-425-desc = [italic]Нахождение на территории защищённой стратегической точки, в пределах границ корпорации NanoTrasen или на закрытых режимных корпоративных объектах, не имея законного разрешения .[/italic] +guidebook-corporatelaw-425-note = { -guidebook-corporatelaw-note } - - Законным разрешением могут считаться: + - Законным разрешением для нахождения в защищённой стратегической точке могут считаться: - наличие законного доступа в соответствующий отдел, при отсутствии нарушений условий предоставленного доступа; - разрешение сотрудника отдела; - иное законное разрешение. - Защищенными стратегическими точками считаются: - - Шаттл представителей ЦК - - Атмосферика - - Токсикология - - Хранилище - - Арсенал - - Ядро ИИ - - Серверная - - Хранилище плат - - Отсек генератора гравитации - - Отсек с Ускорителем частиц - - Каюта капитана - -guidebook-corporatelaw-425-desc = [italic]Несанкционированный вылет с территории действия блюспейс маяка объекта NanoTrasen.[/italic] -guidebook-corporatelaw-425-note = - { -guidebook-corporatelaw-note } - - Законность вылета может быть подтверждена пунктами стандартных рабочих процедур, протоколом эвакуации, приказом центрального командования, постановлением суда. - - Территорией действия блюспейс маяка считается расстояние, которое нужно преодолеть, чтобы шаттл имел возможность совершить блюспейс-прыжок к маяку. - -guidebook-corporatelaw-426-desc = [italic]Нахождение на территории NanoTrasen, не имея соответствующего разрешения.[/italic] -guidebook-corporatelaw-426-note = - { -guidebook-corporatelaw-note } - - Территорией объекта NanoTrasen считаются станции, солнечные системы, иные объекты, принадлежащие корпорации. + - Шаттл представителей ЦК; + - Хранилище; + - Арсенал; + - Ядро ИИ; + - Серверные помещения телекомунникации или НИО; + - Отсек генератора гравитации; + - Отсек с ускорителем частиц; + - Отсек суперматерии; + - Каюта капитана. + - Законным разрешением для нахождения на территории закрытых корпоративных объектов или в пределах границ корпорации могут считаться: + - наличие законного доступа, предусмотренного должностью или рабочим местом сотрудника; + - письменное разрешение от местных административных объектов (ЦК), совета директоров или генерального директора корпорации. + - Режимными корпоративными объектами по умолчанию считаются: экспериментальные, научные, оборонные и стратегические административные (СЦК) объекты. guidebook-corporatelaw-431-desc = [italic]Незаконное владение рабочим снаряжением или оборудованием, не находящимися в свободном доступе без разрешения или необходимости, а также униформой, заведомо вводящей в заблуждение при идентификации.[/italic] guidebook-corporatelaw-431-note = diff --git a/Resources/Locale/ru-RU/job/job-names.ftl b/Resources/Locale/ru-RU/job/job-names.ftl index 661c1b088d..0299ff271b 100644 --- a/Resources/Locale/ru-RU/job/job-names.ftl +++ b/Resources/Locale/ru-RU/job/job-names.ftl @@ -6,9 +6,9 @@ job-name-brigmedic = бригмедик job-name-cadet = кадет СБ job-name-captain = капитан job-name-cargotech = грузчик -job-name-cburn = агент карантинной службы Центком +job-name-cburn = агент РХБЗЗ job-name-ce = старший инженер -job-name-centcommoff = представитель Центрального Командования +job-name-centcommoff = представитель Центком job-name-chef = шеф-повар job-name-chaplain = священник # WL-Changes-start @@ -17,7 +17,7 @@ job-name-chemist = фармацевт job-name-clown = клоун job-name-cluwne = клувень job-name-cmo = главный врач -job-name-deathsquad = агент Центком +job-name-deathsquad = агент эскадрона смерти job-name-detective = детектив job-name-doctor = врач job-name-engineer = инженер diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/structures/Walls/invisible_wall.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/structures/Walls/invisible_wall.ftl index 7b55aafa1b..9702062853 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/structures/Walls/invisible_wall.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/structures/Walls/invisible_wall.ftl @@ -1,3 +1,3 @@ ent-MarkerBlocker = невидимая стена .desc = { ent-MarkerBase.desc } - .suffix = Маркер, Невидимая + .suffix = Маркер, Админ diff --git a/Resources/Maps/Corvax/corvax_astra.yml b/Resources/Maps/Corvax/corvax_astra.yml index c3e912111c..ac632c3d3a 100644 --- a/Resources/Maps/Corvax/corvax_astra.yml +++ b/Resources/Maps/Corvax/corvax_astra.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 268.0.0 + engineVersion: 275.2.0 forkId: "" forkVersion: "" - time: 12/11/2025 08:13:29 - entityCount: 41135 + time: 04/18/2026 14:29:51 + entityCount: 41133 maps: - 1 grids: @@ -60,7 +60,6 @@ tilemap: 52: FloorGrassLight 53: FloorGrayConcrete 54: FloorGrayConcreteMono - 87: FloorGrayConcreteOutside 55: FloorGrayConcreteSmooth 56: FloorGreenCircuit 57: FloorGym @@ -246,7 +245,7 @@ entities: version: 7 4,0: ind: 4,0 - tiles: cAAAAAAAACkAAAAAAAApAAAAAAAAfgAAAAAAAB8AAAAAAAAXAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAHAAAAAAAAApAAAAAAAAKQAAAAAAAH4AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAfgAAAAAAAGQAAAAAAABkAAAAAAAAZAAAAAAAAH4AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAfgAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAFwAAAAAAAB8AAAAAAAAvAAAAAAAAHwAAAAAAABcAAAAAAAA1AAAAAAAANQAAAAAAADUAAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAAfAAAAAAAAFwAAAAAAAB8AAAAAAAB+AAAAAAAANQAAAAAAADUAAAAAAAA1AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAB+AAAAAAAAHwAAAAAAAC8AAAAAAAAfAAAAAAAAfgAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAADYAAAAAAAA2AAAAAAAAfgAAAAAAADcAAAAAAAA3AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA3AAAAAAAANwAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANwAAAAAAADcAAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADcAAAAAAAA3AAAAAAAAfgAAAAAAAH0AAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAAH4AAAAAAAA3AAAAAAAANwAAAAAAAH4AAAAAAAB9AAAAAAAAAAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcAAAAAAABXAAAAAAAAVwAAAAAAAH4AAAAAAABsAAAAAAAAfgAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAAAAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABsAAAAAAAAbAAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAAAAAVwAAAAAAAFcAAAAAAABXAAAAAAAAfgAAAAAAAGwAAAAAAAB+AAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: cAAAAAAAACkAAAAAAAApAAAAAAAAfgAAAAAAAB8AAAAAAAAXAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAHAAAAAAAAApAAAAAAAAKQAAAAAAAH4AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAfgAAAAAAAGQAAAAAAABkAAAAAAAAZAAAAAAAAH4AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAfgAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAFwAAAAAAAB8AAAAAAAAvAAAAAAAAHwAAAAAAABcAAAAAAAA1AAAAAAAANQAAAAAAADUAAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAAfAAAAAAAAFwAAAAAAAB8AAAAAAAB+AAAAAAAANQAAAAAAADUAAAAAAAA1AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAB+AAAAAAAAHwAAAAAAAC8AAAAAAAAfAAAAAAAAfgAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAADYAAAAAAAA2AAAAAAAAfgAAAAAAADcAAAAAAAA3AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA3AAAAAAAANwAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANwAAAAAAADcAAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADcAAAAAAAA3AAAAAAAAfgAAAAAAAH0AAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAAH4AAAAAAAA3AAAAAAAANwAAAAAAAH4AAAAAAAB9AAAAAAAAAAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAAH4AAAAAAABsAAAAAAAAfgAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAAAAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABsAAAAAAAAbAAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAAAAANQAAAAAAADUAAAAAAAA1AAAAAAAAfgAAAAAAAGwAAAAAAAB+AAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 5,-1: ind: 5,-1 @@ -19417,6 +19416,9 @@ entities: id: Astra - type: NavMap - type: ImplicitRoof + - type: TileHistory + chunkHistory: {} + - type: ExplosionAirtightGrid - uid: 4 components: - type: MetaData @@ -20262,6 +20264,9 @@ entities: chunkSize: 4 - type: GasTileOverlay - type: ImplicitRoof + - type: TileHistory + chunkHistory: {} + - type: ExplosionAirtightGrid - proto: AccessConfigurator entities: - uid: 156 @@ -33454,18 +33459,6 @@ entities: - type: Transform pos: -2.5,-53.5 parent: 2 -- proto: BaseChemistryEmptyVial - entities: - - uid: 1953 - components: - - type: Transform - pos: -37.747562,-34.233185 - parent: 2 - - uid: 1954 - components: - - type: Transform - pos: -37.888187,-34.326935 - parent: 2 - proto: BaseComputer entities: - uid: 1955 @@ -103328,6 +103321,18 @@ entities: parent: 1965 - type: Physics canCollide: False +- proto: ChemistryEmptyVial + entities: + - uid: 1953 + components: + - type: Transform + pos: -37.747562,-34.233185 + parent: 2 + - uid: 1954 + components: + - type: Transform + pos: -37.888187,-34.326935 + parent: 2 - proto: ChemistryHotplate entities: - uid: 15563 @@ -106182,8 +106187,8 @@ entities: - type: Transform parent: 159 - type: Clothing - inSlotFlag: HEAD inSlot: head + inSlotFlag: HEAD - type: HandheldLight toggleActionEntity: 161 - type: ContainerContainer @@ -106675,8 +106680,8 @@ entities: - type: Transform parent: 16099 - type: Clothing - inSlotFlag: MASK inSlot: mask + inSlotFlag: MASK - type: Physics canCollide: False - proto: ClothingMaskGasAtmos @@ -106686,8 +106691,8 @@ entities: - type: Transform parent: 159 - type: Clothing - inSlotFlag: MASK inSlot: mask + inSlotFlag: MASK - type: Physics canCollide: False - uid: 16102 @@ -106788,8 +106793,8 @@ entities: - type: TypingIndicatorClothing gotEquippedTime: 5513.9278194 - type: Clothing - inSlotFlag: NECK inSlot: neck + inSlotFlag: NECK - type: Physics canCollide: False - proto: ClothingNeckCloakNanotrasen @@ -108012,8 +108017,8 @@ entities: - type: Transform parent: 16099 - type: Clothing - inSlotFlag: INNERCLOTHING inSlot: jumpsuit + inSlotFlag: INNERCLOTHING - type: Physics canCollide: False - proto: ClothingUniformJumpsuitRepairmanNT @@ -108023,8 +108028,8 @@ entities: - type: Transform parent: 159 - type: Clothing - inSlotFlag: INNERCLOTHING inSlot: jumpsuit + inSlotFlag: INNERCLOTHING - type: Physics canCollide: False - proto: ClothingUniformJumpsuitSafari @@ -110062,8 +110067,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 16561 @@ -110539,8 +110544,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateHydroponics @@ -110605,8 +110610,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateHydroSecure @@ -110788,8 +110793,8 @@ entities: pos: 29.5,21.5 parent: 2 - type: EntityStorage - open: True removedMasks: 28 + open: True - type: Fixtures fixtures: fix1: @@ -111076,8 +111081,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateSecgear @@ -111111,8 +111116,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 16653 @@ -111396,8 +111401,8 @@ entities: immutable: False temperature: 293.147 moles: {} - open: True removedMasks: 20 + open: True - type: Fixtures fixtures: fix1: @@ -111943,7 +111948,6 @@ entities: desc: Это плиткорез. ПЛИТКОРЕЗ. Придайте разнообразия полу вашей станции с помощью интересных узоров! Не засовывайте внутрь пальцы. name: плиткорез - type: Transform - rot: 3.141592653589793 rad pos: 49.5,-46.5 parent: 2 - uid: 16761 @@ -111952,7 +111956,6 @@ entities: desc: Это плиткорез. ПЛИТКОРЕЗ. Придайте разнообразия полу вашей станции с помощью интересных узоров! Не засовывайте внутрь пальцы. name: плиткорез - type: Transform - rot: 3.141592653589793 rad pos: 104.5,-20.5 parent: 2 - proto: d10Dice @@ -128127,7 +128130,7 @@ entities: tags: - DrinkBottle - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.06604335 + sprayFizzinessThresholdRoll: 0.0485634 - type: SolutionContainerManager solutions: null containers: @@ -130223,13 +130226,6 @@ entities: parent: 2 - type: FaxMachine name: Офис Капитана -- proto: FigureSpawner - entities: - - uid: 19862 - components: - - type: Transform - pos: -29.5,13.5 - parent: 2 - proto: filingCabinet entities: - uid: 2452 @@ -174755,7 +174751,14 @@ entities: - type: Transform pos: 17.5,16.5 parent: 2 -- proto: GlowstickBase +- proto: GlowstickBlue + entities: + - uid: 25479 + components: + - type: Transform + pos: -47.2452,-100.72763 + parent: 2 +- proto: GlowstickGreen entities: - uid: 25475 components: @@ -174778,13 +174781,6 @@ entities: - type: Transform pos: -74.14946,-7.76694 parent: 2 -- proto: GlowstickBlue - entities: - - uid: 25479 - components: - - type: Transform - pos: -47.2452,-100.72763 - parent: 2 - proto: GlowstickPurple entities: - uid: 25480 @@ -183900,8 +183896,6 @@ entities: rot: 1.5707963267948966 rad pos: -6.3343987,19.591106 parent: 2 -- proto: HandheldHealthAnalyzerUnpowered - entities: - uid: 27105 components: - type: Transform @@ -184004,37 +183998,6 @@ entities: - type: Transform pos: 31.546932,4.9642363 parent: 2 -- proto: HeadArachnid - entities: - - uid: 27123 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -5.0829196,-42.402122 - parent: 2 -- proto: HeadMoth - entities: - - uid: 27124 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: HeadReptilian - entities: - - uid: 27125 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -64.8308,-32.5277 - parent: 2 -- proto: HeadVox - entities: - - uid: 27126 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 42.36527,-109.73952 - parent: 2 - proto: HeatExchanger entities: - uid: 27127 @@ -186598,66 +186561,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: LeftArmSkeleton - entities: - - uid: 27489 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: LeftFootMoth - entities: - - uid: 27490 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 107.40406,-71.27681 - parent: 2 -- proto: LeftFootSkeleton - entities: - - uid: 27491 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: LeftHandArachnid - entities: - - uid: 27492 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.9422946,-42.714622 - parent: 2 -- proto: LeftHandSkeleton - entities: - - uid: 27493 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: LeftLegArachnid - entities: - - uid: 27494 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.2079196,-41.839622 - parent: 2 -- proto: LeftLegReptilian - entities: - - uid: 27495 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -63.578598,-33.43395 - parent: 2 -- proto: LeftLegSkeleton - entities: - - uid: 27496 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 - proto: LegionnaireBonfire entities: - uid: 27497 @@ -190903,6 +190806,13 @@ entities: - type: Transform pos: -14.487654,-43.432667 parent: 2 +- proto: MechFigurineSpawner50 + entities: + - uid: 19862 + components: + - type: Transform + pos: -29.5,13.5 + parent: 2 - proto: MedalCase entities: - uid: 27942 @@ -192985,6 +192895,22 @@ entities: - type: Transform pos: -15.5,-36.5 parent: 2 +- proto: OrganArachnidHandLeft + entities: + - uid: 27492 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.9422946,-42.714622 + parent: 2 +- proto: OrganArachnidHead + entities: + - uid: 27123 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.0829196,-42.402122 + parent: 2 - proto: OrganArachnidHeart entities: - uid: 16610 @@ -193009,6 +192935,14 @@ entities: - type: Transform pos: -14.375384,-30.439037 parent: 2 +- proto: OrganArachnidLegLeft + entities: + - uid: 27494 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.2079196,-41.839622 + parent: 2 - proto: OrganArachnidTongue entities: - uid: 16611 @@ -193018,6 +192952,14 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: OrganArachnidTorso + entities: + - uid: 33991 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.0985446,-42.652122 + parent: 2 - proto: OrganDionaEyes entities: - uid: 28156 @@ -193105,6 +193047,121 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: OrganMothFootLeft + entities: + - uid: 27490 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 107.40406,-71.27681 + parent: 2 +- proto: OrganMothHead + entities: + - uid: 27124 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganReptilianHead + entities: + - uid: 27125 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -64.8308,-32.5277 + parent: 2 +- proto: OrganReptilianLegLeft + entities: + - uid: 27495 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -63.578598,-33.43395 + parent: 2 +- proto: OrganReptilianLegRight + entities: + - uid: 31197 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -64.73485,-33.68395 + parent: 2 +- proto: OrganSkeletonPersonArmLeft + entities: + - uid: 27489 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonArmRight + entities: + - uid: 31194 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonFootLeft + entities: + - uid: 27491 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonFootRight + entities: + - uid: 31195 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonHandLeft + entities: + - uid: 27493 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonHandRight + entities: + - uid: 31196 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonLegLeft + entities: + - uid: 27496 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonLegRight + entities: + - uid: 31198 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonTorso + entities: + - uid: 33992 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 + - uid: 33993 + components: + - type: Transform + pos: 10.016842,-39.05978 + parent: 2 +- proto: OrganVoxHead + entities: + - uid: 27126 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 42.36527,-109.73952 + parent: 2 - proto: OxygenCanister entities: - uid: 28165 @@ -199228,6 +199285,43 @@ entities: - type: Transform pos: -36.339783,-86.55919 parent: 2 +- proto: PlushieSpawner50 + entities: + - uid: 34049 + components: + - type: Transform + pos: -40.5,6.5 + parent: 2 + - uid: 34050 + components: + - type: Transform + pos: -58.5,-9.5 + parent: 2 + - uid: 34051 + components: + - type: Transform + pos: 56.5,-29.5 + parent: 2 + - uid: 34052 + components: + - type: Transform + pos: -66.5,-12.5 + parent: 2 + - uid: 34053 + components: + - type: Transform + pos: -48.5,-114.5 + parent: 2 + - uid: 34054 + components: + - type: Transform + pos: -84.5,2.5 + parent: 2 + - uid: 34055 + components: + - type: Transform + pos: -51.5,-49.5 + parent: 2 - proto: PlushieXeno entities: - uid: 28494 @@ -215290,42 +215384,6 @@ entities: - type: Transform pos: -71.338974,-2.4210052 parent: 2 -- proto: RightArmSkeleton - entities: - - uid: 31194 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: RightFootSkeleton - entities: - - uid: 31195 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: RightHandSkeleton - entities: - - uid: 31196 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: RightLegReptilian - entities: - - uid: 31197 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -64.73485,-33.68395 - parent: 2 -- proto: RightLegSkeleton - entities: - - uid: 31198 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 - proto: RiotShield entities: - uid: 31199 @@ -224405,7 +224463,7 @@ entities: - type: Transform pos: 16.554049,-31.147774 parent: 2 -- proto: SpacemenFigureSpawner +- proto: SpacemenFigurineSpawner90 entities: - uid: 32323 components: @@ -224819,13 +224877,6 @@ entities: - type: Transform pos: 36.5,17.5 parent: 2 -- proto: SpawnPointBoxer - entities: - - uid: 32382 - components: - - type: Transform - pos: -22.5,-86.5 - parent: 2 - proto: SpawnPointBrigmedic entities: - uid: 32383 @@ -225430,13 +225481,6 @@ entities: - type: Transform pos: 52.5,-22.5 parent: 2 -- proto: SpawnPointZookeeper - entities: - - uid: 32487 - components: - - type: Transform - pos: -66.5,-48.5 - parent: 2 - proto: SpeedLoaderMagnum entities: - uid: 32488 @@ -235228,26 +235272,6 @@ entities: - type: Transform pos: 109.50452,-22.239302 parent: 2 -- proto: TorsoArachnid - entities: - - uid: 33991 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.0985446,-42.652122 - parent: 2 -- proto: TorsoSkeleton - entities: - - uid: 33992 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 - - uid: 33993 - components: - - type: Transform - pos: 10.016842,-39.05978 - parent: 2 - proto: TowelColorNT entities: - uid: 1859 @@ -235638,43 +235662,6 @@ entities: - type: Transform pos: -13.961909,-76.64306 parent: 2 -- proto: ToySpawner - entities: - - uid: 34049 - components: - - type: Transform - pos: -40.5,6.5 - parent: 2 - - uid: 34050 - components: - - type: Transform - pos: -58.5,-9.5 - parent: 2 - - uid: 34051 - components: - - type: Transform - pos: 56.5,-29.5 - parent: 2 - - uid: 34052 - components: - - type: Transform - pos: -66.5,-12.5 - parent: 2 - - uid: 34053 - components: - - type: Transform - pos: -48.5,-114.5 - parent: 2 - - uid: 34054 - components: - - type: Transform - pos: -84.5,2.5 - parent: 2 - - uid: 34055 - components: - - type: Transform - pos: -51.5,-49.5 - parent: 2 - proto: TrackingImplanter entities: - uid: 2527 diff --git a/Resources/Maps/Corvax/corvax_awesome.yml b/Resources/Maps/Corvax/corvax_awesome.yml index db6bcb024f..4129560b78 100644 --- a/Resources/Maps/Corvax/corvax_awesome.yml +++ b/Resources/Maps/Corvax/corvax_awesome.yml @@ -1,15 +1,16 @@ meta: format: 7 category: Map - engineVersion: 266.0.0 + engineVersion: 270.1.0 forkId: "" forkVersion: "" - time: 09/13/2025 05:42:01 - entityCount: 19355 + time: 04/18/2026 11:25:32 + entityCount: 19544 maps: - 1 grids: - 2 +- 14229 - 16832 - 16838 - 16911 @@ -20,6 +21,7 @@ tilemap: 0: Space 1: FloorArcadeBlue 10: FloorAsteroidSandUnvariantized + 8: FloorAstroGrass 14: FloorAstroSnow 15: FloorBar 17: FloorBlue @@ -27,6 +29,7 @@ tilemap: 23: FloorCarpetClown 28: FloorClown 29: FloorConcrete + 6: FloorDark 36: FloorDarkDiagonal 38: FloorDarkHerringbone 39: FloorDarkMini @@ -48,6 +51,8 @@ tilemap: 70: FloorKitchen 76: FloorMime 78: FloorMiningDark + 3: FloorOldConcrete + 2: FloorOldConcreteMono 91: FloorRGlass 93: FloorReinforced 94: FloorReinforcedHardened @@ -83,9 +88,12 @@ tilemap: 133: FloorWhitePavementVertical 134: FloorWhitePlastic 135: FloorWood + 7: FloorWoodChess + 5: FloorWoodChessDark 140: FloorWoodChessLight 141: FloorWoodChessRed 143: FloorWoodLarge + 4: FloorWoodLargeDark 146: FloorWoodLargeLight 147: FloorWoodLargeRed 148: FloorWoodLight @@ -131,11 +139,11 @@ entities: version: 7 -1,-1: ind: -1,-1 - tiles: dwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHQAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAGsAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAABrAAAAAAMAawAAAAACAGsAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAGsAAAAAAgCdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAHgAAAAAAABrAAAAAAMAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAACAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAAAzAAAAAAAAeAAAAAAAADMAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACUAAAAAAEAlAAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAGAAAAAAAACdAAAAAAAAnQAAAAAAAJQAAAAAAgCGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAACUAAAAAAEAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAHgAAAAAAABrAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAlAAAAAADAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAIwAAAAAAACNAAAAAAAAjAAAAAAAAI0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAAAzAAAAAAAAeAAAAAAAADMAAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACNAAAAAAIAjAAAAAABAI0AAAAAAwCMAAAAAAMAgQAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIEAAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAjAAAAAACAI0AAAAAAQCMAAAAAAIAjQAAAAACAIUAAAAAAAASAAAAAAAAPwAAAAAAABIAAAAAAACFAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACBAAAAAAAAPwAAAAAAABIAAAAAAAA/AAAAAAAAgQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAA== + tiles: dwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAHQAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAFsAAAAAAABbAAAAAAAAWwAAAAAAAFsAAAAAAABbAAAAAAAAawAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAGsAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAABrAAAAAAMAawAAAAACAGsAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAGsAAAAAAgCdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAHgAAAAAAABrAAAAAAMAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAACAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAAAzAAAAAAAAeAAAAAAAADMAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACUAAAAAAEAlAAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAGAAAAAAAACdAAAAAAAAnQAAAAAAAJQAAAAAAgCGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAACUAAAAAAEAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAHgAAAAAAABrAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAlAAAAAADAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAIwAAAAAAACNAAAAAAAAjAAAAAAAAI0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAAAzAAAAAAAAeAAAAAAAADMAAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACNAAAAAAIAjAAAAAABAI0AAAAAAwCMAAAAAAMAgQAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIEAAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAjAAAAAACAI0AAAAAAQCMAAAAAAIAjQAAAAACAIUAAAAAAAASAAAAAAAAPwAAAAAAABIAAAAAAACFAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACBAAAAAAAAPwAAAAAAABIAAAAAAAA/AAAAAAAAgQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAA== version: 7 -1,-2: ind: -1,-2 - tiles: XgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAnQAAAAAAAF4AAAAAAABBAAAAAAAAXgAAAAAAAEEAAAAAAABeAAAAAAAAQQAAAAAAAF4AAAAAAACdAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAMAnQAAAAAAAGsAAAAAAABrAAAAAAIAawAAAAAAAJ0AAAAAAABeAAAAAAAAQQAAAAAAAF4AAAAAAABBAAAAAAAAXgAAAAAAAEEAAAAAAABeAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAACAGsAAAAAAQBrAAAAAAMAawAAAAACAGsAAAAAAgCdAAAAAAAAXgAAAAAAAEEAAAAAAABeAAAAAAAAQQAAAAAAAF4AAAAAAABBAAAAAAAAXgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAEEAAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAawAAAAABAJ0AAAAAAABrAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAABBAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAGsAAAAAAQBrAAAAAAMAawAAAAAAAJ0AAAAAAABkAAAAAAEAawAAAAABAGQAAAAAAwCdAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAAAAAQQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAABrAAAAAAMAnQAAAAAAAGsAAAAAAQCdAAAAAAAAZAAAAAACAGQAAAAAAgBkAAAAAAEAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAEEAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAADAA== + tiles: nQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAgAAAAAAAAMAAAAAAAACAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAIAAAAAAAADAAAAAAAAAgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAACAAAAAAAAAwAAAAAAAAIAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAAJAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAACwAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAABwAAAAAAAAcAAAAAAACdAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAMAJAAAAAAAACQAAAAAAAAkAAAAAAAALAAAAAAAACwAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAALAAAAAAAAAcAAAAAAAAHAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAACACwAAAAAAAAkAAAAAAAALAAAAAAAACwAAAAAAACdAAAAAAAALAAAAAAAACcAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAHAAAAAAAABwAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAJ0AAAAAAABrAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAB3AAAAAAAAdAAAAAAAAGsAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAAnQAAAAAAAGsAAAAAAQBrAAAAAAMAawAAAAAAACQAAAAAAAAsAAAAAAAAJAAAAAAAACwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAJ0AAAAAAABrAAAAAAMAnQAAAAAAAGsAAAAAAQAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAHcAAAAAAAB0AAAAAAAAawAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAADAA== version: 7 1,-1: ind: 1,-1 @@ -143,11 +151,11 @@ entities: version: 7 0,-2: ind: 0,-2 - tiles: XgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAACAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAQBrAAAAAAAAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAADAGsAAAAAAgCdAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAAAAGsAAAAAAABrAAAAAAIAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAmQAAAAABAJoAAAAAAgBrAAAAAAEAawAAAAADAJ0AAAAAAABrAAAAAAAAawAAAAADAJ0AAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAAAawAAAAABAJ0AAAAAAABrAAAAAAAAmgAAAAABAJoAAAAAAQCaAAAAAAMAawAAAAADAGsAAAAAAwCdAAAAAAAAawAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAQCdAAAAAAAAawAAAAAAAJ0AAAAAAACaAAAAAAAAmQAAAAADAGsAAAAAAgBrAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAEAnQAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAnQAAAAAAAGsAAAAAAQCdAAAAAAAAmgAAAAAAAJkAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAHoAAAAAAABrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAawAAAAABADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAADADMAAAAAAAAzAAAAAAAAMwAAAAAAAA== + tiles: nQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAABgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAAYAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAAGAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAACAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAQBrAAAAAAAAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAADAGsAAAAAAgCdAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAAAAGsAAAAAAABrAAAAAAIAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAmQAAAAABAJoAAAAAAgBrAAAAAAEAawAAAAADAJ0AAAAAAABrAAAAAAAAawAAAAADAJ0AAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAAAawAAAAABAJ0AAAAAAABrAAAAAAAAmgAAAAABAJoAAAAAAQCaAAAAAAMAawAAAAADAGsAAAAAAwCdAAAAAAAAawAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAQCdAAAAAAAAawAAAAAAAJ0AAAAAAACaAAAAAAAAmQAAAAADAGsAAAAAAgBrAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAEAnQAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAnQAAAAAAAGsAAAAAAQCdAAAAAAAAmgAAAAAAAJkAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAHoAAAAAAABrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAawAAAAABADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAADADMAAAAAAAAzAAAAAAAAMwAAAAAAAA== version: 7 1,-2: ind: 1,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAgB4AAAAAAAAnQAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAnQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAAEEAAAAAAABBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAADAHgAAAAAAACdAAAAAAAAnQAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAACdAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAnQAAAAAAAEIAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAACoAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAnAAAAAAAAKgAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAJ0AAAAAAAB6AAAAAAAAegAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAABrAAAAAAEAawAAAAADAGsAAAAAAQCdAAAAAAAAegAAAAAAAHoAAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAJwAAAAAAACwAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAB6AAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAACsAAAAAAAAsAAAAAAAAKwAAAAAAACwAAAAAAABOAAAAAAAATgAAAAAAAJoAAAAAAgCaAAAAAAIAmQAAAAACAJ0AAAAAAAB6AAAAAAAAoAAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACaAAAAAAAAmgAAAAAAAJoAAAAAAwCdAAAAAAAAegAAAAAAAJ0AAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAeQAAAAAAAJ0AAAAAAACdAAAAAAAAmQAAAAACAJoAAAAAAQCaAAAAAAAAnQAAAAAAAHoAAAAAAACgAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJkAAAAAAQCaAAAAAAEAmgAAAAAAAJ0AAAAAAACdAAAAAAAAoAAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAbAAAAAAAAGwAAAAAAABsAAAAAAAAbAAAAAAAAJ0AAAAAAAB5AAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAEAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAGwAAAAAAABsAAAAAAAAbAAAAAAAAGwAAAAAAACdAAAAAAAAeQAAAAAAAA== + tiles: awAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAdAAAAAAAAHgAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAQgAAAAAAAGsAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAAB4AAAAAAAAnQAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAnQAAAAAAAEIAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAAB0AAAAAAAAeAAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAAEEAAAAAAABBAAAAAAAAawAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAADAHgAAAAAAACdAAAAAAAAnQAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAACdAAAAAAAAQgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAnQAAAAAAAEIAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAACoAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAnAAAAAAAAKgAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAJ0AAAAAAAB6AAAAAAAAegAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAABrAAAAAAEAawAAAAADAGsAAAAAAQCdAAAAAAAAegAAAAAAAHoAAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAJwAAAAAAACwAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAB6AAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAACsAAAAAAAAsAAAAAAAAKwAAAAAAACwAAAAAAABOAAAAAAAATgAAAAAAAJoAAAAAAgCaAAAAAAIAmQAAAAACAJ0AAAAAAAB6AAAAAAAAoAAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACaAAAAAAAAmgAAAAAAAJoAAAAAAwCdAAAAAAAAegAAAAAAAJ0AAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAeQAAAAAAAJ0AAAAAAACdAAAAAAAAmQAAAAACAJoAAAAAAQCaAAAAAAAAnQAAAAAAAHoAAAAAAACgAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJkAAAAAAQCaAAAAAAEAmgAAAAAAAJ0AAAAAAACdAAAAAAAAoAAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAbAAAAAAAAGwAAAAAAABsAAAAAAAAbAAAAAAAAJ0AAAAAAAB5AAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAEAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAGwAAAAAAABsAAAAAAAAbAAAAAAAAGwAAAAAAACdAAAAAAAAeQAAAAAAAA== version: 7 -2,0: ind: -2,0 @@ -155,23 +163,23 @@ entities: version: 7 -2,-1: ind: -2,-1 - tiles: nQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAhgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACGAAAAAAAAWwAAAAAAAGsAAAAAAwCdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAABrAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAA5AAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAB7AAAAAAIAnQAAAAAAAHkAAAAAAAB6AAAAAAAAegAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAB7AAAAAAMAOgAAAAADAJ0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHAAAAAAAAB5AAAAAAAAnQAAAAAAAHQAAAAAAQCdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAZQAAAAAAAGUAAAAAAABlAAAAAAAAZQAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAnQAAAAAAAGUAAAAAAACGAAAAAAAAhgAAAAAAAGUAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABlAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAADAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAB5AAAAAAAAnQAAAAAAAGUAAAAAAABlAAAAAAAAZQAAAAAAAGUAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAA== + tiles: nQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAhgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACGAAAAAAAAWwAAAAAAAGsAAAAAAwCdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAABrAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAA5AAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAB7AAAAAAIAnQAAAAAAAHkAAAAAAAB6AAAAAAAAegAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAB7AAAAAAMAOgAAAAADAJ0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHAAAAAAAAB5AAAAAAAAnQAAAAAAAHQAAAAAAQCdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAZQAAAAAAAGUAAAAAAABlAAAAAAAAZQAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAnQAAAAAAAGUAAAAAAACGAAAAAAAAhgAAAAAAAGUAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABlAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAADAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAB5AAAAAAAAnQAAAAAAAGUAAAAAAABlAAAAAAAAZQAAAAAAAGUAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAA== version: 7 -2,-2: ind: -2,-2 - tiles: nQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAIAawAAAAADAGsAAAAAAQCdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAACAGsAAAAAAwBrAAAAAAAAawAAAAADAGsAAAAAAQBrAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAADAJ0AAAAAAABvAAAAAAMAawAAAAADAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAMAnQAAAAAAAGsAAAAAAQCdAAAAAAAAbwAAAAADAGsAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAADAGsAAAAAAABrAAAAAAAAnQAAAAAAAG8AAAAAAwBrAAAAAAAAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAJ0AAAAAAABvAAAAAAMAawAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAawAAAAAAAGsAAAAAAwCdAAAAAAAAbwAAAAADAGsAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAADAJ0AAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAADAF0AAAAAAABdAAAAAAAAXQAAAAAAAGsAAAAAAQBrAAAAAAIAawAAAAADAGsAAAAAAgBrAAAAAAEAawAAAAAAAGsAAAAAAgBrAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAEAawAAAAADAGsAAAAAAQBdAAAAAAAAXQAAAAAAAF0AAAAAAABrAAAAAAAAnQAAAAAAAGsAAAAAAABkAAAAAAIAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAawAAAAAAAGsAAAAAAgBrAAAAAAIAZAAAAAADAGsAAAAAAQCcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAawAAAAAAAGsAAAAAAQCdAAAAAAAAawAAAAADAGQAAAAAAABrAAAAAAMAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAMAnQAAAAAAAGQAAAAAAABkAAAAAAIAZAAAAAACAJwAAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAZAAAAAACAGsAAAAAAwCcAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAgBrAAAAAAMAnAAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAAAAA== + tiles: nQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAJ0AAAAAAAADAAAAAAAAnQAAAAAAAAMAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAkAAAAAAAAJwAAAAAAAIwAAAAAAACdAAAAAAAAAwAAAAAAAJ0AAAAAAAADAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAAnQAAAAAAACwAAAAAAACdAAAAAAAAJAAAAAAAACcAAAAAAACMAAAAAAAAnQAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAAFsAAAAAAAAsAAAAAAAAnQAAAAAAACQAAAAAAAAnAAAAAAAAjAAAAAAAAJ0AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAACwAAAAAAABbAAAAAAAALAAAAAAAAJ0AAAAAAAAkAAAAAAAAJwAAAAAAAIwAAAAAAACdAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAAWwAAAAAAACwAAAAAAACdAAAAAAAAJAAAAAAAACcAAAAAAACMAAAAAAAAnQAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAAJ0AAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAACwAAAAAAABrAAAAAAIALAAAAAAAACwAAAAAAAAsAAAAAAAAawAAAAAAAGsAAAAAAgBrAAAAAAAAawAAAAABAGsAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAAAsAAAAAAAAnQAAAAAAACwAAAAAAAAkAAAAAAAAJAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAALAAAAAAAAGsAAAAAAgAsAAAAAAAALAAAAAAAACwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAALAAAAAAAACwAAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAJwAAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAAJAAAAAAAACwAAAAAAACcAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAnAAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAAA== version: 7 -2,-3: ind: -2,-3 - tiles: AAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAACAGsAAAAAAgBrAAAAAAEAawAAAAADAGsAAAAAAQB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAQBrAAAAAAAAawAAAAAAAGsAAAAAAgBrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAADAGsAAAAAAQBrAAAAAAMAawAAAAACAGsAAAAAAQBrAAAAAAIAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAADAGsAAAAAAwBrAAAAAAEAawAAAAABAGsAAAAAAABrAAAAAAMAawAAAAABAJ0AAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAgBrAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAwCdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAawAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAACAGsAAAAAAQBrAAAAAAIAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAABAGsAAAAAAQBrAAAAAAIAawAAAAAAAGsAAAAAAQBrAAAAAAMAawAAAAAAAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAACAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAawAAAAADAGsAAAAAAABrAAAAAAEAawAAAAABAGsAAAAAAABrAAAAAAEAnQAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAA== + tiles: AAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAAJ0AAAAAAAB5AAAAAAAAeQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAAHkAAAAAAAB5AAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAsAAAAAAAALAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACwAAAAAAACUAAAAAAAAlAAAAAAAAJQAAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAnQAAAAAAAAQAAAAAAAAFAAAAAAAABAAAAAAAAA== version: 7 -1,-3: ind: -1,-3 - tiles: AAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== version: 7 0,-3: ind: 0,-3 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAA== version: 7 -3,-2: ind: -3,-2 @@ -227,7 +235,7 @@ entities: version: 7 1,-3: ind: 1,-3 - tiles: AAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAegAAAAAAAHoAAAAAAAB6AAAAAAAAnQAAAAAAAHoAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJ0AAAAAAAB6AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAegAAAAAAAHoAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABCAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAQgAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAHoAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAA+AAAAAAIAPgAAAAACAD4AAAAAAwA+AAAAAAEAnQAAAAAAAJ0AAAAAAABCAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAPgAAAAADAD4AAAAAAAA+AAAAAAIAPgAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAnQAAAAAAAD4AAAAAAgA+AAAAAAAAPgAAAAAAAD4AAAAAAgCdAAAAAAAAnQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAwA+AAAAAAMAPgAAAAADAD4AAAAAAQA+AAAAAAAAnQAAAAAAAJ0AAAAAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAACdAAAAAAAAPgAAAAADAD4AAAAAAwA+AAAAAAIAPgAAAAACAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== + tiles: AAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAegAAAAAAAHoAAAAAAAB6AAAAAAAAnQAAAAAAAHoAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJ0AAAAAAAB6AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAegAAAAAAAHoAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABCAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAQgAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAHoAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAA+AAAAAAIAPgAAAAACAD4AAAAAAwA+AAAAAAEAnQAAAAAAAJ0AAAAAAABCAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAPgAAAAADAD4AAAAAAAA+AAAAAAIAPgAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAnQAAAAAAAD4AAAAAAgA+AAAAAAAAPgAAAAAAAD4AAAAAAgCdAAAAAAAAnQAAAAAAAEIAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAwA+AAAAAAMAPgAAAAADAD4AAAAAAQA+AAAAAAAAnQAAAAAAAJ0AAAAAAABCAAAAAAAAawAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAHgAAAAAAACdAAAAAAAAPgAAAAADAD4AAAAAAwA+AAAAAAIAPgAAAAACAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== version: 7 2,-3: ind: 2,-3 @@ -271,7 +279,7 @@ entities: version: 7 -4,1: ind: -4,1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAADAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAgB4AAAAAAAAawAAAAADAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAeAAAAAAAAGsAAAAAAgCdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAgAzAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAzAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAADAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAAzAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAawAAAAABADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAwBrAAAAAAMAMwAAAAAAADMAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABxAAAAAAAAnQAAAAAAAGsAAAAAAQCdAAAAAAAAnQAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAADAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAgB4AAAAAAAAawAAAAADAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAeAAAAAAAAGsAAAAAAgCdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAgAzAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAzAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAADAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAAzAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAawAAAAABADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAwBrAAAAAAMAMwAAAAAAADMAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABxAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== version: 7 -4,0: ind: -4,0 @@ -279,7 +287,7 @@ entities: version: 7 -4,2: ind: -4,2 - tiles: MwAAAAAAADMAAAAAAAB9AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAACdAAAAAAAAnQAAAAAAADMAAAAAAAAzAAAAAAAAfQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAEAnQAAAAAAAJ0AAAAAAABiAAAAAAAAMwAAAAAAAH0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAAAAJ0AAAAAAACdAAAAAAAAYgAAAAAAADMAAAAAAAB9AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAGsAAAAAAgBrAAAAAAIAawAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABiAAAAAAAAYgAAAAAAAH0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAA== + tiles: MwAAAAAAADMAAAAAAAB9AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAABeAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAADMAAAAAAAAzAAAAAAAAfQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXgAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAABiAAAAAAAAMwAAAAAAAH0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAABeAAAAAAAAQQAAAAAAAF4AAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAYgAAAAAAADMAAAAAAAB9AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAXgAAAAAAAEEAAAAAAABeAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnQAAAAAAAF4AAAAAAABBAAAAAAAAXgAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAABiAAAAAAAAYgAAAAAAAH0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAA== version: 7 -2,4: ind: -2,4 @@ -339,11 +347,7 @@ entities: version: 7 -2,-4: ind: -2,-4 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAA== - version: 7 - -1,-4: - ind: -1,-4 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 2,2: ind: 2,2 @@ -375,18 +379,11 @@ entities: id: Basalt7 decals: 7878: 52.71875,-6.109375 - - node: - color: '#FFFFFFFF' - id: Bot - decals: - 3152: -19,-43 - 3154: -21,-43 - 3156: -20,-45 - node: color: '#FFFFFFFF' id: Box decals: - 3155: -20,-45 + 11263: -15,-41 - node: color: '#16212EFF' id: BrickBoxOverlay @@ -539,9 +536,6 @@ entities: color: '#FF9821FF' id: BrickCornerOverlayNE decals: - 9277: -22,-35 - 9288: -24,-41 - 9353: -21,-20 9508: -34,-22 9509: -36,-21 - node: @@ -585,11 +579,6 @@ entities: decals: 9014: -19,-14 11169: -19,2 - - node: - color: '#FF9821FF' - id: BrickCornerOverlayNW - decals: - 9297: -26,-37 - node: color: '#334E6DFF' id: BrickCornerOverlaySE @@ -625,12 +614,6 @@ entities: decals: 8713: 29,-2 8730: 34,4 - - node: - color: '#FF9821FF' - id: BrickCornerOverlaySE - decals: - 9361: -21,-24 - 9381: -17,-16 - node: color: '#639137FF' id: BrickCornerOverlaySW @@ -655,12 +638,6 @@ entities: id: BrickCornerOverlaySW decals: 10934: 30,4 - - node: - color: '#FF9821FF' - id: BrickCornerOverlaySW - decals: - 9340: -27,-34 - 9354: -27,-24 - node: color: '#334E6DFF' id: BrickEndOverlayN @@ -820,7 +797,6 @@ entities: color: '#AE6716FF' id: BrickLineOverlayE decals: - 4096: -17,-17 4256: -1,-18 - node: color: '#BC863FFF' @@ -867,48 +843,7 @@ entities: color: '#FF9821FF' id: BrickLineOverlayE decals: - 9269: -22,-36 - 9270: -22,-37 - 9271: -22,-38 - 9272: -22,-39 - 9282: -25,-36 - 9283: -25,-37 - 9284: -25,-38 - 9285: -25,-39 - 9286: -25,-40 9287: -24,-42 - 9313: -25,-26 - 9314: -25,-25 - 9315: -25,-27 - 9316: -25,-28 - 9317: -25,-29 - 9318: -27,-27 - 9319: -27,-28 - 9320: -27,-29 - 9321: -27,-29 - 9322: -27,-30 - 9323: -27,-31 - 9331: -27,-29 - 9332: -27,-30 - 9333: -27,-31 - 9334: -27,-32 - 9342: -25,-34 - 9343: -25,-33 - 9344: -25,-32 - 9345: -25,-31 - 9346: -25,-30 - 9347: -25,-29 - 9358: -21,-21 - 9359: -21,-22 - 9360: -21,-23 - 9373: -17,-17 - 9374: -17,-18 - 9375: -17,-19 - 9376: -17,-21 - 9377: -17,-22 - 9378: -17,-23 - 9379: -17,-24 - 9380: -17,-15 - node: color: '#334E6DFF' id: BrickLineOverlayN @@ -1045,7 +980,6 @@ entities: 8985: -15,-9 8986: -14,-9 8989: -17,-4 - 9010: -17,-14 9011: -18,-14 9012: -18,-14 9021: -18,2 @@ -1061,13 +995,6 @@ entities: color: '#FF9821FF' id: BrickLineOverlayN decals: - 9278: -23,-35 - 9279: -24,-35 - 9339: -26,-33 - 9349: -26,-26 - 9350: -23,-20 - 9351: -24,-20 - 9352: -22,-20 9364: -20,-22 9365: -20,-24 9488: -33,-23 @@ -1209,16 +1136,8 @@ entities: color: '#FF9821FF' id: BrickLineOverlayS decals: - 9280: -24,-35 - 9281: -23,-35 - 9341: -26,-34 - 9348: -26,-26 - 9356: -26,-24 - 9357: -25,-24 9362: -20,-24 9363: -20,-22 - 9382: -18,-16 - 9383: -19,-16 9494: -33,-23 9495: -32,-23 9496: -30,-23 @@ -1317,7 +1236,6 @@ entities: color: '#AE6716FF' id: BrickLineOverlayW decals: - 4093: -19,-17 4257: -1,-18 - node: color: '#BC863FFF' @@ -1368,45 +1286,8 @@ entities: color: '#FF9821FF' id: BrickLineOverlayW decals: - 9273: -22,-36 - 9274: -22,-37 - 9275: -22,-38 - 9276: -22,-39 - 9289: -26,-41 9290: -26,-42 9291: -26,-42 - 9292: -26,-41 - 9293: -26,-40 - 9294: -26,-39 - 9295: -26,-39 - 9296: -26,-38 - 9298: -25,-36 - 9299: -25,-35 - 9300: -27,-33 - 9301: -27,-32 - 9302: -27,-31 - 9303: -27,-30 - 9304: -27,-29 - 9305: -27,-28 - 9306: -27,-27 - 9307: -27,-26 - 9308: -27,-25 - 9309: -25,-27 - 9310: -25,-28 - 9311: -25,-29 - 9312: -25,-25 - 9335: -25,-29 - 9336: -25,-30 - 9337: -25,-31 - 9338: -25,-32 - 9355: -27,-23 - 9366: -19,-21 - 9367: -19,-22 - 9368: -19,-23 - 9369: -19,-24 - 9370: -19,-19 - 9371: -19,-18 - 9372: -19,-17 - node: color: '#FFFFFFFF' id: BrickTileDarkBox @@ -1525,9 +1406,6 @@ entities: color: '#FFFFFFFF' id: BrickTileSteelBox decals: - 9207: -21,-31 - 9208: -21,-33 - 9268: -21,-35 9839: 10,-8 9840: 10,-7 9841: 8,-7 @@ -1550,10 +1428,6 @@ entities: 8538: -4,28 8694: 34,7 9147: -21,-19 - 9148: -21,-20 - 9205: -21,-26 - 9230: -24,-41 - 9246: -22,-35 9412: -36,-21 9413: -34,-22 9515: 15,7 @@ -1564,7 +1438,6 @@ entities: 9543: -10,3 9544: -10,-3 9545: -10,-9 - 9546: -4,-17 9547: 9,-17 9548: 15,-17 9568: -10,8 @@ -1591,7 +1464,6 @@ entities: 10470: -4,31 10547: -30,39 10550: -30,44 - 10688: -51,34 11196: 3,17 - node: color: '#FFFFFFFF' @@ -1599,8 +1471,6 @@ entities: decals: 8542: -2,28 8543: -2,20 - 9204: -22,-26 - 9237: -26,-37 9517: -8,3 9518: -8,-3 9519: -8,-9 @@ -1608,7 +1478,6 @@ entities: 9542: -4,7 9549: 13,-17 9550: 7,-17 - 9551: -6,-17 9555: 21,-11 9558: 21,3 9559: 21,-3 @@ -1637,6 +1506,7 @@ entities: 10932: 30,7 10935: 29,4 11190: 0,17 + 11424: -9,-17 - node: color: '#FFFFFFFF' id: BrickTileSteelCornerSe @@ -1645,10 +1515,6 @@ entities: 8539: -4,26 8656: 29,-2 8698: 34,4 - 9126: -17,-16 - 9187: -21,-24 - 9191: -21,-28 - 9194: -22,-31 9512: 19,-5 9514: 19,1 9520: -4,-15 @@ -1691,8 +1557,6 @@ entities: decals: 8540: -2,18 8541: -2,26 - 9184: -27,-24 - 9220: -27,-34 9524: 21,-5 9525: 21,-13 9526: 21,1 @@ -1729,18 +1593,22 @@ entities: 10472: -2,33 10931: 30,4 11191: 0,15 + 11430: -8,-15 - node: color: '#FFFFFFFF' id: BrickTileSteelEndE decals: 8535: -4,12 8536: -4,14 + 11426: -11,-19 + 11429: -11,-17 - node: color: '#FFFFFFFF' id: BrickTileSteelEndN decals: 9210: -26,-30 10929: 38,-5 + 11420: -4,-17 - node: color: '#FFFFFFFF' id: BrickTileSteelEndS @@ -1763,9 +1631,6 @@ entities: color: '#FFFFFFFF' id: BrickTileSteelInnerNe decals: - 9174: -27,-26 - 9236: -25,-41 - 9266: -25,-35 9415: -36,-22 9416: -34,-23 9757: 8,-3 @@ -1774,9 +1639,6 @@ entities: id: BrickTileSteelInnerNw decals: 8663: 29,-2 - 9175: -25,-26 - 9199: -22,-31 - 9238: -25,-37 9765: 4,-2 10179: 38,-6 10183: 39,-5 @@ -1786,10 +1648,6 @@ entities: decals: 8583: 30,3 8584: 34,3 - 9128: -17,-14 - 9176: -27,-26 - 9206: -22,-28 - 9267: -25,-35 9689: 2,-3 9706: 8,-3 10363: -10,9 @@ -1800,21 +1658,15 @@ entities: color: '#FFFFFFFF' id: BrickTileSteelInnerSw decals: - 9177: -25,-26 - 9239: -25,-34 - 9245: -22,-35 9707: 8,-3 9756: 12,-3 10181: 38,-6 10416: 21,9 - 10690: -53,34 11199: 2,15 - node: color: '#FFFFFFFF' id: BrickTileSteelLineE decals: - 8084: -19,-17 - 8085: -17,-17 8546: -4,27 8547: -4,19 8548: 30,-2 @@ -1835,51 +1687,8 @@ entities: 8674: 29,3 8695: 34,6 8696: 34,5 - 9127: -17,-15 - 9136: -17,-17 - 9137: -17,-18 - 9138: -17,-19 - 9139: -17,-21 - 9140: -17,-22 - 9141: -17,-23 - 9142: -17,-24 - 9149: -21,-21 - 9150: -21,-22 - 9151: -21,-23 - 9164: -27,-27 - 9165: -27,-28 - 9166: -27,-29 - 9167: -25,-25 - 9168: -25,-26 - 9169: -25,-27 - 9170: -25,-28 - 9171: -25,-29 - 9178: -25,-25 - 9179: -27,-25 - 9190: -21,-27 - 9192: -22,-29 - 9193: -22,-30 9211: -26,-31 9229: -24,-42 - 9231: -25,-40 - 9232: -25,-39 - 9233: -25,-38 - 9234: -25,-37 - 9235: -25,-36 - 9247: -22,-36 - 9248: -22,-37 - 9249: -22,-38 - 9250: -22,-38 - 9251: -22,-39 - 9260: -25,-34 - 9261: -25,-33 - 9262: -25,-32 - 9263: -25,-31 - 9264: -25,-30 - 9265: -25,-29 - 9324: -27,-30 - 9325: -27,-31 - 9326: -27,-32 9560: 19,-4 9561: 19,2 9569: -10,2 @@ -2015,12 +1824,6 @@ entities: 10668: -39,31 10669: -39,30 10671: -53,31 - 10672: -53,32 - 10673: -53,33 - 10681: -51,33 - 10682: -51,32 - 10683: -51,32 - 10684: -51,31 10691: -57,24 10692: -57,23 10693: -57,25 @@ -2106,15 +1909,6 @@ entities: 8692: 32,7 8693: 33,7 9143: -20,-22 - 9144: -22,-20 - 9145: -24,-20 - 9146: -23,-20 - 9173: -26,-26 - 9197: -24,-31 - 9198: -23,-31 - 9258: -23,-35 - 9259: -24,-35 - 9330: -26,-33 9391: -28,-23 9392: -29,-23 9393: -30,-23 @@ -2125,7 +1919,6 @@ entities: 9409: -37,-21 9410: -38,-21 9414: -35,-22 - 9552: -5,-17 9553: 8,-17 9554: 14,-17 9564: 14,7 @@ -2173,10 +1966,6 @@ entities: 10312: 20,-17 10313: 21,-17 10314: 21,-17 - 10336: -11,-17 - 10337: -10,-17 - 10338: -9,-17 - 10339: -9,-17 10340: -8,-17 10341: -7,-17 10388: -8,7 @@ -2250,10 +2039,6 @@ entities: 10642: -68,28 10643: -69,28 10644: -70,28 - 10674: -52,33 - 10685: -54,34 - 10686: -53,34 - 10687: -52,34 10946: 31,4 10947: 32,4 10948: 33,4 @@ -2265,6 +2050,8 @@ entities: 11187: 4,19 11188: 2,17 11189: 1,17 + 11421: -5,-17 + 11422: -6,-17 - node: zIndex: -1 color: '#FFFFFFFF' @@ -2293,16 +2080,6 @@ entities: 8679: 33,4 8684: 29,6 8753: 32,4 - 9124: -18,-16 - 9125: -19,-16 - 9172: -26,-26 - 9182: -26,-24 - 9183: -25,-24 - 9195: -24,-31 - 9196: -23,-31 - 9221: -26,-34 - 9240: -24,-35 - 9244: -23,-35 9398: -34,-23 9399: -35,-23 9400: -36,-23 @@ -2364,7 +2141,6 @@ entities: 10332: -2,-15 10333: -3,-15 10334: -7,-15 - 10335: -8,-15 10364: -7,9 10365: -6,9 10366: -5,9 @@ -2435,7 +2211,6 @@ entities: 10663: -41,30 10664: -40,30 10665: -39,30 - 10689: -54,34 10941: 31,7 10942: 32,7 10943: 33,7 @@ -2451,8 +2226,6 @@ entities: id: BrickTileSteelLineW decals: 3571: 9,14 - 8082: -19,-17 - 8083: -17,-17 8544: -2,19 8545: -2,27 8565: 34,-2 @@ -2470,53 +2243,8 @@ entities: 8667: 29,2 8668: 29,3 8682: 30,5 - 9129: -19,-17 - 9130: -19,-18 - 9131: -19,-19 - 9132: -19,-21 - 9133: -19,-22 - 9134: -19,-23 - 9135: -19,-24 - 9155: -27,-25 - 9156: -27,-26 - 9157: -27,-27 - 9158: -27,-28 - 9159: -27,-29 - 9160: -25,-25 - 9161: -25,-29 - 9162: -25,-28 - 9163: -25,-27 - 9180: -27,-25 - 9181: -25,-25 - 9185: -27,-23 - 9200: -22,-30 - 9201: -22,-29 - 9202: -22,-28 - 9203: -22,-27 9212: -26,-31 - 9213: -27,-29 - 9214: -27,-30 - 9215: -27,-30 - 9216: -27,-31 - 9217: -27,-31 - 9218: -27,-32 - 9219: -27,-33 - 9222: -25,-35 - 9223: -25,-36 - 9224: -26,-38 - 9225: -26,-39 - 9226: -26,-40 - 9227: -26,-41 9228: -26,-42 - 9252: -22,-36 - 9253: -22,-37 - 9254: -22,-38 - 9255: -22,-38 - 9256: -22,-39 - 9257: -22,-39 - 9327: -25,-30 - 9328: -25,-31 - 9329: -25,-32 9556: 21,-12 9557: 21,-4 9572: -8,-10 @@ -2645,12 +2373,7 @@ entities: 10586: -37,11 10587: -37,10 10588: -37,10 - 10675: -51,33 - 10676: -51,32 - 10677: -51,31 10678: -53,31 - 10679: -53,32 - 10680: -53,33 10709: -55,13 10710: -55,12 10711: -55,11 @@ -2722,6 +2445,12 @@ entities: 11182: 3,19 11183: 3,18 11197: 0,16 + 11425: -9,-18 + 11427: -9,-19 + 11428: -9,-20 + 11431: -8,-14 + 11432: -8,-13 + 11433: -8,-12 - node: color: '#FFFFFFFF' id: BrickTileWhiteBox @@ -2816,7 +2545,6 @@ entities: 8250: -18,17 8262: -20,13 8386: -9,17 - 8862: -19,-16 8944: -13,-11 9046: -24,-14 10810: -62,36 @@ -2967,7 +2695,6 @@ entities: 8433: -17,11 8434: -14,11 8859: -18,-14 - 8860: -17,-14 8903: -18,2 8904: -17,2 8905: -16,2 @@ -3132,16 +2859,35 @@ entities: 10841: -21,9 10852: -20,11 10924: -27,11 + - node: + color: '#FFFFFFFF' + id: Busha3 + decals: + 11434: -6,-19 + 11435: -7,-18 + - node: + color: '#A88661FF' + id: CheckerNESW + decals: + 11312: -21,-26 + 11313: -21,-27 + 11314: -21,-28 + 11315: -21,-29 + 11316: -21,-30 + - node: + color: '#55391AFF' + id: CheckerNWSE + decals: + 11408: -6,-22 + 11409: -5,-22 + 11410: -5,-23 + 11411: -6,-23 + 11412: -6,-24 + 11413: -5,-24 - node: color: '#FFFFFFFF' id: Delivery decals: - 3146: -21,-44 - 3147: -21,-45 - 3148: -21,-46 - 3149: -19,-44 - 3150: -19,-45 - 3151: -19,-46 8175: -36,-29 8177: -36,-32 8624: 31,2 @@ -3200,12 +2946,6 @@ entities: id: Dirt decals: 672: -8,17 - 1015: -18,-29 - 1016: -14,-28 - 1017: -10,-27 - 1018: -9,-29 - 1019: -6,-29 - 1020: 0,-28 1021: 3,-29 1022: 3,-30 1023: 1,-32 @@ -3476,6 +3216,9 @@ entities: 11126: 34,-11 11127: 34,-12 11128: 34,-13 + 11353: 16,-31 + 11354: 16,-29 + 11355: 16,-33 - node: color: '#FFFFFFFF' id: MiniTileDarkLineN @@ -3501,6 +3244,13 @@ entities: 11060: 48,-14 11061: 49,-14 11062: 50,-14 + 11336: 19,-30 + 11337: 18,-30 + 11338: 17,-30 + 11339: 17,-32 + 11340: 18,-32 + 11341: 19,-32 + 11352: 15,-33 - node: color: '#FFFFFFFF' id: MiniTileDarkLineS @@ -3526,6 +3276,13 @@ entities: 11081: 32,-14 11082: 33,-14 11083: 34,-14 + 11345: 15,-29 + 11346: 17,-30 + 11347: 18,-30 + 11348: 19,-30 + 11349: 19,-32 + 11350: 18,-32 + 11351: 17,-32 - node: color: '#FFFFFFFF' id: MiniTileDarkLineW @@ -3533,6 +3290,9 @@ entities: 11123: 36,-13 11124: 36,-12 11125: 36,-11 + 11342: 16,-32 + 11343: 16,-31 + 11344: 16,-30 - node: color: '#43999EFF' id: MiniTileEndOverlayN @@ -3577,9 +3337,6 @@ entities: color: '#FF9821FF' id: MiniTileInnerOverlayNE decals: - 9389: -25,-35 - 9390: -25,-41 - 9484: -27,-26 9510: -36,-22 9511: -34,-23 - node: @@ -3630,14 +3387,7 @@ entities: id: MiniTileInnerOverlayNW decals: 9031: -13,-11 - 9032: -19,-16 9062: -24,-14 - - node: - color: '#FF9821FF' - id: MiniTileInnerOverlayNW - decals: - 9388: -25,-37 - 9485: -25,-26 - node: color: '#FFA647FF' id: MiniTileInnerOverlayNW @@ -3685,13 +3435,6 @@ entities: decals: 9029: -19,-4 11170: -19,2 - - node: - color: '#FF9821FF' - id: MiniTileInnerOverlaySE - decals: - 9384: -17,-14 - 9385: -25,-35 - 9487: -27,-26 - node: color: '#FFA647FF' id: MiniTileInnerOverlaySE @@ -3732,13 +3475,6 @@ entities: decals: 9030: -13,-9 9655: -13,-11 - - node: - color: '#FF9821FF' - id: MiniTileInnerOverlaySW - decals: - 9386: -25,-34 - 9387: -22,-35 - 9486: -25,-26 - node: color: '#FFA647FF' id: MiniTileInnerOverlaySW @@ -4159,6 +3895,19 @@ entities: 10229: 9,16 10230: 9,15 10231: 9,14 + - node: + color: '#3EB38896' + id: MiniTileWhiteBox + decals: + 11327: -21,-25 + 11328: -20,-26 + 11329: -20,-28 + - node: + color: '#EFB34196' + id: MiniTileWhiteBox + decals: + 11394: -11,-22 + 11397: -11,-24 - node: color: '#FFFFFFFF' id: MiniTileWhiteBox @@ -4169,11 +3918,59 @@ entities: 9110: -25,16 9113: -30,18 9121: -30,15 + - node: + color: '#EFB34196' + id: MiniTileWhiteCornerNe + decals: + 11215: -24,-33 + 11246: -21,-35 + 11277: -21,-20 + 11376: -13,-18 + 11388: -13,-22 + 11401: -7,-22 + - node: + color: '#EFB34196' + id: MiniTileWhiteCornerNw + decals: + 11212: -27,-33 + 11377: -19,-18 + - node: + color: '#EFB34196' + id: MiniTileWhiteCornerSe + decals: + 11233: -20,-42 + 11378: -13,-20 + 11389: -13,-24 + 11399: -7,-24 - node: color: '#FFFFFFFF' id: MiniTileWhiteCornerSe decals: 9096: -24,17 + - node: + color: '#EFB34196' + id: MiniTileWhiteCornerSw + decals: + 11225: -27,-41 + 11232: -21,-42 + 11282: -27,-24 + 11365: -19,-24 + - node: + color: '#3EB38896' + id: MiniTileWhiteEndE + decals: + 11326: -21,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteEndE + decals: + 11252: -18,-38 + 11253: -18,-36 + - node: + color: '#3EB38896' + id: MiniTileWhiteEndN + decals: + 11321: -22,-26 - node: color: '#FFFFFFFF' id: MiniTileWhiteEndN @@ -4188,6 +3985,84 @@ entities: 9116: -34,15 10868: -20,8 10872: -22,9 + - node: + color: '#3EB38896' + id: MiniTileWhiteEndW + decals: + 11332: -24,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteEndW + decals: + 11395: -9,-22 + 11396: -9,-24 + - node: + color: '#3EB38896' + id: MiniTileWhiteInnerNe + decals: + 11330: -22,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteInnerNe + decals: + 11217: -24,-35 + 11243: -21,-38 + 11245: -21,-36 + 11384: -17,-22 + - node: + color: '#3EB38896' + id: MiniTileWhiteInnerNw + decals: + 11331: -22,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteInnerNw + decals: + 11406: -7,-24 + - node: + color: '#EFB34196' + id: MiniTileWhiteInnerSe + decals: + 11237: -20,-38 + 11244: -21,-36 + 11383: -17,-20 + - node: + color: '#EFB34196' + id: MiniTileWhiteInnerSw + decals: + 11231: -21,-41 + 11405: -7,-22 + - node: + color: '#3EB38896' + id: MiniTileWhiteLineE + decals: + 11322: -22,-27 + 11323: -22,-28 + 11324: -22,-29 + 11325: -22,-30 + - node: + color: '#EFB34196' + id: MiniTileWhiteLineE + decals: + 11216: -24,-34 + 11234: -20,-41 + 11235: -20,-40 + 11236: -20,-39 + 11251: -21,-37 + 11264: -25,-28 + 11265: -25,-29 + 11266: -25,-27 + 11267: -25,-26 + 11268: -25,-25 + 11278: -21,-21 + 11279: -21,-22 + 11280: -21,-23 + 11281: -21,-24 + 11357: -17,-17 + 11379: -13,-19 + 11386: -17,-21 + 11387: -13,-23 + 11400: -7,-23 - node: color: '#FFFFFFFF' id: MiniTileWhiteLineE @@ -4195,6 +4070,70 @@ entities: 9097: -24,18 9119: -34,17 9120: -34,16 + - node: + color: '#3EB38896' + id: MiniTileWhiteLineN + decals: + 11334: -23,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteLineN + decals: + 11213: -26,-33 + 11214: -25,-33 + 11241: -19,-38 + 11242: -20,-38 + 11247: -22,-35 + 11248: -23,-35 + 11249: -20,-36 + 11250: -19,-36 + 11274: -24,-20 + 11275: -23,-20 + 11276: -22,-20 + 11358: -12,-18 + 11359: -12,-20 + 11371: -18,-18 + 11372: -17,-18 + 11373: -16,-18 + 11374: -15,-18 + 11375: -14,-18 + 11385: -16,-22 + 11391: -14,-22 + 11392: -12,-23 + 11402: -8,-22 + 11403: -8,-24 + - node: + color: '#3EB38896' + id: MiniTileWhiteLineS + decals: + 11333: -23,-31 + 11335: -22,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteLineS + decals: + 11226: -26,-41 + 11227: -25,-41 + 11228: -24,-41 + 11229: -23,-41 + 11230: -22,-41 + 11238: -19,-38 + 11239: -19,-36 + 11240: -20,-36 + 11284: -26,-24 + 11285: -25,-24 + 11360: -12,-20 + 11361: -12,-18 + 11362: -16,-24 + 11363: -17,-24 + 11364: -18,-24 + 11380: -14,-20 + 11381: -15,-20 + 11382: -16,-20 + 11390: -14,-24 + 11393: -12,-23 + 11398: -8,-24 + 11404: -8,-22 - node: color: '#FFFFFFFF' id: MiniTileWhiteLineS @@ -4203,17 +4142,71 @@ entities: 9099: -27,17 9100: -26,17 9101: -25,17 + - node: + color: '#3EB38896' + id: MiniTileWhiteLineW + decals: + 11317: -22,-30 + 11318: -22,-29 + 11319: -22,-28 + 11320: -22,-27 + - node: + color: '#EFB34196' + id: MiniTileWhiteLineW + decals: + 11218: -27,-34 + 11219: -27,-35 + 11220: -27,-36 + 11221: -27,-37 + 11222: -27,-38 + 11223: -27,-39 + 11224: -27,-40 + 11269: -27,-29 + 11270: -27,-28 + 11271: -27,-27 + 11272: -27,-26 + 11273: -27,-25 + 11283: -27,-23 + 11356: -18,-17 + 11366: -19,-23 + 11367: -19,-22 + 11368: -19,-21 + 11369: -19,-20 + 11370: -19,-19 + 11407: -7,-23 - node: color: '#FFFFFFFF' id: MiniTileWhiteLineW decals: 9117: -34,16 9118: -34,17 + - node: + color: '#4B362BFF' + id: QuarterTileOverlayGreyscale + decals: + 11303: -18,-33 + - node: + color: '#EFB34196' + id: QuarterTileOverlayGreyscale + decals: + 11288: -26,-24 + 11289: -25,-24 + - node: + color: '#4B362BFF' + id: QuarterTileOverlayGreyscale180 + decals: + 11302: -18,-33 - node: color: '#4B709CFF' id: QuarterTileOverlayGreyscale180 decals: 10293: 2,-17 + - node: + color: '#EFB34196' + id: QuarterTileOverlayGreyscale180 + decals: + 11286: -27,-23 + 11287: -26,-23 - node: color: '#FFFFFFFF' id: WarnCornerNE @@ -4267,17 +4260,17 @@ entities: color: '#FFFFFFFF' id: WarnCornerSmallSE decals: - 110: -11,-29 - 111: -14,-29 - 112: -17,-29 8855: -25,-21 + 11296: -10,-37 + 11297: -7,-37 + 11298: -4,-37 - node: color: '#FFFFFFFF' id: WarnCornerSmallSW decals: - 113: -13,-29 - 114: -10,-29 - 115: -16,-29 + 11299: -3,-37 + 11300: -6,-37 + 11301: -9,-37 - node: color: '#FFFFFFFF' id: WarnEndS @@ -4289,9 +4282,6 @@ entities: decals: 52: -26,-43 64: -25,-43 - 107: -17,-30 - 108: -14,-30 - 109: -11,-30 173: -26,-46 174: -26,-45 175: -26,-44 @@ -4301,6 +4291,12 @@ entities: 8193: -35,-32 8842: -22,-24 8843: -22,-23 + 11258: -17,-40 + 11259: -17,-41 + 11260: -17,-42 + 11293: -7,-38 + 11294: -10,-38 + 11295: -4,-38 - node: color: '#DE3A3AFF' id: WarnLineGreyscaleN @@ -4321,6 +4317,7 @@ entities: 8191: -36,-30 8850: -27,-22 8851: -26,-22 + 11262: -19,-41 - node: color: '#FFFFFFFF' id: WarnLineS @@ -4329,9 +4326,6 @@ entities: 57: -24,-43 58: -24,-44 61: -25,-43 - 104: -13,-30 - 105: -10,-30 - 106: -16,-30 168: -25,-44 169: -25,-45 171: -24,-45 @@ -4340,6 +4334,9 @@ entities: 8189: -37,-32 8840: -24,-24 8841: -24,-23 + 11290: -9,-38 + 11291: -6,-38 + 11292: -3,-38 - node: color: '#FFFFFFFF' id: WarnLineW @@ -4350,6 +4347,7 @@ entities: 8195: -36,-31 8848: -26,-20 8849: -27,-20 + 11261: -19,-41 - node: color: '#612620FF' id: WoodTrimThinBoxWhite @@ -4374,6 +4372,80 @@ entities: 5: -3,-5 6: -4,-6 15: -4,-4 + - node: + color: '#55391AFF' + id: WoodTrimThinCornerNeWhite + decals: + 11419: -5,-22 + - node: + color: '#55391AFF' + id: WoodTrimThinCornerNwWhite + decals: + 11418: -6,-22 + - node: + color: '#55391AFF' + id: WoodTrimThinCornerSeWhite + decals: + 11414: -5,-24 + - node: + color: '#55391AFF' + id: WoodTrimThinCornerSwWhite + decals: + 11415: -6,-24 + - node: + color: '#A88661FF' + id: WoodTrimThinEndEWhite + decals: + 11255: -18,-37 + - node: + color: '#A88661FF' + id: WoodTrimThinEndWWhite + decals: + 11254: -20,-37 + - node: + color: '#4B362BFF' + id: WoodTrimThinInnerNwWhite + decals: + 11311: -16,-35 + - node: + color: '#4B362BFF' + id: WoodTrimThinInnerSwWhite + decals: + 11310: -16,-31 + - node: + color: '#55391AFF' + id: WoodTrimThinLineEWhite + decals: + 11416: -5,-23 + - node: + color: '#A88661FF' + id: WoodTrimThinLineNWhite + decals: + 11256: -19,-37 + - node: + color: '#4B362BFF' + id: WoodTrimThinLineSWhite + decals: + 11304: -19,-31 + 11305: -17,-31 + 11306: -18,-31 + - node: + color: '#A88661FF' + id: WoodTrimThinLineSWhite + decals: + 11257: -19,-37 + - node: + color: '#4B362BFF' + id: WoodTrimThinLineWWhite + decals: + 11307: -16,-34 + 11308: -16,-33 + 11309: -16,-32 + - node: + color: '#55391AFF' + id: WoodTrimThinLineWWhite + decals: + 11417: -6,-23 - node: color: '#FFFFFFFF' id: bushsnowa2 @@ -4617,8 +4689,6 @@ entities: 1: 53709 -3,3: 1: 56829 - -4,4: - 1: 65528 -3,4: 1: 56825 -3,-1: @@ -4641,7 +4711,7 @@ entities: -1,4: 1: 30583 -4,-4: - 1: 3855 + 1: 4095 -5,-4: 1: 28654 -4,-3: @@ -4655,13 +4725,13 @@ entities: -5,-1: 1: 26223 -3,-4: - 1: 53231 + 1: 53247 -3,-3: 1: 56799 -3,-2: 1: 56783 -3,-5: - 1: 57480 + 1: 61423 -2,-4: 1: 4607 0: 32768 @@ -4672,66 +4742,53 @@ entities: 1: 4369 0: 4 -2,-5: - 1: 61491 + 1: 65535 -1,-5: 1: 63662 -4,-8: - 3: 3 - 1: 65280 - 4: 8 + 1: 65535 -4,-9: - 3: 12288 - 4: 32768 - 0: 79 + 1: 65528 -5,-8: - 1: 60928 - 3: 6 + 1: 61166 -4,-7: 1: 4095 -5,-7: 1: 4079 -4,-6: - 1: 61422 + 1: 4095 -5,-6: 1: 61423 -4,-5: - 1: 3822 + 1: 4095 -5,-5: - 1: 44782 + 1: 52974 -3,-8: - 4: 1 - 1: 65280 - 5: 12 + 1: 65527 -3,-7: 1: 4095 - -3,-9: - 4: 4096 - 5: 49152 - 0: 47 -3,-6: - 1: 3822 + 1: 3838 + -3,-9: + 1: 63487 -2,-8: - 1: 65382 + 1: 65520 -2,-7: 1: 4095 -2,-6: 1: 4095 -2,-9: - 1: 24576 - 0: 159 + 1: 28927 -1,-8: - 3: 7 - 1: 60992 + 1: 32631 -1,-7: - 1: 3823 + 1: 1919 -1,-9: - 3: 28672 - 0: 159 + 1: 65535 -1,-6: 1: 43246 0,-8: - 3: 3 - 1: 65416 + 1: 65534 0,-7: 1: 4095 0,-6: @@ -4784,9 +4841,8 @@ entities: 8,-1: 1: 65520 0,-9: - 3: 12288 - 1: 32768 - 0: 207 + 1: 57361 + 0: 204 1,-8: 1: 65523 1,-7: @@ -4800,34 +4856,42 @@ entities: 1: 29431 2,-9: 1: 4096 - 0: 26127 + 0: 60943 2,-8: - 0: 61030 + 0: 61166 2,-7: 0: 3822 3,-8: - 0: 13056 + 0: 13107 + 1: 34952 3,-7: 0: 4595 1: 49152 3,-6: 1: 64796 + 3,-9: + 0: 13071 + 1: 32768 + 4,-8: + 1: 65535 4,-7: 0: 240 1: 28672 4,-6: 1: 30487 + 4,-9: + 1: 61440 + 0: 15 + 5,-8: + 1: 61423 5,-7: - 0: 17 + 0: 16 1: 47308 5,-6: 1: 48043 - 5,-8: - 0: 4369 - 1: 52428 5,-9: - 0: 4369 - 1: 52424 + 1: 60616 + 0: 17 6,-7: 1: 64797 6,-6: @@ -4861,7 +4925,7 @@ entities: -8,2: 1: 30576 -9,2: - 1: 56768 + 1: 56704 0: 1 -8,3: 1: 29431 @@ -4958,8 +5022,7 @@ entities: -6,-7: 1: 36590 -5,-9: - 3: 24576 - 0: 142 + 1: 60935 -8,-11: 0: 60394 -9,-11: @@ -4982,42 +5045,65 @@ entities: -7,-10: 1: 61166 -6,-12: - 0: 15 - 1: 47872 - -6,-11: - 1: 63675 - -6,-10: - 1: 65523 - -6,-13: - 0: 24320 - -5,-12: 0: 34959 1: 13056 + -6,-11: + 1: 63539 + 0: 8 + -6,-10: + 1: 65535 + -6,-13: + 0: 22272 + -5,-12: + 0: 44835 -5,-11: - 1: 13107 - 0: 34952 - -5,-13: - 0: 24320 + 0: 15 + 1: 64768 -5,-10: - 0: 26344 + 1: 30493 + -5,-13: + 0: 8192 -4,-12: - 0: 12850 + 0: 44800 -4,-11: - 0: 12850 + 0: 15 + 1: 30464 -4,-10: - 0: 44594 - -4,-13: - 0: 8960 + 1: 34823 + 3: 13056 + -3,-12: + 0: 58112 + -3,-11: + 0: 245 + 4: 12288 + 5: 32768 -3,-10: - 0: 44800 + 4: 3 + 1: 65280 + 5: 8 + -2,-12: + 0: 61440 + -2,-11: + 0: 245 + 5: 4096 + 6: 49152 -2,-10: - 0: 44800 + 5: 1 + 1: 65280 + 6: 12 + -1,-12: + 0: 61440 + -1,-11: + 0: 245 + 6: 24576 -1,-10: - 0: 44800 - 0,-10: - 0: 44936 + 1: 65280 + 6: 6 0,-12: - 0: 34952 + 0: 63624 + 0,-10: + 1: 4352 + 0: 52360 0,-13: 0: 34816 0,-11: @@ -5036,16 +5122,12 @@ entities: 0: 3822 3,-11: 0: 58612 - 3,-9: - 0: 15 3,-12: 0: 61166 3,-10: 0: 3822 4,-11: 0: 58612 - 4,-9: - 0: 15 -12,-7: 0: 49152 -12,-6: @@ -5234,6 +5316,8 @@ entities: 8,7: 0: 112 1: 30464 + -4,4: + 1: 65520 -4,5: 1: 60943 -5,5: @@ -5241,7 +5325,7 @@ entities: -4,6: 1: 61166 -5,6: - 1: 4095 + 1: 4087 -4,7: 1: 61684 -5,7: @@ -5646,7 +5730,7 @@ entities: 0: 40863 -4,16: 0: 15 - 3: 30464 + 6: 30464 -3,13: 0: 30076 -3,15: @@ -5660,7 +5744,7 @@ entities: -12,7: 1: 4095 -13,7: - 1: 45055 + 1: 36863 -12,8: 1: 56797 -11,7: @@ -5707,12 +5791,13 @@ entities: -9,1: 0: 1911 -13,8: - 1: 48059 + 1: 34952 + 0: 1 -12,9: 1: 40413 -13,9: 1: 34952 - 0: 272 + 0: 256 -12,10: 1: 61695 -13,10: @@ -5761,7 +5846,7 @@ entities: 0: 4881 -8,16: 1: 51215 - 3: 13056 + 6: 13056 -7,14: 1: 61440 0: 234 @@ -5771,7 +5856,7 @@ entities: 0: 43748 -7,16: 1: 29467 - 3: 34816 + 6: 34816 -6,13: 0: 8949 -6,14: @@ -5781,10 +5866,10 @@ entities: 0: 14 -6,16: 1: 7 - 3: 65280 + 6: 65280 -5,16: 0: 15 - 3: 65280 + 6: 65280 -16,7: 1: 16383 -17,7: @@ -5818,13 +5903,13 @@ entities: 1: 13107 0: 2184 -14,7: - 1: 36859 + 1: 4091 -14,3: 1: 13299 0: 32768 -14,8: - 1: 52428 - 0: 16 + 0: 4 + 1: 60936 -13,5: 1: 1 -15,2: @@ -5865,27 +5950,28 @@ entities: 0: 61440 -15,11: 0: 245 - -14,9: - 0: 545 -14,10: 1: 239 0: 28672 -14,11: 0: 23925 + -14,9: + 1: 14 + 0: 512 -14,12: 0: 15 -13,12: 0: 3759 -9,16: 1: 8 - 3: 65280 + 6: 65280 0: 3 -8,17: - 3: 3 + 6: 3 0: 12032 1: 8 -9,17: - 3: 15 + 6: 15 0: 20224 -8,18: 0: 47 @@ -5894,21 +5980,21 @@ entities: -7,17: 1: 3 0: 12032 - 3: 8 + 6: 8 -7,18: 0: 47 -6,17: - 3: 15 + 6: 15 0: 7936 -6,18: 0: 31 -5,17: - 3: 15 + 6: 15 0: 40704 -5,18: 0: 159 -4,17: - 3: 7 + 6: 7 0: 20224 -4,18: 0: 79 @@ -5916,19 +6002,19 @@ entities: 0: 20288 -11,16: 0: 4383 - 3: 52224 + 6: 52224 -10,15: 0: 12064 -10,16: 0: 15 - 3: 65280 + 6: 65280 -11,17: 0: 12049 - 3: 12 + 6: 12 -11,18: 0: 47 -10,17: - 3: 15 + 6: 15 0: 40704 -10,18: 0: 159 @@ -6021,94 +6107,32 @@ entities: uniqueMixes: - volume: 2500 immutable: True - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + moles: {} - volume: 2500 temperature: 293.15 moles: - - 21.824879 - - 82.10312 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 21.824879 + Nitrogen: 82.10312 - volume: 2500 temperature: 235 moles: - - 21.824879 - - 82.10312 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 27.225372 + Nitrogen: 102.419266 - volume: 2500 temperature: 293.15 moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Plasma: 6666.982 - volume: 2500 temperature: 293.15 moles: - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 6666.982 - volume: 2500 temperature: 293.15 moles: - - 0 - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Nitrogen: 6666.982 + - volume: 2500 + temperature: 293.15 + moles: {} chunkSize: 4 - type: GasTileOverlay - type: RadiationGridResistance @@ -6116,6 +6140,48 @@ entities: - type: BecomesStation id: Awesome - type: ImplicitRoof + - type: ExplosionAirtightGrid + - uid: 14229 + components: + - type: MetaData + name: grid + - type: Transform + pos: -2.786892,1.3025341 + parent: 1 + - type: MapGrid + chunks: + -2,-4: + ind: -2,-4 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 + - type: Broadphase + - type: Physics + bodyStatus: InAir + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + dampingModifier: 0.25 + - type: ImplicitRoof + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - type: GridAtmosphere + version: 2 + data: + chunkSize: 4 + - type: GasTileOverlay + - type: IFF + flags: HideLabel + - type: NavMap - uid: 16832 components: - type: MetaData @@ -6167,6 +6233,7 @@ entities: - type: IFF flags: Hide - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 16838 components: - type: MetaData @@ -6222,6 +6289,7 @@ entities: - type: IFF flags: Hide - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 16911 components: - type: MetaData @@ -7097,6 +7165,7 @@ entities: - type: GasTileOverlay - type: RadiationGridResistance - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 19351 components: - type: MetaData @@ -7166,19 +7235,6 @@ entities: - 7700 - type: Fixtures fixtures: {} - - uid: 5 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-30.5 - parent: 2 - - type: DeviceList - devices: - - 9832 - - 9850 - - 9849 - - type: Fixtures - fixtures: {} - uid: 6 components: - type: Transform @@ -7234,8 +7290,8 @@ entities: - 7514 - 7513 - 7512 - - 7767 - - 7768 + - 3550 + - 3437 - 7593 - 7747 - 7749 @@ -7243,37 +7299,16 @@ entities: - 7750 - 7503 - 7679 - - type: Fixtures - fixtures: {} - - uid: 10 - components: - - type: Transform - pos: -8.5,-20.5 - parent: 2 - - type: DeviceList - devices: - - 9699 - - 425 - - 9833 - - 7606 + - 3441 + - 3292 - type: Fixtures fixtures: {} - uid: 11 components: - type: Transform - pos: -21.5,-33.5 + rot: 1.5707963267948966 rad + pos: -27.5,-32.5 parent: 2 - - type: DeviceList - devices: - - 9844 - - 437 - - 9710 - - 9843 - - 436 - - 9709 - - 7596 - - 7595 - - 7594 - type: Fixtures fixtures: {} - uid: 12 @@ -7309,46 +7344,6 @@ entities: - 9688 - type: Fixtures fixtures: {} - - uid: 14 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-19.5 - parent: 2 - - type: DeviceList - devices: - - 7765 - - 7766 - - 9701 - - 426 - - 9835 - - 7604 - - 7600 - - 7601 - - 7754 - - 7753 - - type: Fixtures - fixtures: {} - - uid: 15 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-14.5 - parent: 2 - - type: DeviceList - devices: - - 7686 - - 7685 - - 7605 - - 7766 - - 7765 - - 7768 - - 7767 - - 9845 - - 438 - - 9711 - - type: Fixtures - fixtures: {} - uid: 16 components: - type: Transform @@ -7363,19 +7358,6 @@ entities: - 7506 - type: Fixtures fixtures: {} - - uid: 17 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-20.5 - parent: 2 - - type: DeviceList - devices: - - 9712 - - 440 - - 7604 - - type: Fixtures - fixtures: {} - uid: 18 components: - type: Transform @@ -8491,7 +8473,6 @@ entities: - 7546 - 7545 - 7728 - - 7727 - 7726 - type: Fixtures fixtures: {} @@ -8661,39 +8642,6 @@ entities: - 7733 - type: Fixtures fixtures: {} - - uid: 98 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 25.5,-25.5 - parent: 2 - - type: DeviceList - devices: - - 7519 - - 7520 - - 9733 - - 456 - - 9865 - - 7521 - - 7493 - - type: Fixtures - fixtures: {} - - uid: 99 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 24.5,-32.5 - parent: 2 - - type: DeviceList - devices: - - 9734 - - 419 - - 7521 - - 7493 - - 7676 - - 9866 - - type: Fixtures - fixtures: {} - uid: 100 components: - type: Transform @@ -8786,6 +8734,62 @@ entities: - 7611 - type: Fixtures fixtures: {} + - uid: 170 + components: + - type: Transform + pos: -6.5,-20.5 + parent: 2 + - type: DeviceList + devices: + - 3454 + - 4810 + - 169 + - type: Fixtures + fixtures: {} + - uid: 993 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.5,-19.5 + parent: 2 + - type: DeviceList + devices: + - 3438 + - 3443 + - 3454 + - 3550 + - 3437 + - 3441 + - 3292 + - 3293 + - 3291 + - type: Fixtures + fixtures: {} + - uid: 1509 + components: + - type: Transform + pos: 17.5,-27.5 + parent: 2 + - type: DeviceList + devices: + - 13567 + - 13564 + - 1507 + - 9922 + - type: Fixtures + fixtures: {} + - uid: 19372 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -51.5,34.5 + parent: 2 + - type: DeviceList + devices: + - 19370 + - 19371 + - type: Fixtures + fixtures: {} - proto: AirCanister entities: - uid: 105 @@ -8838,11 +8842,6 @@ entities: - type: Transform pos: -70.5,33.5 parent: 2 - - uid: 115 - components: - - type: Transform - pos: -14.5,-31.5 - parent: 2 - uid: 116 components: - type: Transform @@ -8889,18 +8888,8 @@ entities: immutable: False temperature: 0 moles: - - 393.0592 - - 1478.6512 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 393.0592 + Nitrogen: 1478.6512 - uid: 16918 components: - type: Transform @@ -9012,13 +9001,20 @@ entities: rot: -1.5707963267948966 rad pos: 34.5,-32.5 parent: 2 -- proto: AirlockAtmosphericsLocked +- proto: AirlockAtmosphericsGlassLocked entities: - - uid: 135 + - uid: 12159 components: - type: Transform - pos: -3.5,-27.5 + pos: -0.5,-29.5 parent: 2 + - uid: 12160 + components: + - type: Transform + pos: -0.5,-27.5 + parent: 2 +- proto: AirlockAtmosphericsLocked + entities: - uid: 136 components: - type: Transform @@ -9148,11 +9144,10 @@ entities: parent: 2 - proto: AirlockChiefEngineerLocked entities: - - uid: 158 + - uid: 11276 components: - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-21.5 + pos: -11.5,-22.5 parent: 2 - proto: AirlockChiefMedicalOfficerLocked entities: @@ -9216,18 +9211,6 @@ entities: rot: 1.5707963267948966 rad pos: 24.5,-33.5 parent: 2 -- proto: AirlockEngineeringGlassLocked - entities: - - uid: 169 - components: - - type: Transform - pos: -18.5,-16.5 - parent: 2 - - uid: 170 - components: - - type: Transform - pos: -16.5,-16.5 - parent: 2 - proto: AirlockEngineeringLocked entities: - uid: 171 @@ -9260,11 +9243,6 @@ entities: - type: Transform pos: -35.5,-23.5 parent: 2 - - uid: 177 - components: - - type: Transform - pos: -15.5,-15.5 - parent: 2 - uid: 178 components: - type: Transform @@ -9290,11 +9268,6 @@ entities: - type: Transform pos: -22.5,-32.5 parent: 2 - - uid: 183 - components: - - type: Transform - pos: -11.5,-15.5 - parent: 2 - uid: 184 components: - type: Transform @@ -9310,11 +9283,6 @@ entities: - type: Transform pos: 31.5,-17.5 parent: 2 - - uid: 187 - components: - - type: Transform - pos: -21.5,-40.5 - parent: 2 - uid: 188 components: - type: Transform @@ -9342,18 +9310,32 @@ entities: rot: 1.5707963267948966 rad pos: 25.5,29.5 parent: 2 -- proto: AirlockEVALocked - entities: - - uid: 193 + - uid: 10322 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -52.5,31.5 + pos: -11.5,-17.5 parent: 2 - - uid: 194 + - uid: 11175 components: - type: Transform - pos: -50.5,31.5 + pos: -11.5,-19.5 + parent: 2 + - uid: 12158 + components: + - type: Transform + pos: -18.5,-40.5 + parent: 2 +- proto: AirlockEVAGlassLocked + entities: + - uid: 19365 + components: + - type: Transform + pos: 20.5,-31.5 + parent: 2 + - uid: 19366 + components: + - type: Transform + pos: 20.5,-29.5 parent: 2 - proto: AirlockExternal entities: @@ -9638,10 +9620,10 @@ entities: parent: 2 - proto: AirlockExternalGlassAtmosphericsLocked entities: - - uid: 234 + - uid: 12547 components: - type: Transform - pos: -1.5,-30.5 + pos: -8.5,-32.5 parent: 2 - proto: AirlockExternalGlassEngineeringLocked entities: @@ -10651,16 +10633,6 @@ entities: - type: Transform pos: -19.5,-9.5 parent: 2 - - uid: 396 - components: - - type: Transform - pos: -15.5,-13.5 - parent: 2 - - uid: 397 - components: - - type: Transform - pos: -11.5,-13.5 - parent: 2 - uid: 398 components: - type: Transform @@ -10784,14 +10756,6 @@ entities: - type: DeviceNetwork deviceLists: - 69 - - uid: 419 - components: - - type: Transform - pos: 23.5,-32.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 99 - uid: 420 components: - type: Transform @@ -10829,22 +10793,6 @@ entities: - type: DeviceNetwork deviceLists: - 18 - - uid: 425 - components: - - type: Transform - pos: -7.5,-22.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 10 - - uid: 426 - components: - - type: Transform - pos: -17.5,-19.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - uid: 427 components: - type: Transform @@ -10913,25 +10861,6 @@ entities: - type: Transform pos: -25.5,-37.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - uid: 437 - components: - - type: Transform - pos: -21.5,-36.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - uid: 438 - components: - - type: Transform - pos: -17.5,-14.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - uid: 439 components: - type: Transform @@ -10940,14 +10869,6 @@ entities: - type: DeviceNetwork deviceLists: - 77 - - uid: 440 - components: - - type: Transform - pos: -13.5,-21.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 17 - uid: 441 components: - type: Transform @@ -11065,14 +10986,6 @@ entities: - type: DeviceNetwork deviceLists: - 97 - - uid: 456 - components: - - type: Transform - pos: 24.5,-26.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - uid: 457 components: - type: Transform @@ -11672,6 +11585,11 @@ entities: - type: Transform pos: -20.5,7.5 parent: 2 + - uid: 648 + components: + - type: Transform + pos: -6.5,-22.5 + parent: 2 - proto: AltarNanotrasen entities: - uid: 536 @@ -11770,13 +11688,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 548 - components: - - type: Transform - pos: -6.5,-20.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 549 components: - type: Transform @@ -12028,14 +11939,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 581 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-21.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 582 components: - type: Transform @@ -12089,14 +11992,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 589 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -19.5,-20.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 590 components: - type: Transform @@ -12156,13 +12051,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 598 - components: - - type: Transform - pos: -19.5,27.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 599 components: - type: Transform @@ -12201,14 +12089,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 604 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -49.5,33.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 605 components: - type: Transform @@ -12271,6 +12151,37 @@ entities: parent: 2 - type: Fixtures fixtures: {} + - uid: 1505 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -55.5,34.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 2098 + components: + - type: Transform + pos: -17.5,27.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 4073 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-24.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 9835 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-20.5 + parent: 2 + - type: Fixtures + fixtures: {} - uid: 16942 components: - type: Transform @@ -12317,6 +12228,20 @@ entities: parent: 16911 - type: Fixtures fixtures: {} + - uid: 19487 + components: + - type: Transform + pos: 1.5,-24.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 19494 + components: + - type: Transform + pos: 15.5,-27.5 + parent: 2 + - type: Fixtures + fixtures: {} - proto: APCElectronics entities: - uid: 613 @@ -12428,6 +12353,11 @@ entities: - type: Transform pos: -32.477085,63.38263 parent: 2 + - uid: 15519 + components: + - type: Transform + pos: -11.492144,-26.475725 + parent: 2 - proto: Ashtray entities: - uid: 634 @@ -12450,6 +12380,11 @@ entities: - type: Transform pos: -37.501637,39.603874 parent: 2 + - uid: 14547 + components: + - type: Transform + pos: -11.695269,-26.24135 + parent: 2 - proto: AsimovCircuitBoard entities: - uid: 639 @@ -12473,35 +12408,6 @@ entities: rot: 1.5707963267948966 rad pos: 54.5,-7.5 parent: 2 - - uid: 647 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -6.5,-18.5 - parent: 2 - - uid: 648 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -6.5,-19.5 - parent: 2 - - uid: 649 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -8.5,-18.5 - parent: 2 - - uid: 650 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -8.5,-19.5 - parent: 2 - - uid: 651 - components: - - type: Transform - pos: -1.5,-30.5 - parent: 2 - uid: 652 components: - type: Transform @@ -12637,63 +12543,28 @@ entities: rot: 1.5707963267948966 rad pos: 54.5,-8.5 parent: 2 + - uid: 14425 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -53.5,32.5 + parent: 2 + - uid: 15318 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -51.5,32.5 + parent: 2 +- proto: AtmosDeviceFanTiny + entities: + - uid: 8097 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-32.5 + parent: 2 - proto: AtmosFixBlockerMarker entities: - - uid: 675 - components: - - type: Transform - pos: -15.5,-31.5 - parent: 2 - - uid: 676 - components: - - type: Transform - pos: -14.5,-31.5 - parent: 2 - - uid: 677 - components: - - type: Transform - pos: -14.5,-32.5 - parent: 2 - - uid: 678 - components: - - type: Transform - pos: -15.5,-32.5 - parent: 2 - - uid: 679 - components: - - type: Transform - pos: -18.5,-31.5 - parent: 2 - - uid: 680 - components: - - type: Transform - pos: -17.5,-31.5 - parent: 2 - - uid: 681 - components: - - type: Transform - pos: -17.5,-32.5 - parent: 2 - - uid: 682 - components: - - type: Transform - pos: -18.5,-32.5 - parent: 2 - - uid: 683 - components: - - type: Transform - pos: -3.5,-31.5 - parent: 2 - - uid: 684 - components: - - type: Transform - pos: -2.5,-31.5 - parent: 2 - - uid: 685 - components: - - type: Transform - pos: -2.5,-32.5 - parent: 2 - uid: 686 components: - type: Transform @@ -13054,40 +12925,45 @@ entities: - type: Transform pos: -13.5,67.5 parent: 2 - - uid: 758 + - uid: 12087 components: - type: Transform - pos: -3.5,-32.5 + pos: -4.5,-39.5 parent: 2 - - uid: 759 + - uid: 12088 components: - type: Transform - pos: -1.5,-32.5 + pos: -5.5,-39.5 parent: 2 - - uid: 760 + - uid: 12089 components: - type: Transform - pos: -1.5,-31.5 + pos: -5.5,-40.5 parent: 2 - - uid: 761 + - uid: 12090 components: - type: Transform - pos: 0.5,-31.5 + pos: -4.5,-40.5 parent: 2 - - uid: 762 + - uid: 12095 components: - type: Transform - pos: 0.5,-32.5 + pos: -2.5,-39.5 parent: 2 - - uid: 763 + - uid: 12096 components: - type: Transform - pos: 1.5,-31.5 + pos: -2.5,-40.5 parent: 2 - - uid: 764 + - uid: 12155 components: - type: Transform - pos: 1.5,-32.5 + pos: -1.5,-40.5 + parent: 2 + - uid: 12156 + components: + - type: Transform + pos: -1.5,-39.5 parent: 2 - proto: AtmosFixFreezerMarker entities: @@ -13538,47 +13414,69 @@ entities: parent: 2 - proto: AtmosFixNitrogenMarker entities: - - uid: 854 + - uid: 12035 components: - type: Transform - pos: -9.5,-31.5 + pos: -7.5,-40.5 parent: 2 - - uid: 855 + - uid: 12036 components: - type: Transform - pos: -8.5,-31.5 + pos: -7.5,-39.5 parent: 2 - - uid: 856 + - uid: 12085 components: - type: Transform - pos: -8.5,-32.5 + pos: -8.5,-39.5 parent: 2 - - uid: 857 + - uid: 12086 components: - type: Transform - pos: -9.5,-32.5 + pos: -8.5,-40.5 parent: 2 - proto: AtmosFixOxygenMarker entities: - - uid: 858 + - uid: 11930 components: - type: Transform - pos: -12.5,-31.5 + pos: -10.5,-40.5 parent: 2 - - uid: 859 + - uid: 11932 components: - type: Transform - pos: -11.5,-31.5 + pos: -10.5,-39.5 parent: 2 - - uid: 860 + - uid: 12003 components: - type: Transform - pos: -11.5,-32.5 + pos: -11.5,-39.5 parent: 2 - - uid: 861 + - uid: 12004 components: - type: Transform - pos: -12.5,-32.5 + pos: -11.5,-40.5 + parent: 2 +- proto: AtmosFixPlasmaMarker + entities: + - uid: 10691 + components: + - type: Transform + pos: -14.5,-37.5 + parent: 2 + - uid: 11792 + components: + - type: Transform + pos: -14.5,-36.5 + parent: 2 + - uid: 11813 + components: + - type: Transform + pos: -15.5,-36.5 + parent: 2 + - uid: 11929 + components: + - type: Transform + pos: -15.5,-37.5 parent: 2 - proto: Autolathe entities: @@ -13604,10 +13502,20 @@ entities: parent: 2 - proto: BannerEngineering entities: - - uid: 866 + - uid: 1283 components: - type: Transform - pos: -12.5,-20.5 + pos: -14.5,-21.5 + parent: 2 + - uid: 3442 + components: + - type: Transform + pos: -8.5,-21.5 + parent: 2 + - uid: 3621 + components: + - type: Transform + pos: -6.5,-23.5 parent: 2 - proto: BannerGreen entities: @@ -13633,6 +13541,16 @@ entities: - type: Transform pos: 5.5,-3.5 parent: 2 + - uid: 19359 + components: + - type: Transform + pos: 15.5,-32.5 + parent: 2 + - uid: 19360 + components: + - type: Transform + pos: 15.5,-28.5 + parent: 2 - proto: BannerRevolution entities: - uid: 871 @@ -13888,11 +13806,6 @@ entities: - type: Transform pos: 40.5,-15.5 parent: 2 - - uid: 916 - components: - - type: Transform - pos: -13.5,-17.5 - parent: 2 - uid: 917 components: - type: Transform @@ -13983,6 +13896,11 @@ entities: - type: Transform pos: 42.5,-4.5 parent: 2 + - uid: 3451 + components: + - type: Transform + pos: -4.5,-22.5 + parent: 2 - uid: 16951 components: - type: Transform @@ -14002,10 +13920,10 @@ entities: parent: 2 - proto: BedsheetCE entities: - - uid: 936 + - uid: 3439 components: - type: Transform - pos: -13.5,-17.5 + pos: -4.5,-22.5 parent: 2 - proto: BedsheetClown entities: @@ -14197,30 +14115,15 @@ entities: parent: 2 - proto: BlastDoor entities: - - uid: 966 + - uid: 683 components: - type: Transform - pos: -1.5,-33.5 + pos: -15.5,-35.5 parent: 2 - - uid: 967 + - uid: 685 components: - type: Transform - pos: -9.5,-30.5 - parent: 2 - - uid: 968 - components: - - type: Transform - pos: -12.5,-30.5 - parent: 2 - - uid: 969 - components: - - type: Transform - pos: -17.5,-33.5 - parent: 2 - - uid: 970 - components: - - type: Transform - pos: -18.5,-33.5 + pos: -2.5,-38.5 parent: 2 - uid: 971 components: @@ -14232,11 +14135,6 @@ entities: - type: Transform pos: -27.5,-16.5 parent: 2 - - uid: 973 - components: - - type: Transform - pos: -2.5,-33.5 - parent: 2 - uid: 974 components: - type: Transform @@ -14257,26 +14155,6 @@ entities: - type: Transform pos: -27.5,-17.5 parent: 2 - - uid: 978 - components: - - type: Transform - pos: -15.5,-33.5 - parent: 2 - - uid: 979 - components: - - type: Transform - pos: -14.5,-33.5 - parent: 2 - - uid: 980 - components: - - type: Transform - pos: -15.5,-30.5 - parent: 2 - - uid: 981 - components: - - type: Transform - pos: -18.5,-30.5 - parent: 2 - uid: 982 components: - type: Transform @@ -14307,10 +14185,20 @@ entities: - type: Transform pos: -23.5,-16.5 parent: 2 - - uid: 988 + - uid: 2836 components: - type: Transform - pos: 0.5,-30.5 + pos: -5.5,-38.5 + parent: 2 + - uid: 2851 + components: + - type: Transform + pos: -11.5,-38.5 + parent: 2 + - uid: 2862 + components: + - type: Transform + pos: -8.5,-38.5 parent: 2 - uid: 16953 components: @@ -14366,31 +14254,11 @@ entities: parent: 2 - proto: BlastDoorOpen entities: - - uid: 993 - components: - - type: Transform - pos: -6.5,-19.5 - parent: 2 - - uid: 994 - components: - - type: Transform - pos: -6.5,-18.5 - parent: 2 - uid: 995 components: - type: Transform pos: 6.5,-18.5 parent: 2 - - uid: 996 - components: - - type: Transform - pos: -8.5,-19.5 - parent: 2 - - uid: 997 - components: - - type: Transform - pos: -8.5,-18.5 - parent: 2 - uid: 998 components: - type: Transform @@ -14406,21 +14274,6 @@ entities: - type: Transform pos: -4.5,-5.5 parent: 2 - - uid: 1001 - components: - - type: Transform - pos: -8.5,-32.5 - parent: 2 - - uid: 1002 - components: - - type: Transform - pos: -11.5,-32.5 - parent: 2 - - uid: 1003 - components: - - type: Transform - pos: 1.5,-32.5 - parent: 2 - uid: 1004 components: - type: Transform @@ -14436,6 +14289,47 @@ entities: - type: Transform pos: 59.5,-7.5 parent: 2 + - uid: 19388 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-33.5 + parent: 2 + - uid: 19389 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,-33.5 + parent: 2 + - uid: 19390 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-33.5 + parent: 2 + - uid: 19391 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-31.5 + parent: 2 + - uid: 19392 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-31.5 + parent: 2 + - uid: 19393 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,-31.5 + parent: 2 + - uid: 19394 + components: + - type: Transform + pos: -4.5,-32.5 + parent: 2 - proto: BlockGameArcade entities: - uid: 1007 @@ -14800,6 +14694,11 @@ entities: - type: Transform pos: 42.5,-6.5 parent: 2 + - uid: 10016 + components: + - type: Transform + pos: -12.5,-23.5 + parent: 2 - proto: BookSpaceLaw entities: - uid: 1025 @@ -14850,11 +14749,6 @@ entities: - type: Transform pos: -41.5,39.5 parent: 2 - - uid: 1082 - components: - - type: Transform - pos: -17.5,-23.5 - parent: 2 - proto: BoxBeaker entities: - uid: 1084 @@ -14871,10 +14765,10 @@ entities: parent: 2 - proto: BoxBeanbag entities: - - uid: 1086 + - uid: 19368 components: - type: Transform - pos: -10.510994,-23.445034 + pos: -54.328426,34.476143 parent: 2 - proto: BoxBodyBag entities: @@ -15370,11 +15264,13 @@ entities: rot: 1.5707963267948966 rad pos: 28.532946,2.5569396 parent: 2 - - uid: 1145 +- proto: BoxFolderYellowThreePapers + entities: + - uid: 7478 components: - type: Transform rot: 3.141592653589793 rad - pos: -13.605877,-23.453194 + pos: -9.848166,-23.38774 parent: 2 - proto: BoxForensicPad entities: @@ -15390,6 +15286,24 @@ entities: - type: Transform pos: 30.51166,-9.325979 parent: 2 +- proto: BoxInflatable + entities: + - uid: 12473 + components: + - type: Transform + parent: 12333 + - type: Physics + canCollide: False + - type: InsideEntityStorage + storage: 12333 + - uid: 12474 + components: + - type: Transform + parent: 12333 + - type: Physics + canCollide: False + - type: InsideEntityStorage + storage: 12333 - proto: BoxLatexGloves entities: - uid: 1148 @@ -15866,11 +15780,10 @@ entities: rot: 3.141592653589793 rad pos: -27.5,4.5 parent: 2 - - uid: 1226 + - uid: 2846 components: - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-24.5 + pos: -8.5,-33.5 parent: 2 - proto: ButtonFrameCautionSecurity entities: @@ -15958,11 +15871,6 @@ entities: - type: Transform pos: -74.5,17.5 parent: 2 - - uid: 1242 - components: - - type: Transform - pos: -10.5,-14.5 - parent: 2 - uid: 1243 components: - type: Transform @@ -16108,71 +16016,6 @@ entities: - type: Transform pos: -33.5,-27.5 parent: 2 - - uid: 1272 - components: - - type: Transform - pos: -8.5,-22.5 - parent: 2 - - uid: 1273 - components: - - type: Transform - pos: -7.5,-22.5 - parent: 2 - - uid: 1274 - components: - - type: Transform - pos: -6.5,-22.5 - parent: 2 - - uid: 1275 - components: - - type: Transform - pos: -6.5,-20.5 - parent: 2 - - uid: 1276 - components: - - type: Transform - pos: -9.5,-22.5 - parent: 2 - - uid: 1277 - components: - - type: Transform - pos: -5.5,-22.5 - parent: 2 - - uid: 1278 - components: - - type: Transform - pos: -7.5,-21.5 - parent: 2 - - uid: 1279 - components: - - type: Transform - pos: -7.5,-20.5 - parent: 2 - - uid: 1280 - components: - - type: Transform - pos: -7.5,-19.5 - parent: 2 - - uid: 1281 - components: - - type: Transform - pos: -7.5,-18.5 - parent: 2 - - uid: 1282 - components: - - type: Transform - pos: -9.5,-23.5 - parent: 2 - - uid: 1283 - components: - - type: Transform - pos: -5.5,-23.5 - parent: 2 - - uid: 1284 - components: - - type: Transform - pos: -7.5,-17.5 - parent: 2 - uid: 1285 components: - type: Transform @@ -17273,36 +17116,6 @@ entities: - type: Transform pos: -28.5,31.5 parent: 2 - - uid: 1505 - components: - - type: Transform - pos: -49.5,33.5 - parent: 2 - - uid: 1506 - components: - - type: Transform - pos: -51.5,33.5 - parent: 2 - - uid: 1507 - components: - - type: Transform - pos: -52.5,33.5 - parent: 2 - - uid: 1508 - components: - - type: Transform - pos: -50.5,33.5 - parent: 2 - - uid: 1509 - components: - - type: Transform - pos: -52.5,34.5 - parent: 2 - - uid: 1510 - components: - - type: Transform - pos: -50.5,34.5 - parent: 2 - uid: 1511 components: - type: Transform @@ -20238,11 +20051,6 @@ entities: - type: Transform pos: -25.5,17.5 parent: 2 - - uid: 2098 - components: - - type: Transform - pos: -19.5,27.5 - parent: 2 - uid: 2099 components: - type: Transform @@ -20251,7 +20059,7 @@ entities: - uid: 2100 components: - type: Transform - pos: -19.5,26.5 + pos: -17.5,26.5 parent: 2 - uid: 2101 components: @@ -23218,156 +23026,6 @@ entities: - type: Transform pos: -18.5,-12.5 parent: 2 - - uid: 2694 - components: - - type: Transform - pos: -18.5,-13.5 - parent: 2 - - uid: 2695 - components: - - type: Transform - pos: -17.5,-13.5 - parent: 2 - - uid: 2696 - components: - - type: Transform - pos: -16.5,-13.5 - parent: 2 - - uid: 2697 - components: - - type: Transform - pos: -15.5,-13.5 - parent: 2 - - uid: 2698 - components: - - type: Transform - pos: -13.5,-13.5 - parent: 2 - - uid: 2699 - components: - - type: Transform - pos: -14.5,-13.5 - parent: 2 - - uid: 2700 - components: - - type: Transform - pos: -18.5,-15.5 - parent: 2 - - uid: 2701 - components: - - type: Transform - pos: -16.5,-15.5 - parent: 2 - - uid: 2702 - components: - - type: Transform - pos: -15.5,-15.5 - parent: 2 - - uid: 2703 - components: - - type: Transform - pos: -17.5,-15.5 - parent: 2 - - uid: 2704 - components: - - type: Transform - pos: -14.5,-15.5 - parent: 2 - - uid: 2705 - components: - - type: Transform - pos: -13.5,-15.5 - parent: 2 - - uid: 2706 - components: - - type: Transform - pos: -18.5,-16.5 - parent: 2 - - uid: 2707 - components: - - type: Transform - pos: -18.5,-18.5 - parent: 2 - - uid: 2708 - components: - - type: Transform - pos: -18.5,-19.5 - parent: 2 - - uid: 2709 - components: - - type: Transform - pos: -18.5,-20.5 - parent: 2 - - uid: 2710 - components: - - type: Transform - pos: -18.5,-17.5 - parent: 2 - - uid: 2711 - components: - - type: Transform - pos: -19.5,-20.5 - parent: 2 - - uid: 2712 - components: - - type: Transform - pos: -17.5,-17.5 - parent: 2 - - uid: 2713 - components: - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - uid: 2714 - components: - - type: Transform - pos: -16.5,-18.5 - parent: 2 - - uid: 2715 - components: - - type: Transform - pos: -16.5,-20.5 - parent: 2 - - uid: 2716 - components: - - type: Transform - pos: -16.5,-21.5 - parent: 2 - - uid: 2717 - components: - - type: Transform - pos: -16.5,-22.5 - parent: 2 - - uid: 2718 - components: - - type: Transform - pos: -16.5,-23.5 - parent: 2 - - uid: 2719 - components: - - type: Transform - pos: -16.5,-19.5 - parent: 2 - - uid: 2720 - components: - - type: Transform - pos: -17.5,-23.5 - parent: 2 - - uid: 2721 - components: - - type: Transform - pos: -18.5,-23.5 - parent: 2 - - uid: 2722 - components: - - type: Transform - pos: -18.5,-22.5 - parent: 2 - - uid: 2723 - components: - - type: Transform - pos: -18.5,-21.5 - parent: 2 - uid: 2724 components: - type: Transform @@ -23598,26 +23256,6 @@ entities: - type: Transform pos: -21.5,-34.5 parent: 2 - - uid: 2770 - components: - - type: Transform - pos: -21.5,-35.5 - parent: 2 - - uid: 2771 - components: - - type: Transform - pos: -21.5,-37.5 - parent: 2 - - uid: 2772 - components: - - type: Transform - pos: -21.5,-38.5 - parent: 2 - - uid: 2773 - components: - - type: Transform - pos: -21.5,-36.5 - parent: 2 - uid: 2774 components: - type: Transform @@ -23783,256 +23421,26 @@ entities: - type: Transform pos: -15.5,-24.5 parent: 2 - - uid: 2807 - components: - - type: Transform - pos: -15.5,-26.5 - parent: 2 - uid: 2808 components: - type: Transform pos: -15.5,-25.5 parent: 2 - - uid: 2809 - components: - - type: Transform - pos: -18.5,-28.5 - parent: 2 - - uid: 2810 - components: - - type: Transform - pos: -5.5,-27.5 - parent: 2 - - uid: 2811 - components: - - type: Transform - pos: -5.5,-29.5 - parent: 2 - - uid: 2812 - components: - - type: Transform - pos: -5.5,-30.5 - parent: 2 - - uid: 2813 - components: - - type: Transform - pos: -5.5,-28.5 - parent: 2 - - uid: 2814 - components: - - type: Transform - pos: -18.5,-27.5 - parent: 2 - - uid: 2815 - components: - - type: Transform - pos: -18.5,-29.5 - parent: 2 - - uid: 2816 - components: - - type: Transform - pos: -18.5,-30.5 - parent: 2 - - uid: 2817 - components: - - type: Transform - pos: -18.5,-31.5 - parent: 2 - - uid: 2818 - components: - - type: Transform - pos: -18.5,-32.5 - parent: 2 - - uid: 2819 - components: - - type: Transform - pos: -17.5,-32.5 - parent: 2 - - uid: 2820 - components: - - type: Transform - pos: -15.5,-27.5 - parent: 2 - - uid: 2821 - components: - - type: Transform - pos: -15.5,-29.5 - parent: 2 - - uid: 2822 - components: - - type: Transform - pos: -15.5,-30.5 - parent: 2 - - uid: 2823 - components: - - type: Transform - pos: -15.5,-31.5 - parent: 2 - - uid: 2824 - components: - - type: Transform - pos: -15.5,-28.5 - parent: 2 - - uid: 2825 - components: - - type: Transform - pos: -15.5,-32.5 - parent: 2 - - uid: 2826 - components: - - type: Transform - pos: -14.5,-32.5 - parent: 2 - - uid: 2827 - components: - - type: Transform - pos: -12.5,-27.5 - parent: 2 - - uid: 2828 - components: - - type: Transform - pos: -12.5,-29.5 - parent: 2 - - uid: 2829 - components: - - type: Transform - pos: -12.5,-30.5 - parent: 2 - - uid: 2830 - components: - - type: Transform - pos: -12.5,-31.5 - parent: 2 - - uid: 2831 - components: - - type: Transform - pos: -12.5,-32.5 - parent: 2 - - uid: 2832 - components: - - type: Transform - pos: -12.5,-28.5 - parent: 2 - - uid: 2833 - components: - - type: Transform - pos: -9.5,-27.5 - parent: 2 - - uid: 2834 - components: - - type: Transform - pos: -9.5,-29.5 - parent: 2 - - uid: 2835 - components: - - type: Transform - pos: -9.5,-30.5 - parent: 2 - - uid: 2836 - components: - - type: Transform - pos: -9.5,-31.5 - parent: 2 - - uid: 2837 - components: - - type: Transform - pos: -9.5,-32.5 - parent: 2 - - uid: 2838 - components: - - type: Transform - pos: -9.5,-28.5 - parent: 2 - - uid: 2839 - components: - - type: Transform - pos: -5.5,-31.5 - parent: 2 - - uid: 2840 - components: - - type: Transform - pos: -4.5,-27.5 - parent: 2 - - uid: 2841 - components: - - type: Transform - pos: -2.5,-27.5 - parent: 2 - - uid: 2842 - components: - - type: Transform - pos: -1.5,-27.5 - parent: 2 - - uid: 2843 - components: - - type: Transform - pos: -3.5,-27.5 - parent: 2 - - uid: 2844 - components: - - type: Transform - pos: -1.5,-26.5 - parent: 2 - - uid: 2845 - components: - - type: Transform - pos: 0.5,-26.5 - parent: 2 - - uid: 2846 - components: - - type: Transform - pos: 1.5,-26.5 - parent: 2 - uid: 2847 components: - type: Transform pos: 2.5,-26.5 parent: 2 - - uid: 2848 - components: - - type: Transform - pos: -0.5,-26.5 - parent: 2 - uid: 2849 components: - type: Transform pos: 3.5,-26.5 parent: 2 - - uid: 2850 - components: - - type: Transform - pos: -1.5,-28.5 - parent: 2 - - uid: 2851 - components: - - type: Transform - pos: -1.5,-29.5 - parent: 2 - - uid: 2852 - components: - - type: Transform - pos: -0.5,-29.5 - parent: 2 - - uid: 2853 - components: - - type: Transform - pos: 1.5,-29.5 - parent: 2 - - uid: 2854 - components: - - type: Transform - pos: 2.5,-29.5 - parent: 2 - uid: 2855 components: - type: Transform pos: 3.5,-29.5 parent: 2 - - uid: 2856 - components: - - type: Transform - pos: 0.5,-29.5 - parent: 2 - uid: 2857 components: - type: Transform @@ -24053,21 +23461,6 @@ entities: - type: Transform pos: 54.5,-8.5 parent: 2 - - uid: 2861 - components: - - type: Transform - pos: -1.5,-31.5 - parent: 2 - - uid: 2862 - components: - - type: Transform - pos: -1.5,-32.5 - parent: 2 - - uid: 2863 - components: - - type: Transform - pos: -1.5,-30.5 - parent: 2 - uid: 2864 components: - type: Transform @@ -25173,11 +24566,6 @@ entities: - type: Transform pos: 7.5,-29.5 parent: 2 - - uid: 3085 - components: - - type: Transform - pos: -11.5,-27.5 - parent: 2 - uid: 3086 components: - type: Transform @@ -25188,21 +24576,6 @@ entities: - type: Transform pos: 7.5,-26.5 parent: 2 - - uid: 3088 - components: - - type: Transform - pos: -13.5,-27.5 - parent: 2 - - uid: 3089 - components: - - type: Transform - pos: -8.5,-27.5 - parent: 2 - - uid: 3090 - components: - - type: Transform - pos: -11.5,-21.5 - parent: 2 - uid: 3091 components: - type: Transform @@ -25228,71 +24601,6 @@ entities: - type: Transform pos: -46.5,50.5 parent: 2 - - uid: 3096 - components: - - type: Transform - pos: -7.5,-27.5 - parent: 2 - - uid: 3097 - components: - - type: Transform - pos: -14.5,-27.5 - parent: 2 - - uid: 3098 - components: - - type: Transform - pos: -10.5,-27.5 - parent: 2 - - uid: 3099 - components: - - type: Transform - pos: -17.5,-27.5 - parent: 2 - - uid: 3100 - components: - - type: Transform - pos: -16.5,-27.5 - parent: 2 - - uid: 3101 - components: - - type: Transform - pos: -13.5,-21.5 - parent: 2 - - uid: 3102 - components: - - type: Transform - pos: -12.5,-21.5 - parent: 2 - - uid: 3103 - components: - - type: Transform - pos: -13.5,-20.5 - parent: 2 - - uid: 3104 - components: - - type: Transform - pos: -13.5,-18.5 - parent: 2 - - uid: 3105 - components: - - type: Transform - pos: -13.5,-17.5 - parent: 2 - - uid: 3106 - components: - - type: Transform - pos: -13.5,-19.5 - parent: 2 - - uid: 3107 - components: - - type: Transform - pos: -13.5,-22.5 - parent: 2 - - uid: 3108 - components: - - type: Transform - pos: -6.5,-27.5 - parent: 2 - uid: 3109 components: - type: Transform @@ -26043,6 +25351,176 @@ entities: - type: Transform pos: -26.5,9.5 parent: 2 + - uid: 4403 + components: + - type: Transform + pos: -17.5,27.5 + parent: 2 + - uid: 7032 + components: + - type: Transform + pos: -52.5,34.5 + parent: 2 + - uid: 7033 + components: + - type: Transform + pos: -52.5,33.5 + parent: 2 + - uid: 8209 + components: + - type: Transform + pos: -54.5,34.5 + parent: 2 + - uid: 8210 + components: + - type: Transform + pos: -52.5,32.5 + parent: 2 + - uid: 8211 + components: + - type: Transform + pos: -55.5,34.5 + parent: 2 + - uid: 8212 + components: + - type: Transform + pos: -53.5,35.5 + parent: 2 + - uid: 8253 + components: + - type: Transform + pos: -53.5,34.5 + parent: 2 + - uid: 14297 + components: + - type: Transform + pos: -8.5,-24.5 + parent: 2 + - uid: 14302 + components: + - type: Transform + pos: -8.5,-23.5 + parent: 2 + - uid: 14303 + components: + - type: Transform + pos: -8.5,-22.5 + parent: 2 + - uid: 14304 + components: + - type: Transform + pos: -9.5,-22.5 + parent: 2 + - uid: 14305 + components: + - type: Transform + pos: -10.5,-22.5 + parent: 2 + - uid: 14325 + components: + - type: Transform + pos: -7.5,-22.5 + parent: 2 + - uid: 14326 + components: + - type: Transform + pos: -6.5,-22.5 + parent: 2 + - uid: 14768 + components: + - type: Transform + pos: -15.5,-20.5 + parent: 2 + - uid: 15436 + components: + - type: Transform + pos: -16.5,-25.5 + parent: 2 + - uid: 15623 + components: + - type: Transform + pos: -16.5,-20.5 + parent: 2 + - uid: 15624 + components: + - type: Transform + pos: -17.5,-20.5 + parent: 2 + - uid: 15625 + components: + - type: Transform + pos: -17.5,-21.5 + parent: 2 + - uid: 15626 + components: + - type: Transform + pos: -17.5,-22.5 + parent: 2 + - uid: 15627 + components: + - type: Transform + pos: -16.5,-22.5 + parent: 2 + - uid: 15680 + components: + - type: Transform + pos: -15.5,-22.5 + parent: 2 + - uid: 15941 + components: + - type: Transform + pos: -14.5,-22.5 + parent: 2 + - uid: 15942 + components: + - type: Transform + pos: -13.5,-22.5 + parent: 2 + - uid: 16306 + components: + - type: Transform + pos: -17.5,-19.5 + parent: 2 + - uid: 16357 + components: + - type: Transform + pos: -17.5,-18.5 + parent: 2 + - uid: 16358 + components: + - type: Transform + pos: -16.5,-18.5 + parent: 2 + - uid: 16466 + components: + - type: Transform + pos: -15.5,-18.5 + parent: 2 + - uid: 16516 + components: + - type: Transform + pos: -17.5,-25.5 + parent: 2 + - uid: 16544 + components: + - type: Transform + pos: -14.5,-18.5 + parent: 2 + - uid: 16569 + components: + - type: Transform + pos: -13.5,-18.5 + parent: 2 + - uid: 16570 + components: + - type: Transform + pos: -12.5,-18.5 + parent: 2 + - uid: 16571 + components: + - type: Transform + pos: -12.5,-17.5 + parent: 2 - uid: 16980 components: - type: Transform @@ -26933,6 +26411,526 @@ entities: - type: Transform pos: 11.5,20.5 parent: 16911 + - uid: 19378 + components: + - type: Transform + pos: -11.5,-17.5 + parent: 2 + - uid: 19379 + components: + - type: Transform + pos: -10.5,-17.5 + parent: 2 + - uid: 19380 + components: + - type: Transform + pos: -5.5,-15.5 + parent: 2 + - uid: 19381 + components: + - type: Transform + pos: -5.5,-16.5 + parent: 2 + - uid: 19382 + components: + - type: Transform + pos: -5.5,-17.5 + parent: 2 + - uid: 19395 + components: + - type: Transform + pos: -17.5,-26.5 + parent: 2 + - uid: 19396 + components: + - type: Transform + pos: -17.5,-27.5 + parent: 2 + - uid: 19397 + components: + - type: Transform + pos: -17.5,-28.5 + parent: 2 + - uid: 19398 + components: + - type: Transform + pos: -17.5,-29.5 + parent: 2 + - uid: 19399 + components: + - type: Transform + pos: -17.5,-30.5 + parent: 2 + - uid: 19400 + components: + - type: Transform + pos: -17.5,-31.5 + parent: 2 + - uid: 19401 + components: + - type: Transform + pos: -17.5,-32.5 + parent: 2 + - uid: 19402 + components: + - type: Transform + pos: -16.5,-30.5 + parent: 2 + - uid: 19403 + components: + - type: Transform + pos: -15.5,-30.5 + parent: 2 + - uid: 19404 + components: + - type: Transform + pos: -14.5,-30.5 + parent: 2 + - uid: 19405 + components: + - type: Transform + pos: -14.5,-31.5 + parent: 2 + - uid: 19406 + components: + - type: Transform + pos: -14.5,-32.5 + parent: 2 + - uid: 19407 + components: + - type: Transform + pos: -14.5,-33.5 + parent: 2 + - uid: 19408 + components: + - type: Transform + pos: -13.5,-33.5 + parent: 2 + - uid: 19409 + components: + - type: Transform + pos: -12.5,-33.5 + parent: 2 + - uid: 19410 + components: + - type: Transform + pos: -12.5,-34.5 + parent: 2 + - uid: 19411 + components: + - type: Transform + pos: -12.5,-35.5 + parent: 2 + - uid: 19412 + components: + - type: Transform + pos: -12.5,-36.5 + parent: 2 + - uid: 19413 + components: + - type: Transform + pos: -11.5,-36.5 + parent: 2 + - uid: 19414 + components: + - type: Transform + pos: -10.5,-36.5 + parent: 2 + - uid: 19415 + components: + - type: Transform + pos: -9.5,-36.5 + parent: 2 + - uid: 19416 + components: + - type: Transform + pos: -8.5,-36.5 + parent: 2 + - uid: 19417 + components: + - type: Transform + pos: -7.5,-36.5 + parent: 2 + - uid: 19418 + components: + - type: Transform + pos: -4.5,-36.5 + parent: 2 + - uid: 19419 + components: + - type: Transform + pos: -5.5,-36.5 + parent: 2 + - uid: 19420 + components: + - type: Transform + pos: -3.5,-36.5 + parent: 2 + - uid: 19421 + components: + - type: Transform + pos: -2.5,-36.5 + parent: 2 + - uid: 19422 + components: + - type: Transform + pos: -6.5,-36.5 + parent: 2 + - uid: 19423 + components: + - type: Transform + pos: -1.5,-36.5 + parent: 2 + - uid: 19424 + components: + - type: Transform + pos: -0.5,-36.5 + parent: 2 + - uid: 19425 + components: + - type: Transform + pos: 0.5,-36.5 + parent: 2 + - uid: 19426 + components: + - type: Transform + pos: -14.5,-34.5 + parent: 2 + - uid: 19427 + components: + - type: Transform + pos: -14.5,-35.5 + parent: 2 + - uid: 19428 + components: + - type: Transform + pos: -14.5,-36.5 + parent: 2 + - uid: 19429 + components: + - type: Transform + pos: -10.5,-37.5 + parent: 2 + - uid: 19430 + components: + - type: Transform + pos: -10.5,-38.5 + parent: 2 + - uid: 19431 + components: + - type: Transform + pos: -10.5,-39.5 + parent: 2 + - uid: 19432 + components: + - type: Transform + pos: -7.5,-37.5 + parent: 2 + - uid: 19433 + components: + - type: Transform + pos: -7.5,-38.5 + parent: 2 + - uid: 19434 + components: + - type: Transform + pos: -7.5,-39.5 + parent: 2 + - uid: 19435 + components: + - type: Transform + pos: -4.5,-37.5 + parent: 2 + - uid: 19436 + components: + - type: Transform + pos: -4.5,-38.5 + parent: 2 + - uid: 19437 + components: + - type: Transform + pos: -4.5,-39.5 + parent: 2 + - uid: 19438 + components: + - type: Transform + pos: -1.5,-37.5 + parent: 2 + - uid: 19439 + components: + - type: Transform + pos: -1.5,-38.5 + parent: 2 + - uid: 19440 + components: + - type: Transform + pos: -1.5,-39.5 + parent: 2 + - uid: 19441 + components: + - type: Transform + pos: -11.5,-33.5 + parent: 2 + - uid: 19442 + components: + - type: Transform + pos: -10.5,-33.5 + parent: 2 + - uid: 19443 + components: + - type: Transform + pos: -9.5,-33.5 + parent: 2 + - uid: 19444 + components: + - type: Transform + pos: -9.5,-32.5 + parent: 2 + - uid: 19445 + components: + - type: Transform + pos: -8.5,-32.5 + parent: 2 + - uid: 19446 + components: + - type: Transform + pos: -14.5,-29.5 + parent: 2 + - uid: 19447 + components: + - type: Transform + pos: -14.5,-28.5 + parent: 2 + - uid: 19448 + components: + - type: Transform + pos: -13.5,-28.5 + parent: 2 + - uid: 19449 + components: + - type: Transform + pos: -12.5,-28.5 + parent: 2 + - uid: 19450 + components: + - type: Transform + pos: -12.5,-27.5 + parent: 2 + - uid: 19451 + components: + - type: Transform + pos: -11.5,-28.5 + parent: 2 + - uid: 19452 + components: + - type: Transform + pos: -10.5,-28.5 + parent: 2 + - uid: 19453 + components: + - type: Transform + pos: -9.5,-28.5 + parent: 2 + - uid: 19454 + components: + - type: Transform + pos: -8.5,-28.5 + parent: 2 + - uid: 19455 + components: + - type: Transform + pos: -7.5,-28.5 + parent: 2 + - uid: 19456 + components: + - type: Transform + pos: -6.5,-28.5 + parent: 2 + - uid: 19457 + components: + - type: Transform + pos: -5.5,-28.5 + parent: 2 + - uid: 19458 + components: + - type: Transform + pos: -4.5,-28.5 + parent: 2 + - uid: 19459 + components: + - type: Transform + pos: -3.5,-28.5 + parent: 2 + - uid: 19460 + components: + - type: Transform + pos: -2.5,-28.5 + parent: 2 + - uid: 19488 + components: + - type: Transform + pos: 1.5,-24.5 + parent: 2 + - uid: 19489 + components: + - type: Transform + pos: 1.5,-25.5 + parent: 2 + - uid: 19490 + components: + - type: Transform + pos: 1.5,-26.5 + parent: 2 + - uid: 19491 + components: + - type: Transform + pos: 2.5,-27.5 + parent: 2 + - uid: 19492 + components: + - type: Transform + pos: 2.5,-28.5 + parent: 2 + - uid: 19493 + components: + - type: Transform + pos: 2.5,-29.5 + parent: 2 + - uid: 19507 + components: + - type: Transform + pos: 15.5,-27.5 + parent: 2 + - uid: 19508 + components: + - type: Transform + pos: 15.5,-28.5 + parent: 2 + - uid: 19509 + components: + - type: Transform + pos: 16.5,-28.5 + parent: 2 + - uid: 19510 + components: + - type: Transform + pos: 16.5,-30.5 + parent: 2 + - uid: 19511 + components: + - type: Transform + pos: 16.5,-29.5 + parent: 2 + - uid: 19512 + components: + - type: Transform + pos: 16.5,-31.5 + parent: 2 + - uid: 19513 + components: + - type: Transform + pos: 17.5,-31.5 + parent: 2 + - uid: 19514 + components: + - type: Transform + pos: 18.5,-31.5 + parent: 2 + - uid: 19515 + components: + - type: Transform + pos: 19.5,-31.5 + parent: 2 + - uid: 19516 + components: + - type: Transform + pos: 17.5,-29.5 + parent: 2 + - uid: 19517 + components: + - type: Transform + pos: 18.5,-29.5 + parent: 2 + - uid: 19518 + components: + - type: Transform + pos: 19.5,-29.5 + parent: 2 + - uid: 19530 + components: + - type: Transform + pos: -21.5,-36.5 + parent: 2 + - uid: 19531 + components: + - type: Transform + pos: -21.5,-35.5 + parent: 2 + - uid: 19532 + components: + - type: Transform + pos: -20.5,-36.5 + parent: 2 + - uid: 19533 + components: + - type: Transform + pos: -19.5,-36.5 + parent: 2 + - uid: 19534 + components: + - type: Transform + pos: -18.5,-36.5 + parent: 2 + - uid: 19535 + components: + - type: Transform + pos: -20.5,-37.5 + parent: 2 + - uid: 19536 + components: + - type: Transform + pos: -20.5,-38.5 + parent: 2 + - uid: 19537 + components: + - type: Transform + pos: -20.5,-39.5 + parent: 2 + - uid: 19538 + components: + - type: Transform + pos: -20.5,-40.5 + parent: 2 + - uid: 19539 + components: + - type: Transform + pos: -19.5,-40.5 + parent: 2 + - uid: 19540 + components: + - type: Transform + pos: -18.5,-40.5 + parent: 2 + - uid: 19541 + components: + - type: Transform + pos: -17.5,-40.5 + parent: 2 + - uid: 19542 + components: + - type: Transform + pos: -16.5,-40.5 + parent: 2 + - uid: 19543 + components: + - type: Transform + pos: -15.5,-40.5 + parent: 2 + - uid: 19544 + components: + - type: Transform + pos: -14.5,-40.5 + parent: 2 - proto: CableApcStack entities: - uid: 3259 @@ -27003,6 +27001,76 @@ entities: parent: 2 - proto: CableHV entities: + - uid: 10 + components: + - type: Transform + pos: -16.5,-17.5 + parent: 2 + - uid: 177 + components: + - type: Transform + pos: -15.5,-28.5 + parent: 2 + - uid: 916 + components: + - type: Transform + pos: -9.5,-17.5 + parent: 2 + - uid: 936 + components: + - type: Transform + pos: -15.5,-17.5 + parent: 2 + - uid: 1086 + components: + - type: Transform + pos: -12.5,-28.5 + parent: 2 + - uid: 1145 + components: + - type: Transform + pos: -17.5,-28.5 + parent: 2 + - uid: 1272 + components: + - type: Transform + pos: -8.5,-28.5 + parent: 2 + - uid: 1278 + components: + - type: Transform + pos: -11.5,-28.5 + parent: 2 + - uid: 3090 + components: + - type: Transform + pos: -11.5,-17.5 + parent: 2 + - uid: 3101 + components: + - type: Transform + pos: -8.5,-17.5 + parent: 2 + - uid: 3102 + components: + - type: Transform + pos: -12.5,-17.5 + parent: 2 + - uid: 3105 + components: + - type: Transform + pos: -14.5,-28.5 + parent: 2 + - uid: 3106 + components: + - type: Transform + pos: -17.5,-19.5 + parent: 2 + - uid: 3107 + components: + - type: Transform + pos: -13.5,-28.5 + parent: 2 - uid: 3271 components: - type: Transform @@ -27053,16 +27121,6 @@ entities: - type: Transform pos: -26.5,-22.5 parent: 2 - - uid: 3281 - components: - - type: Transform - pos: -7.5,-18.5 - parent: 2 - - uid: 3282 - components: - - type: Transform - pos: -7.5,-16.5 - parent: 2 - uid: 3283 components: - type: Transform @@ -27103,21 +27161,6 @@ entities: - type: Transform pos: -35.5,-16.5 parent: 2 - - uid: 3291 - components: - - type: Transform - pos: -7.5,-17.5 - parent: 2 - - uid: 3292 - components: - - type: Transform - pos: -7.5,-19.5 - parent: 2 - - uid: 3293 - components: - - type: Transform - pos: -7.5,-20.5 - parent: 2 - uid: 3294 components: - type: Transform @@ -27573,26 +27616,11 @@ entities: - type: Transform pos: -26.5,-30.5 parent: 2 - - uid: 3385 - components: - - type: Transform - pos: -7.5,-23.5 - parent: 2 - - uid: 3386 - components: - - type: Transform - pos: -7.5,-21.5 - parent: 2 - uid: 3387 components: - type: Transform pos: -35.5,-26.5 parent: 2 - - uid: 3388 - components: - - type: Transform - pos: -7.5,-22.5 - parent: 2 - uid: 3389 components: - type: Transform @@ -27678,25 +27706,15 @@ entities: - type: Transform pos: -20.5,-23.5 parent: 2 - - uid: 3406 - components: - - type: Transform - pos: -19.5,-40.5 - parent: 2 - - uid: 3407 - components: - - type: Transform - pos: -7.5,-24.5 - parent: 2 - uid: 3408 components: - type: Transform - pos: -45.5,1.5 + pos: -17.5,-21.5 parent: 2 - uid: 3409 components: - type: Transform - pos: -44.5,1.5 + pos: -9.5,-28.5 parent: 2 - uid: 3410 components: @@ -27711,82 +27729,42 @@ entities: - uid: 3412 components: - type: Transform - pos: -39.5,1.5 + pos: -5.5,-28.5 parent: 2 - uid: 3413 components: - type: Transform - pos: -41.5,1.5 + pos: -17.5,-20.5 parent: 2 - uid: 3414 components: - type: Transform - pos: -8.5,-24.5 + pos: -17.5,-17.5 parent: 2 - uid: 3415 components: - type: Transform - pos: -9.5,-24.5 + pos: -17.5,-22.5 parent: 2 - uid: 3416 components: - type: Transform - pos: -6.5,-24.5 + pos: -17.5,-18.5 parent: 2 - uid: 3417 components: - type: Transform - pos: -5.5,-24.5 - parent: 2 - - uid: 3418 - components: - - type: Transform - pos: -8.5,-21.5 + pos: -7.5,-28.5 parent: 2 - uid: 3419 components: - type: Transform - pos: -5.5,-23.5 + pos: -6.5,-28.5 parent: 2 - uid: 3420 components: - type: Transform - pos: -4.5,-23.5 - parent: 2 - - uid: 3421 - components: - - type: Transform - pos: -9.5,-21.5 - parent: 2 - - uid: 3422 - components: - - type: Transform - pos: -9.5,-20.5 - parent: 2 - - uid: 3423 - components: - - type: Transform - pos: -10.5,-20.5 - parent: 2 - - uid: 3424 - components: - - type: Transform - pos: -6.5,-21.5 - parent: 2 - - uid: 3425 - components: - - type: Transform - pos: -5.5,-21.5 - parent: 2 - - uid: 3426 - components: - - type: Transform - pos: -5.5,-20.5 - parent: 2 - - uid: 3427 - components: - - type: Transform - pos: -4.5,-20.5 + pos: -8.5,-16.5 parent: 2 - uid: 3428 components: @@ -27798,87 +27776,7 @@ entities: - type: Transform pos: -8.5,-15.5 parent: 2 - - uid: 3430 - components: - - type: Transform - pos: -9.5,-15.5 - parent: 2 - - uid: 3431 - components: - - type: Transform - pos: -10.5,-15.5 - parent: 2 - - uid: 3432 - components: - - type: Transform - pos: -11.5,-15.5 - parent: 2 - - uid: 3433 - components: - - type: Transform - pos: -12.5,-15.5 - parent: 2 - - uid: 3434 - components: - - type: Transform - pos: -13.5,-15.5 - parent: 2 - uid: 3435 - components: - - type: Transform - pos: -14.5,-15.5 - parent: 2 - - uid: 3436 - components: - - type: Transform - pos: -15.5,-15.5 - parent: 2 - - uid: 3437 - components: - - type: Transform - pos: -16.5,-15.5 - parent: 2 - - uid: 3438 - components: - - type: Transform - pos: -16.5,-16.5 - parent: 2 - - uid: 3439 - components: - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - uid: 3440 - components: - - type: Transform - pos: -17.5,-17.5 - parent: 2 - - uid: 3441 - components: - - type: Transform - pos: -17.5,-18.5 - parent: 2 - - uid: 3442 - components: - - type: Transform - pos: -17.5,-19.5 - parent: 2 - - uid: 3443 - components: - - type: Transform - pos: -17.5,-20.5 - parent: 2 - - uid: 3444 - components: - - type: Transform - pos: -17.5,-21.5 - parent: 2 - - uid: 3445 - components: - - type: Transform - pos: -17.5,-22.5 - parent: 2 - - uid: 3446 components: - type: Transform pos: -17.5,-23.5 @@ -27898,31 +27796,11 @@ entities: - type: Transform pos: -20.5,-31.5 parent: 2 - - uid: 3450 - components: - - type: Transform - pos: -18.5,-24.5 - parent: 2 - - uid: 3451 - components: - - type: Transform - pos: -16.5,-23.5 - parent: 2 - uid: 3452 components: - type: Transform pos: -16.5,-24.5 parent: 2 - - uid: 3453 - components: - - type: Transform - pos: -37.5,1.5 - parent: 2 - - uid: 3454 - components: - - type: Transform - pos: -40.5,1.5 - parent: 2 - uid: 3455 components: - type: Transform @@ -28338,11 +28216,6 @@ entities: - type: Transform pos: -46.5,0.5 parent: 2 - - uid: 3538 - components: - - type: Transform - pos: -46.5,1.5 - parent: 2 - uid: 3539 components: - type: Transform @@ -28398,11 +28271,6 @@ entities: - type: Transform pos: -43.5,-1.5 parent: 2 - - uid: 3550 - components: - - type: Transform - pos: -42.5,1.5 - parent: 2 - uid: 3551 components: - type: Transform @@ -28433,11 +28301,6 @@ entities: - type: Transform pos: -38.5,0.5 parent: 2 - - uid: 3557 - components: - - type: Transform - pos: -38.5,1.5 - parent: 2 - uid: 3558 components: - type: Transform @@ -28508,11 +28371,6 @@ entities: - type: Transform pos: -34.5,-0.5 parent: 2 - - uid: 3572 - components: - - type: Transform - pos: -34.5,1.5 - parent: 2 - uid: 3573 components: - type: Transform @@ -28633,11 +28491,6 @@ entities: - type: Transform pos: -41.5,-2.5 parent: 2 - - uid: 3597 - components: - - type: Transform - pos: -43.5,1.5 - parent: 2 - uid: 3598 components: - type: Transform @@ -28753,11 +28606,6 @@ entities: - type: Transform pos: 14.5,-44.5 parent: 2 - - uid: 3621 - components: - - type: Transform - pos: 14.5,-42.5 - parent: 2 - uid: 3622 components: - type: Transform @@ -28863,16 +28711,6 @@ entities: - type: Transform pos: 10.5,-43.5 parent: 2 - - uid: 3643 - components: - - type: Transform - pos: 10.5,-42.5 - parent: 2 - - uid: 3644 - components: - - type: Transform - pos: 9.5,-42.5 - parent: 2 - uid: 3645 components: - type: Transform @@ -28913,21 +28751,6 @@ entities: - type: Transform pos: 18.5,-43.5 parent: 2 - - uid: 3653 - components: - - type: Transform - pos: 18.5,-42.5 - parent: 2 - - uid: 3654 - components: - - type: Transform - pos: 17.5,-42.5 - parent: 2 - - uid: 3655 - components: - - type: Transform - pos: 19.5,-42.5 - parent: 2 - uid: 3656 components: - type: Transform @@ -30528,31 +30351,6 @@ entities: - type: Transform pos: -28.5,2.5 parent: 2 - - uid: 3976 - components: - - type: Transform - pos: -19.5,-41.5 - parent: 2 - - uid: 3977 - components: - - type: Transform - pos: -18.5,-40.5 - parent: 2 - - uid: 3978 - components: - - type: Transform - pos: -35.5,1.5 - parent: 2 - - uid: 3979 - components: - - type: Transform - pos: -33.5,1.5 - parent: 2 - - uid: 3980 - components: - - type: Transform - pos: -36.5,1.5 - parent: 2 - uid: 3981 components: - type: Transform @@ -30641,17 +30439,17 @@ entities: - uid: 3998 components: - type: Transform - pos: 8.5,-42.5 + pos: -3.5,-28.5 parent: 2 - uid: 3999 components: - type: Transform - pos: 7.5,-42.5 + pos: -4.5,-28.5 parent: 2 - uid: 4000 components: - type: Transform - pos: 6.5,-42.5 + pos: -18.5,-27.5 parent: 2 - uid: 4001 components: @@ -30703,11 +30501,6 @@ entities: - type: Transform pos: -31.5,-35.5 parent: 2 - - uid: 4011 - components: - - type: Transform - pos: 20.5,-42.5 - parent: 2 - uid: 4012 components: - type: Transform @@ -30716,27 +30509,17 @@ entities: - uid: 4013 components: - type: Transform - pos: 16.5,-42.5 + pos: -17.5,-27.5 parent: 2 - uid: 4014 components: - type: Transform - pos: 15.5,-42.5 + pos: -10.5,-28.5 parent: 2 - uid: 4015 components: - type: Transform - pos: 13.5,-42.5 - parent: 2 - - uid: 4016 - components: - - type: Transform - pos: 12.5,-42.5 - parent: 2 - - uid: 4017 - components: - - type: Transform - pos: 11.5,-42.5 + pos: -16.5,-28.5 parent: 2 - uid: 4018 components: @@ -30748,11 +30531,6 @@ entities: - type: Transform pos: -30.5,1.5 parent: 2 - - uid: 4020 - components: - - type: Transform - pos: -32.5,1.5 - parent: 2 - uid: 4021 components: - type: Transform @@ -30778,76 +30556,6 @@ entities: - type: Transform pos: -22.5,-19.5 parent: 2 - - uid: 4026 - components: - - type: Transform - pos: -20.5,-43.5 - parent: 2 - - uid: 4027 - components: - - type: Transform - pos: -20.5,-44.5 - parent: 2 - - uid: 4028 - components: - - type: Transform - pos: -19.5,-39.5 - parent: 2 - - uid: 4029 - components: - - type: Transform - pos: -20.5,-45.5 - parent: 2 - - uid: 4030 - components: - - type: Transform - pos: -19.5,-44.5 - parent: 2 - - uid: 4031 - components: - - type: Transform - pos: -19.5,-45.5 - parent: 2 - - uid: 4032 - components: - - type: Transform - pos: -18.5,-43.5 - parent: 2 - - uid: 4033 - components: - - type: Transform - pos: -18.5,-44.5 - parent: 2 - - uid: 4034 - components: - - type: Transform - pos: -19.5,-43.5 - parent: 2 - - uid: 4035 - components: - - type: Transform - pos: -18.5,-45.5 - parent: 2 - - uid: 4036 - components: - - type: Transform - pos: -20.5,-39.5 - parent: 2 - - uid: 4037 - components: - - type: Transform - pos: -19.5,-42.5 - parent: 2 - - uid: 4038 - components: - - type: Transform - pos: -18.5,-39.5 - parent: 2 - - uid: 4039 - components: - - type: Transform - pos: -18.5,-41.5 - parent: 2 - uid: 4040 components: - type: Transform @@ -30868,21 +30576,11 @@ entities: - type: Transform pos: -23.5,-40.5 parent: 2 - - uid: 4044 - components: - - type: Transform - pos: -21.5,-40.5 - parent: 2 - uid: 4045 components: - type: Transform pos: -24.5,-38.5 parent: 2 - - uid: 4046 - components: - - type: Transform - pos: -20.5,-40.5 - parent: 2 - uid: 4047 components: - type: Transform @@ -30903,6 +30601,146 @@ entities: - type: Transform pos: -12.5,5.5 parent: 2 + - uid: 4404 + components: + - type: Transform + pos: 27.5,18.5 + parent: 2 + - uid: 4946 + components: + - type: Transform + pos: -19.5,-27.5 + parent: 2 + - uid: 7172 + components: + - type: Transform + pos: -26.5,-7.5 + parent: 2 + - uid: 7173 + components: + - type: Transform + pos: -20.5,28.5 + parent: 2 + - uid: 8169 + components: + - type: Transform + pos: -13.5,-17.5 + parent: 2 + - uid: 9502 + components: + - type: Transform + pos: -10.5,-17.5 + parent: 2 + - uid: 10695 + components: + - type: Transform + pos: -13.5,-39.5 + parent: 2 + - uid: 10696 + components: + - type: Transform + pos: -14.5,-39.5 + parent: 2 + - uid: 10748 + components: + - type: Transform + pos: -15.5,-39.5 + parent: 2 + - uid: 10750 + components: + - type: Transform + pos: -14.5,-40.5 + parent: 2 + - uid: 10951 + components: + - type: Transform + pos: -13.5,-41.5 + parent: 2 + - uid: 10967 + components: + - type: Transform + pos: -14.5,-41.5 + parent: 2 + - uid: 10968 + components: + - type: Transform + pos: -15.5,-41.5 + parent: 2 + - uid: 10969 + components: + - type: Transform + pos: -16.5,-41.5 + parent: 2 + - uid: 10970 + components: + - type: Transform + pos: -16.5,-40.5 + parent: 2 + - uid: 10971 + components: + - type: Transform + pos: -16.5,-39.5 + parent: 2 + - uid: 10972 + components: + - type: Transform + pos: -17.5,-40.5 + parent: 2 + - uid: 10973 + components: + - type: Transform + pos: -18.5,-40.5 + parent: 2 + - uid: 11309 + components: + - type: Transform + pos: -14.5,-17.5 + parent: 2 + - uid: 11569 + components: + - type: Transform + pos: 0.5,-27.5 + parent: 2 + - uid: 11614 + components: + - type: Transform + pos: -0.5,-27.5 + parent: 2 + - uid: 11615 + components: + - type: Transform + pos: -2.5,-28.5 + parent: 2 + - uid: 12419 + components: + - type: Transform + pos: -1.5,-27.5 + parent: 2 + - uid: 12420 + components: + - type: Transform + pos: -1.5,-28.5 + parent: 2 + - uid: 13087 + components: + - type: Transform + pos: 32.5,-15.5 + parent: 2 + - uid: 15609 + components: + - type: Transform + pos: -21.5,-40.5 + parent: 2 + - uid: 15610 + components: + - type: Transform + pos: -20.5,-40.5 + parent: 2 + - uid: 15611 + components: + - type: Transform + pos: -19.5,-40.5 + parent: 2 - uid: 17158 components: - type: Transform @@ -31328,6 +31166,11 @@ entities: - type: Transform pos: 9.5,11.5 parent: 16911 + - uid: 19519 + components: + - type: Transform + pos: -51.5,44.5 + parent: 2 - proto: CableHVStack entities: - uid: 17244 @@ -31356,6 +31199,56 @@ entities: parent: 2 - proto: CableMV entities: + - uid: 397 + components: + - type: Transform + pos: -12.5,-22.5 + parent: 2 + - uid: 604 + components: + - type: Transform + pos: -52.5,32.5 + parent: 2 + - uid: 2702 + components: + - type: Transform + pos: -17.5,-22.5 + parent: 2 + - uid: 2713 + components: + - type: Transform + pos: -19.5,-21.5 + parent: 2 + - uid: 2714 + components: + - type: Transform + pos: -20.5,-21.5 + parent: 2 + - uid: 2715 + components: + - type: Transform + pos: -21.5,-21.5 + parent: 2 + - uid: 2717 + components: + - type: Transform + pos: -8.5,-22.5 + parent: 2 + - uid: 2718 + components: + - type: Transform + pos: -15.5,-22.5 + parent: 2 + - uid: 2723 + components: + - type: Transform + pos: -8.5,-23.5 + parent: 2 + - uid: 3979 + components: + - type: Transform + pos: -17.5,-21.5 + parent: 2 - uid: 4054 components: - type: Transform @@ -31384,28 +31277,13 @@ entities: - uid: 4059 components: - type: Transform - pos: -50.5,44.5 + pos: -51.5,43.5 parent: 2 - uid: 4060 components: - type: Transform pos: -12.5,25.5 parent: 2 - - uid: 4061 - components: - - type: Transform - pos: -15.5,-15.5 - parent: 2 - - uid: 4062 - components: - - type: Transform - pos: -18.5,-21.5 - parent: 2 - - uid: 4063 - components: - - type: Transform - pos: -17.5,-15.5 - parent: 2 - uid: 4064 components: - type: Transform @@ -31449,37 +31327,17 @@ entities: - uid: 4072 components: - type: Transform - pos: -4.5,-23.5 - parent: 2 - - uid: 4073 - components: - - type: Transform - pos: -4.5,-23.5 + pos: -8.5,-24.5 parent: 2 - uid: 4074 components: - type: Transform - pos: -6.5,-23.5 - parent: 2 - - uid: 4075 - components: - - type: Transform - pos: -6.5,-20.5 - parent: 2 - - uid: 4076 - components: - - type: Transform - pos: -6.5,-21.5 - parent: 2 - - uid: 4077 - components: - - type: Transform - pos: -5.5,-23.5 + pos: -14.5,-22.5 parent: 2 - uid: 4078 components: - type: Transform - pos: -6.5,-22.5 + pos: -17.5,-20.5 parent: 2 - uid: 4079 components: @@ -31816,11 +31674,6 @@ entities: - type: Transform pos: 31.5,-16.5 parent: 2 - - uid: 4146 - components: - - type: Transform - pos: 31.5,-15.5 - parent: 2 - uid: 4147 components: - type: Transform @@ -32689,52 +32542,12 @@ entities: - uid: 4320 components: - type: Transform - pos: -21.5,-20.5 - parent: 2 - - uid: 4321 - components: - - type: Transform - pos: -19.5,-20.5 + pos: -15.5,-20.5 parent: 2 - uid: 4322 components: - type: Transform - pos: -20.5,-20.5 - parent: 2 - - uid: 4323 - components: - - type: Transform - pos: -17.5,-21.5 - parent: 2 - - uid: 4324 - components: - - type: Transform - pos: -16.5,-21.5 - parent: 2 - - uid: 4325 - components: - - type: Transform - pos: -15.5,-21.5 - parent: 2 - - uid: 4326 - components: - - type: Transform - pos: -14.5,-21.5 - parent: 2 - - uid: 4327 - components: - - type: Transform - pos: -13.5,-21.5 - parent: 2 - - uid: 4328 - components: - - type: Transform - pos: -12.5,-21.5 - parent: 2 - - uid: 4329 - components: - - type: Transform - pos: -11.5,-21.5 + pos: -16.5,-22.5 parent: 2 - uid: 4330 components: @@ -32804,7 +32617,7 @@ entities: - uid: 4343 components: - type: Transform - pos: -20.5,29.5 + pos: -17.5,27.5 parent: 2 - uid: 4344 components: @@ -33101,21 +32914,6 @@ entities: - type: Transform pos: -17.5,26.5 parent: 2 - - uid: 4403 - components: - - type: Transform - pos: -18.5,26.5 - parent: 2 - - uid: 4404 - components: - - type: Transform - pos: -19.5,26.5 - parent: 2 - - uid: 4405 - components: - - type: Transform - pos: -19.5,27.5 - parent: 2 - uid: 4406 components: - type: Transform @@ -33206,11 +33004,6 @@ entities: - type: Transform pos: -28.5,12.5 parent: 2 - - uid: 4424 - components: - - type: Transform - pos: 26.5,18.5 - parent: 2 - uid: 4425 components: - type: Transform @@ -34391,25 +34184,10 @@ entities: - type: Transform pos: -50.5,30.5 parent: 2 - - uid: 4661 - components: - - type: Transform - pos: -50.5,31.5 - parent: 2 - - uid: 4662 - components: - - type: Transform - pos: -50.5,33.5 - parent: 2 - - uid: 4663 - components: - - type: Transform - pos: -50.5,32.5 - parent: 2 - uid: 4664 components: - type: Transform - pos: -49.5,33.5 + pos: -52.5,31.5 parent: 2 - uid: 4665 components: @@ -34829,7 +34607,7 @@ entities: - uid: 4748 components: - type: Transform - pos: -25.5,-7.5 + pos: 27.5,17.5 parent: 2 - uid: 4749 components: @@ -35136,21 +34914,11 @@ entities: - type: Transform pos: -17.5,-11.5 parent: 2 - - uid: 4810 - components: - - type: Transform - pos: -17.5,-13.5 - parent: 2 - uid: 4811 components: - type: Transform pos: -17.5,-12.5 parent: 2 - - uid: 4812 - components: - - type: Transform - pos: -18.5,-13.5 - parent: 2 - uid: 4813 components: - type: Transform @@ -35491,11 +35259,6 @@ entities: - type: Transform pos: 54.5,-16.5 parent: 2 - - uid: 4881 - components: - - type: Transform - pos: -18.5,-20.5 - parent: 2 - uid: 4882 components: - type: Transform @@ -35816,61 +35579,6 @@ entities: - type: Transform pos: 3.5,26.5 parent: 2 - - uid: 4946 - components: - - type: Transform - pos: -18.5,-17.5 - parent: 2 - - uid: 4947 - components: - - type: Transform - pos: -18.5,-19.5 - parent: 2 - - uid: 4948 - components: - - type: Transform - pos: -18.5,-16.5 - parent: 2 - - uid: 4949 - components: - - type: Transform - pos: -18.5,-15.5 - parent: 2 - - uid: 4950 - components: - - type: Transform - pos: -18.5,-18.5 - parent: 2 - - uid: 4951 - components: - - type: Transform - pos: -14.5,-15.5 - parent: 2 - - uid: 4952 - components: - - type: Transform - pos: -13.5,-15.5 - parent: 2 - - uid: 4953 - components: - - type: Transform - pos: -16.5,-15.5 - parent: 2 - - uid: 4954 - components: - - type: Transform - pos: -11.5,-15.5 - parent: 2 - - uid: 4955 - components: - - type: Transform - pos: -10.5,-15.5 - parent: 2 - - uid: 4956 - components: - - type: Transform - pos: -12.5,-15.5 - parent: 2 - uid: 4957 components: - type: Transform @@ -36021,6 +35729,66 @@ entities: - type: Transform pos: -30.5,40.5 parent: 2 + - uid: 6539 + components: + - type: Transform + pos: -11.5,-22.5 + parent: 2 + - uid: 6592 + components: + - type: Transform + pos: -10.5,-22.5 + parent: 2 + - uid: 6596 + components: + - type: Transform + pos: -13.5,-22.5 + parent: 2 + - uid: 8208 + components: + - type: Transform + pos: -9.5,-22.5 + parent: 2 + - uid: 9823 + components: + - type: Transform + pos: -16.5,-20.5 + parent: 2 + - uid: 9833 + components: + - type: Transform + pos: -18.5,-21.5 + parent: 2 + - uid: 13080 + components: + - type: Transform + pos: 32.5,-16.5 + parent: 2 + - uid: 13546 + components: + - type: Transform + pos: -26.5,-7.5 + parent: 2 + - uid: 13551 + components: + - type: Transform + pos: -26.5,-6.5 + parent: 2 + - uid: 13552 + components: + - type: Transform + pos: 27.5,18.5 + parent: 2 + - uid: 14231 + components: + - type: Transform + pos: -18.5,-13.5 + parent: 2 + - uid: 14236 + components: + - type: Transform + pos: -17.5,-13.5 + parent: 2 - uid: 17247 components: - type: Transform @@ -36676,8 +36444,248 @@ entities: - type: Transform pos: 13.5,4.5 parent: 16911 + - uid: 19373 + components: + - type: Transform + pos: -52.5,33.5 + parent: 2 + - uid: 19374 + components: + - type: Transform + pos: -52.5,34.5 + parent: 2 + - uid: 19375 + components: + - type: Transform + pos: -53.5,34.5 + parent: 2 + - uid: 19376 + components: + - type: Transform + pos: -54.5,34.5 + parent: 2 + - uid: 19377 + components: + - type: Transform + pos: -55.5,34.5 + parent: 2 + - uid: 19461 + components: + - type: Transform + pos: -17.5,-26.5 + parent: 2 + - uid: 19462 + components: + - type: Transform + pos: -17.5,-27.5 + parent: 2 + - uid: 19463 + components: + - type: Transform + pos: -17.5,-28.5 + parent: 2 + - uid: 19464 + components: + - type: Transform + pos: -16.5,-28.5 + parent: 2 + - uid: 19465 + components: + - type: Transform + pos: -15.5,-28.5 + parent: 2 + - uid: 19466 + components: + - type: Transform + pos: -14.5,-28.5 + parent: 2 + - uid: 19467 + components: + - type: Transform + pos: -13.5,-28.5 + parent: 2 + - uid: 19468 + components: + - type: Transform + pos: -12.5,-28.5 + parent: 2 + - uid: 19469 + components: + - type: Transform + pos: -11.5,-28.5 + parent: 2 + - uid: 19470 + components: + - type: Transform + pos: -10.5,-28.5 + parent: 2 + - uid: 19471 + components: + - type: Transform + pos: -9.5,-28.5 + parent: 2 + - uid: 19472 + components: + - type: Transform + pos: -8.5,-28.5 + parent: 2 + - uid: 19473 + components: + - type: Transform + pos: -7.5,-28.5 + parent: 2 + - uid: 19474 + components: + - type: Transform + pos: -6.5,-28.5 + parent: 2 + - uid: 19475 + components: + - type: Transform + pos: -5.5,-28.5 + parent: 2 + - uid: 19476 + components: + - type: Transform + pos: -4.5,-28.5 + parent: 2 + - uid: 19477 + components: + - type: Transform + pos: -3.5,-28.5 + parent: 2 + - uid: 19478 + components: + - type: Transform + pos: -2.5,-28.5 + parent: 2 + - uid: 19479 + components: + - type: Transform + pos: -1.5,-28.5 + parent: 2 + - uid: 19480 + components: + - type: Transform + pos: -1.5,-27.5 + parent: 2 + - uid: 19481 + components: + - type: Transform + pos: -0.5,-27.5 + parent: 2 + - uid: 19482 + components: + - type: Transform + pos: 0.5,-27.5 + parent: 2 + - uid: 19483 + components: + - type: Transform + pos: 1.5,-27.5 + parent: 2 + - uid: 19484 + components: + - type: Transform + pos: 1.5,-26.5 + parent: 2 + - uid: 19485 + components: + - type: Transform + pos: 1.5,-25.5 + parent: 2 + - uid: 19486 + components: + - type: Transform + pos: 1.5,-24.5 + parent: 2 + - uid: 19495 + components: + - type: Transform + pos: 22.5,-31.5 + parent: 2 + - uid: 19496 + components: + - type: Transform + pos: 21.5,-31.5 + parent: 2 + - uid: 19497 + components: + - type: Transform + pos: 20.5,-31.5 + parent: 2 + - uid: 19498 + components: + - type: Transform + pos: 19.5,-31.5 + parent: 2 + - uid: 19499 + components: + - type: Transform + pos: 18.5,-31.5 + parent: 2 + - uid: 19500 + components: + - type: Transform + pos: 17.5,-31.5 + parent: 2 + - uid: 19501 + components: + - type: Transform + pos: 16.5,-31.5 + parent: 2 + - uid: 19502 + components: + - type: Transform + pos: 16.5,-30.5 + parent: 2 + - uid: 19503 + components: + - type: Transform + pos: 16.5,-29.5 + parent: 2 + - uid: 19504 + components: + - type: Transform + pos: 16.5,-28.5 + parent: 2 + - uid: 19505 + components: + - type: Transform + pos: 15.5,-28.5 + parent: 2 + - uid: 19506 + components: + - type: Transform + pos: 15.5,-27.5 + parent: 2 + - uid: 19520 + components: + - type: Transform + pos: -51.5,44.5 + parent: 2 + - uid: 19522 + components: + - type: Transform + pos: 32.5,-15.5 + parent: 2 + - uid: 19528 + components: + - type: Transform + pos: -19.5,28.5 + parent: 2 + - uid: 19529 + components: + - type: Transform + pos: -20.5,28.5 + parent: 2 - proto: CableTerminal entities: + - uid: 4405 + components: + - type: Transform + pos: -25.5,-6.5 + parent: 2 - uid: 4987 components: - type: Transform @@ -36725,17 +36733,11 @@ entities: rot: 3.141592653589793 rad pos: 23.5,-42.5 parent: 2 - - uid: 4995 + - uid: 13550 components: - type: Transform rot: 3.141592653589793 rad - pos: -19.5,-41.5 - parent: 2 - - uid: 4996 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-41.5 + pos: 26.5,17.5 parent: 2 - uid: 17378 components: @@ -36797,6 +36799,18 @@ entities: rot: -1.5707963267948966 rad pos: 24.5,-10.5 parent: 16911 + - uid: 19521 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -50.5,43.5 + parent: 2 + - uid: 19524 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 31.5,-16.5 + parent: 2 - proto: CandleGreenInfinite entities: - uid: 4997 @@ -36887,6 +36901,23 @@ entities: - type: Transform pos: -3.4659548,-3.9046545 parent: 2 +- proto: CarbonDioxideCanister + entities: + - uid: 3088 + components: + - type: Transform + pos: -8.5,-25.5 + parent: 2 + - uid: 5740 + components: + - type: Transform + pos: -4.5,-39.5 + parent: 2 + - uid: 8070 + components: + - type: Transform + pos: -8.5,-26.5 + parent: 2 - proto: CargoMailTeleporter entities: - uid: 5014 @@ -37426,6 +37457,76 @@ entities: spent: True - proto: Catwalk entities: + - uid: 679 + components: + - type: Transform + pos: -8.5,-27.5 + parent: 2 + - uid: 681 + components: + - type: Transform + pos: -9.5,-27.5 + parent: 2 + - uid: 684 + components: + - type: Transform + pos: -9.5,-26.5 + parent: 2 + - uid: 761 + components: + - type: Transform + pos: -8.5,-26.5 + parent: 2 + - uid: 764 + components: + - type: Transform + pos: -8.5,-25.5 + parent: 2 + - uid: 854 + components: + - type: Transform + pos: -7.5,-25.5 + parent: 2 + - uid: 980 + components: + - type: Transform + pos: -6.5,-25.5 + parent: 2 + - uid: 981 + components: + - type: Transform + pos: -7.5,-26.5 + parent: 2 + - uid: 2770 + components: + - type: Transform + pos: -7.5,-27.5 + parent: 2 + - uid: 2837 + components: + - type: Transform + pos: -5.5,-26.5 + parent: 2 + - uid: 2840 + components: + - type: Transform + pos: -6.5,-26.5 + parent: 2 + - uid: 2843 + components: + - type: Transform + pos: -5.5,-25.5 + parent: 2 + - uid: 2844 + components: + - type: Transform + pos: -5.5,-27.5 + parent: 2 + - uid: 3098 + components: + - type: Transform + pos: -6.5,-27.5 + parent: 2 - uid: 5102 components: - type: Transform @@ -37497,11 +37598,6 @@ entities: - type: Transform pos: 36.5,1.5 parent: 2 - - uid: 5116 - components: - - type: Transform - pos: 1.5,-29.5 - parent: 2 - uid: 5117 components: - type: Transform @@ -38613,16 +38709,6 @@ entities: - type: Transform pos: -76.5,17.5 parent: 2 - - uid: 5336 - components: - - type: Transform - pos: -17.5,-29.5 - parent: 2 - - uid: 5337 - components: - - type: Transform - pos: -14.5,-29.5 - parent: 2 - uid: 5338 components: - type: Transform @@ -40659,36 +40745,6 @@ entities: - type: Transform pos: -35.5,-35.5 parent: 2 - - uid: 5740 - components: - - type: Transform - pos: -6.5,-30.5 - parent: 2 - - uid: 5741 - components: - - type: Transform - pos: -6.5,-31.5 - parent: 2 - - uid: 5742 - components: - - type: Transform - pos: -5.5,-30.5 - parent: 2 - - uid: 5743 - components: - - type: Transform - pos: -5.5,-31.5 - parent: 2 - - uid: 5744 - components: - - type: Transform - pos: -5.5,-32.5 - parent: 2 - - uid: 5745 - components: - - type: Transform - pos: -6.5,-32.5 - parent: 2 - uid: 5746 components: - type: Transform @@ -40827,6 +40883,96 @@ entities: rot: 1.5707963267948966 rad pos: 38.5,22.5 parent: 2 + - uid: 7836 + components: + - type: Transform + pos: -9.5,-25.5 + parent: 2 + - uid: 7838 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-39.5 + parent: 2 + - uid: 7841 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-39.5 + parent: 2 + - uid: 7842 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-39.5 + parent: 2 + - uid: 7843 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-39.5 + parent: 2 + - uid: 7845 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-36.5 + parent: 2 + - uid: 10058 + components: + - type: Transform + pos: -24.5,-29.5 + parent: 2 + - uid: 11185 + components: + - type: Transform + pos: -24.5,-30.5 + parent: 2 + - uid: 11298 + components: + - type: Transform + pos: -24.5,-31.5 + parent: 2 + - uid: 11299 + components: + - type: Transform + pos: -25.5,-29.5 + parent: 2 + - uid: 11300 + components: + - type: Transform + pos: -25.5,-30.5 + parent: 2 + - uid: 11301 + components: + - type: Transform + pos: -25.5,-31.5 + parent: 2 + - uid: 11304 + components: + - type: Transform + pos: -26.5,-29.5 + parent: 2 + - uid: 11305 + components: + - type: Transform + pos: -26.5,-30.5 + parent: 2 + - uid: 11350 + components: + - type: Transform + pos: -26.5,-31.5 + parent: 2 + - uid: 12157 + components: + - type: Transform + pos: -20.5,-32.5 + parent: 2 + - uid: 14314 + components: + - type: Transform + pos: -17.5,-29.5 + parent: 2 - uid: 17390 components: - type: Transform @@ -42563,18 +42709,6 @@ entities: rot: 1.5707963267948966 rad pos: 22.5,-33.5 parent: 2 - - uid: 5795 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-32.5 - parent: 2 - - uid: 5796 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-31.5 - parent: 2 - uid: 5797 components: - type: Transform @@ -42849,6 +42983,42 @@ entities: rot: -1.5707963267948966 rad pos: -47.53233,37.54135 parent: 2 + - uid: 10977 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-38.5 + parent: 2 + - uid: 10978 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-37.5 + parent: 2 + - uid: 10979 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-36.5 + parent: 2 + - uid: 10980 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-36.5 + parent: 2 + - uid: 10981 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-37.5 + parent: 2 + - uid: 10982 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-38.5 + parent: 2 - proto: ChairGreyscale entities: - uid: 5844 @@ -42977,6 +43147,16 @@ entities: parent: 16911 - proto: ChairOfficeLight entities: + - uid: 2828 + components: + - type: Transform + pos: -12.5,-25.5 + parent: 2 + - uid: 3655 + components: + - type: Transform + pos: -9.5,-22.5 + parent: 2 - uid: 5863 components: - type: Transform @@ -43069,6 +43249,22 @@ entities: rot: -1.5707963267948966 rad pos: -20.542828,21.603903 parent: 2 + - uid: 10983 + components: + - type: Transform + pos: -22.5,-35.5 + parent: 2 + - uid: 12410 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-17.5 + parent: 2 + - uid: 12478 + components: + - type: Transform + pos: -1.4284668,-33.681366 + parent: 2 - proto: ChairPilotSeat entities: - uid: 5879 @@ -43242,6 +43438,31 @@ entities: - type: Transform pos: -37.626637,39.635124 parent: 2 + - uid: 15428 + components: + - type: Transform + pos: -11.820269,-26.3976 + parent: 2 + - uid: 15433 + components: + - type: Transform + pos: -11.867144,-26.2726 + parent: 2 + - uid: 15438 + components: + - type: Transform + pos: -11.632769,-26.36635 + parent: 2 + - uid: 15439 + components: + - type: Transform + pos: -12.226519,-26.444475 + parent: 2 + - uid: 15453 + components: + - type: Transform + pos: -12.335894,-26.381975 + parent: 2 - proto: CigCartonGreen entities: - uid: 5905 @@ -43286,18 +43507,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -43384,18 +43595,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - uid: 5922 components: - type: Transform @@ -43621,20 +43822,15 @@ entities: immutable: False temperature: 293.147 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: ClosetFireFilled entities: + - uid: 5795 + components: + - type: Transform + pos: 22.5,-26.5 + parent: 2 - uid: 5965 components: - type: Transform @@ -43676,18 +43872,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - uid: 5972 components: - type: Transform @@ -43738,11 +43924,6 @@ entities: - type: Transform pos: 11.5,-14.5 parent: 2 - - uid: 5982 - components: - - type: Transform - pos: 22.5,-28.5 - parent: 2 - uid: 5983 components: - type: Transform @@ -43841,18 +44022,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -43875,18 +44046,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8977377 - - 7.139109 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8977377 + Nitrogen: 7.139109 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44028,18 +44189,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44062,18 +44213,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44096,18 +44237,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44171,15 +44302,15 @@ entities: parent: 2 - proto: ClosetRadiationSuitFilled entities: - - uid: 6037 + - uid: 11153 components: - type: Transform - pos: -22.5,-39.5 + pos: -19.5,-41.5 parent: 2 - - uid: 6038 + - uid: 11154 components: - type: Transform - pos: -23.5,-39.5 + pos: -20.5,-41.5 parent: 2 - proto: ClosetTool entities: @@ -44244,18 +44375,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44279,18 +44400,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44316,18 +44427,8 @@ entities: immutable: False temperature: 293.14697 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44394,6 +44495,13 @@ entities: pos: -45.52265,33.840355 parent: 2 - type: EyeProtection +- proto: ClothingEyesGlassesMeson + entities: + - uid: 8395 + components: + - type: Transform + pos: -15.597261,-17.287827 + parent: 2 - proto: ClothingEyesGlassesSunglasses entities: - uid: 6065 @@ -44401,6 +44509,18 @@ entities: - type: Transform pos: 15.503975,-19.02182 parent: 2 +- proto: ClothingEyesGlassesThermal + entities: + - uid: 10015 + components: + - type: Transform + pos: -15.581636,-17.600327 + parent: 2 + - uid: 14315 + components: + - type: Transform + pos: 0.5,-34.5 + parent: 2 - proto: ClothingHandsGlovesColorBlack entities: - uid: 6055 @@ -44442,6 +44562,12 @@ entities: - type: Transform pos: -32.285084,64.626755 parent: 2 + - uid: 8396 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.269136,-17.475327 + parent: 2 - proto: ClothingHandsGlovesFingerlessInsulated entities: - uid: 6071 @@ -44790,10 +44916,10 @@ entities: - type: InsideEntityStorage - proto: ClothingShoesBling entities: - - uid: 6119 + - uid: 11496 components: - type: Transform - pos: -4.46716,-21.245726 + pos: -53.5,34.5 parent: 2 - proto: ClothingShoesBootsLaceup entities: @@ -44809,6 +44935,16 @@ entities: - type: Transform pos: -20.337545,-12.3313 parent: 2 + - uid: 16754 + components: + - type: Transform + pos: 15.498966,-29.263462 + parent: 2 + - uid: 19356 + components: + - type: Transform + pos: 15.483341,-29.700962 + parent: 2 - proto: ClothingShoesBootsWinterSec entities: - uid: 6059 @@ -44827,10 +44963,10 @@ entities: parent: 2 - proto: ClothingShoesSlippers entities: - - uid: 6123 + - uid: 3282 components: - type: Transform - pos: -13.53664,-18.218166 + pos: -5.2967405,-22.787128 parent: 2 - proto: ClothingUniformJumpskirtDetective entities: @@ -44995,11 +45131,6 @@ entities: rot: -1.5707963267948966 rad pos: 10.5,-6.5 parent: 2 - - uid: 6141 - components: - - type: Transform - pos: -13.5,-22.5 - parent: 2 - uid: 6142 components: - type: Transform @@ -45035,6 +45166,18 @@ entities: rot: 1.5707963267948966 rad pos: 29.5,-11.5 parent: 2 + - uid: 8264 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-23.5 + parent: 2 + - uid: 11789 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-23.5 + parent: 2 - uid: 17684 components: - type: Transform @@ -45070,6 +45213,12 @@ entities: rot: 3.141592653589793 rad pos: -33.5,-39.5 parent: 2 + - uid: 7878 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-25.5 + parent: 2 - proto: ComputerAnalysisConsole entities: - uid: 6150 @@ -45089,6 +45238,14 @@ entities: rot: 1.5707963267948966 rad pos: -26.5,-14.5 parent: 2 +- proto: ComputerAtmosMonitoring + entities: + - uid: 7852 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-25.5 + parent: 2 - proto: computerBodyScanner entities: - uid: 6152 @@ -45169,10 +45326,11 @@ entities: parent: 2 - proto: ComputerCargoOrdersEngineering entities: - - uid: 6156 + - uid: 581 components: - type: Transform - pos: -17.5,-17.5 + rot: 1.5707963267948966 rad + pos: -18.5,-18.5 parent: 2 - proto: ComputerCargoOrdersMedical entities: @@ -45302,6 +45460,12 @@ entities: parent: 2 - proto: ComputerPowerMonitoring entities: + - uid: 589 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-17.5 + parent: 2 - uid: 6176 components: - type: Transform @@ -45462,6 +45626,12 @@ entities: rot: -1.5707963267948966 rad pos: 15.5,20.5 parent: 2 + - uid: 14316 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -18.5,-31.5 + parent: 2 - uid: 17694 components: - type: Transform @@ -45926,18 +46096,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 6276 components: - type: Transform @@ -45959,18 +46119,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46000,18 +46150,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46060,6 +46200,25 @@ entities: - type: Transform pos: 31.5,-1.5 parent: 2 +- proto: CrateEngineering + entities: + - uid: 12333 + components: + - type: Transform + pos: -0.5,-32.5 + parent: 2 + - type: ContainerContainer + containers: + entity_storage: !type:Container + showEnts: False + occludes: True + ents: + - 12473 + - 12474 + paper_label: !type:ContainerSlot + showEnts: False + occludes: True + ent: null - proto: CrateEngineeringAMEControl entities: - uid: 6283 @@ -46098,6 +46257,13 @@ entities: - type: Transform pos: -30.5,0.5 parent: 2 +- proto: CrateEngineeringSingularityCollector + entities: + - uid: 11791 + components: + - type: Transform + pos: -16.5,-39.5 + parent: 2 - proto: CrateEngineeringSolar entities: - uid: 6287 @@ -46111,18 +46277,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - proto: CrateFreezer entities: - uid: 6288 @@ -46130,6 +46286,23 @@ entities: - type: Transform pos: -19.5,7.5 parent: 2 + - uid: 15588 + components: + - type: Transform + pos: -16.5,-33.5 + parent: 2 + - type: ContainerContainer + containers: + entity_storage: !type:Container + showEnts: False + occludes: True + ents: + - 15589 + - 15590 + paper_label: !type:ContainerSlot + showEnts: False + occludes: True + ent: null - proto: CrateFunInstrumentsVariety entities: - uid: 6289 @@ -46152,18 +46325,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateFunToyBox entities: - uid: 6291 @@ -46187,18 +46350,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46226,18 +46379,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46360,18 +46503,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46422,18 +46555,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46460,18 +46583,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46498,18 +46611,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46536,18 +46639,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46576,18 +46669,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46724,18 +46807,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 6352 components: - type: Transform @@ -47701,6 +47774,95 @@ entities: - type: Transform pos: -22.345272,-8.254836 parent: 2 +- proto: DecalSpawnerDirtWide + entities: + - uid: 396 + components: + - type: Transform + pos: -24.5,-33.5 + parent: 2 + - uid: 3421 + components: + - type: Transform + pos: -14.5,-14.5 + parent: 2 + - uid: 3422 + components: + - type: Transform + pos: -14.5,-30.5 + parent: 2 + - uid: 3978 + components: + - type: Transform + pos: -7.5,-35.5 + parent: 2 + - uid: 4323 + components: + - type: Transform + pos: -20.5,-40.5 + parent: 2 + - uid: 4325 + components: + - type: Transform + pos: -5.5,-29.5 + parent: 2 + - uid: 4948 + components: + - type: Transform + pos: -9.5,-18.5 + parent: 2 + - uid: 4953 + components: + - type: Transform + pos: -15.5,-40.5 + parent: 2 + - uid: 4954 + components: + - type: Transform + pos: 4.5,-31.5 + parent: 2 + - uid: 6478 + components: + - type: Transform + pos: -14.5,-22.5 + parent: 2 + - uid: 7405 + components: + - type: Transform + pos: -24.5,-38.5 + parent: 2 + - uid: 8222 + components: + - type: Transform + pos: -24.5,-22.5 + parent: 2 + - uid: 8256 + components: + - type: Transform + pos: -17.5,-19.5 + parent: 2 +- proto: DecalSpawnerFlowers + entities: + - uid: 2697 + components: + - type: Transform + pos: -5.5,-17.5 + parent: 2 + - uid: 3423 + components: + - type: Transform + pos: -7.5,-19.5 + parent: 2 + - uid: 3980 + components: + - type: Transform + pos: -4.5,-19.5 + parent: 2 + - uid: 4326 + components: + - type: Transform + pos: -6.5,-18.5 + parent: 2 - proto: DefaultStationBeaconAI entities: - uid: 6422 @@ -47748,13 +47910,6 @@ entities: - type: Transform pos: -23.5,-13.5 parent: 2 -- proto: DefaultStationBeaconAtmospherics - entities: - - uid: 6429 - components: - - type: Transform - pos: -9.5,-26.5 - parent: 2 - proto: DefaultStationBeaconBar entities: - uid: 6430 @@ -47804,13 +47959,6 @@ entities: - type: Transform pos: 25.5,4.5 parent: 2 -- proto: DefaultStationBeaconCERoom - entities: - - uid: 6437 - components: - - type: Transform - pos: -13.5,-19.5 - parent: 2 - proto: DefaultStationBeaconChapel entities: - uid: 6438 @@ -47860,13 +48008,6 @@ entities: - type: Transform pos: -28.5,27.5 parent: 2 -- proto: DefaultStationBeaconEngineering - entities: - - uid: 6445 - components: - - type: Transform - pos: -17.5,-21.5 - parent: 2 - proto: DefaultStationBeaconEvac entities: - uid: 6446 @@ -47876,10 +48017,10 @@ entities: parent: 2 - proto: DefaultStationBeaconEVAStorage entities: - - uid: 6447 + - uid: 19364 components: - type: Transform - pos: -51.5,33.5 + pos: 17.5,-30.5 parent: 2 - proto: DefaultStationBeaconGravGen entities: @@ -48091,10 +48232,10 @@ entities: parent: 2 - proto: DefaultStationBeaconVault entities: - - uid: 6478 + - uid: 12544 components: - type: Transform - pos: -7.5,-21.5 + pos: -53.5,35.5 parent: 2 - proto: DefaultStationBeaconWardensOffice entities: @@ -48170,6 +48311,11 @@ entities: parent: 2 - proto: DeskBell entities: + - uid: 1281 + components: + - type: Transform + pos: -16.425982,-16.430346 + parent: 2 - uid: 6491 components: - type: Transform @@ -48189,6 +48335,12 @@ entities: parent: 2 - proto: DisposalBend entities: + - uid: 4329 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-23.5 + parent: 2 - uid: 6494 components: - type: Transform @@ -48223,12 +48375,6 @@ entities: rot: 1.5707963267948966 rad pos: 2.5,-1.5 parent: 2 - - uid: 6500 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 29.5,36.5 - parent: 2 - uid: 6501 components: - type: Transform @@ -48436,23 +48582,6 @@ entities: rot: 1.5707963267948966 rad pos: -24.5,-23.5 parent: 2 - - uid: 6537 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-23.5 - parent: 2 - - uid: 6538 - components: - - type: Transform - pos: -16.5,-14.5 - parent: 2 - - uid: 6539 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-14.5 - parent: 2 - uid: 6540 components: - type: Transform @@ -48620,6 +48749,24 @@ entities: - type: Transform pos: -23.5,6.5 parent: 2 + - uid: 11601 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-22.5 + parent: 2 + - uid: 12361 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-17.5 + parent: 2 + - uid: 12657 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-17.5 + parent: 2 - uid: 17811 components: - type: Transform @@ -48676,6 +48823,12 @@ entities: parent: 16911 - proto: DisposalJunction entities: + - uid: 2704 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-15.5 + parent: 2 - uid: 6569 components: - type: Transform @@ -48808,12 +48961,6 @@ entities: rot: 3.141592653589793 rad pos: 20.5,-7.5 parent: 2 - - uid: 6592 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -16.5,-21.5 - parent: 2 - uid: 6593 components: - type: Transform @@ -48832,13 +48979,25 @@ entities: rot: 3.141592653589793 rad pos: 7.5,37.5 parent: 2 -- proto: DisposalJunctionFlipped - entities: - - uid: 6596 + - uid: 7001 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-22.5 + parent: 2 + - uid: 12448 components: - type: Transform rot: 1.5707963267948966 rad - pos: -10.5,-15.5 + pos: -14.5,-17.5 + parent: 2 +- proto: DisposalJunctionFlipped + entities: + - uid: 3426 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-14.5 parent: 2 - uid: 6597 components: @@ -48935,6 +49094,57 @@ entities: parent: 2 - proto: DisposalPipe entities: + - uid: 17 + components: + - type: Transform + pos: -14.5,-18.5 + parent: 2 + - uid: 426 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-17.5 + parent: 2 + - uid: 2712 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-17.5 + parent: 2 + - uid: 2719 + components: + - type: Transform + pos: -18.5,-13.5 + parent: 2 + - uid: 2721 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-16.5 + parent: 2 + - uid: 3425 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-14.5 + parent: 2 + - uid: 4881 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-17.5 + parent: 2 + - uid: 4952 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -16.5,-14.5 + parent: 2 + - uid: 6156 + components: + - type: Transform + pos: -8.5,-13.5 + parent: 2 - uid: 6613 components: - type: Transform @@ -49267,8 +49477,7 @@ entities: - uid: 6668 components: - type: Transform - rot: 3.141592653589793 rad - pos: 29.5,35.5 + pos: 30.5,35.5 parent: 2 - uid: 6669 components: @@ -50221,15 +50430,11 @@ entities: - type: Transform pos: -8.5,-11.5 parent: 2 - - uid: 6841 - components: - - type: Transform - pos: -8.5,-13.5 - parent: 2 - uid: 6842 components: - type: Transform - pos: -8.5,-14.5 + rot: -1.5707963267948966 rad + pos: -15.5,-14.5 parent: 2 - uid: 6843 components: @@ -51112,77 +51317,11 @@ entities: rot: 3.141592653589793 rad pos: 23.5,-29.5 parent: 2 - - uid: 6998 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -9.5,-15.5 - parent: 2 - - uid: 6999 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-15.5 - parent: 2 - - uid: 7000 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-15.5 - parent: 2 - - uid: 7001 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -14.5,-15.5 - parent: 2 - - uid: 7002 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-15.5 - parent: 2 - - uid: 7003 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -12.5,-15.5 - parent: 2 - - uid: 7004 - components: - - type: Transform - pos: -16.5,-16.5 - parent: 2 - - uid: 7005 - components: - - type: Transform - pos: -16.5,-18.5 - parent: 2 - uid: 7006 - components: - - type: Transform - pos: -16.5,-19.5 - parent: 2 - - uid: 7007 - components: - - type: Transform - pos: -16.5,-20.5 - parent: 2 - - uid: 7008 - components: - - type: Transform - pos: -16.5,-22.5 - parent: 2 - - uid: 7009 - components: - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - uid: 7010 components: - type: Transform rot: -1.5707963267948966 rad - pos: -17.5,-23.5 + pos: -12.5,-14.5 parent: 2 - uid: 7011 components: @@ -51190,12 +51329,6 @@ entities: rot: -1.5707963267948966 rad pos: -19.5,-23.5 parent: 2 - - uid: 7012 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-23.5 - parent: 2 - uid: 7013 components: - type: Transform @@ -51304,17 +51437,6 @@ entities: rot: 1.5707963267948966 rad pos: -22.5,-34.5 parent: 2 - - uid: 7032 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-14.5 - parent: 2 - - uid: 7033 - components: - - type: Transform - pos: -18.5,-13.5 - parent: 2 - uid: 7034 components: - type: Transform @@ -51767,24 +51889,6 @@ entities: rot: 1.5707963267948966 rad pos: 1.5,-9.5 parent: 2 - - uid: 7116 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-21.5 - parent: 2 - - uid: 7117 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-21.5 - parent: 2 - - uid: 7118 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -14.5,-21.5 - parent: 2 - uid: 7119 components: - type: Transform @@ -52029,6 +52133,132 @@ entities: rot: 1.5707963267948966 rad pos: -26.5,6.5 parent: 2 + - uid: 7164 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 29.5,34.5 + parent: 2 + - uid: 7167 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-14.5 + parent: 2 + - uid: 7217 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-14.5 + parent: 2 + - uid: 7276 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-14.5 + parent: 2 + - uid: 7280 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-14.5 + parent: 2 + - uid: 8255 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-14.5 + parent: 2 + - uid: 9491 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-17.5 + parent: 2 + - uid: 9711 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-23.5 + parent: 2 + - uid: 12034 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-22.5 + parent: 2 + - uid: 12043 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-22.5 + parent: 2 + - uid: 12357 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-22.5 + parent: 2 + - uid: 12360 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-22.5 + parent: 2 + - uid: 12422 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-18.5 + parent: 2 + - uid: 12423 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-22.5 + parent: 2 + - uid: 12424 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-19.5 + parent: 2 + - uid: 12445 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -9.5,-17.5 + parent: 2 + - uid: 12446 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-17.5 + parent: 2 + - uid: 12447 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-22.5 + parent: 2 + - uid: 13057 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -16.5,-17.5 + parent: 2 + - uid: 13322 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-20.5 + parent: 2 + - uid: 13323 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-21.5 + parent: 2 - uid: 17820 components: - type: Transform @@ -52606,12 +52836,6 @@ entities: parent: 16911 - proto: DisposalSignalRouterFlipped entities: - - uid: 7164 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 29.5,34.5 - parent: 2 - uid: 7165 components: - type: Transform @@ -52620,17 +52844,23 @@ entities: parent: 2 - proto: DisposalTrunk entities: + - uid: 2722 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-15.5 + parent: 2 + - uid: 6500 + components: + - type: Transform + pos: 30.5,36.5 + parent: 2 - uid: 7166 components: - type: Transform rot: 3.141592653589793 rad pos: 9.5,-3.5 parent: 2 - - uid: 7167 - components: - - type: Transform - pos: -10.5,-14.5 - parent: 2 - uid: 7168 components: - type: Transform @@ -52655,17 +52885,6 @@ entities: rot: 3.141592653589793 rad pos: 12.5,25.5 parent: 2 - - uid: 7172 - components: - - type: Transform - pos: 30.5,35.5 - parent: 2 - - uid: 7173 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 30.5,36.5 - parent: 2 - uid: 7174 components: - type: Transform @@ -52916,12 +53135,6 @@ entities: rot: 3.141592653589793 rad pos: 5.5,-9.5 parent: 2 - - uid: 7217 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -12.5,-21.5 - parent: 2 - uid: 7218 components: - type: Transform @@ -52957,8 +53170,29 @@ entities: rot: 3.141592653589793 rad pos: -23.5,5.5 parent: 2 + - uid: 9845 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-19.5 + parent: 2 + - uid: 9973 + components: + - type: Transform + pos: -10.5,-21.5 + parent: 2 - proto: DisposalUnit entities: + - uid: 2720 + components: + - type: Transform + pos: -18.5,-15.5 + parent: 2 + - uid: 3644 + components: + - type: Transform + pos: -10.5,-21.5 + parent: 2 - uid: 7224 components: - type: Transform @@ -52999,11 +53233,6 @@ entities: - type: Transform pos: 33.5,13.5 parent: 2 - - uid: 7232 - components: - - type: Transform - pos: -10.5,-14.5 - parent: 2 - uid: 7233 components: - type: Transform @@ -53204,11 +53433,6 @@ entities: - type: Transform pos: 5.5,-9.5 parent: 2 - - uid: 7273 - components: - - type: Transform - pos: -12.5,-21.5 - parent: 2 - uid: 7274 components: - type: Transform @@ -53219,13 +53443,23 @@ entities: - type: Transform pos: 36.5,17.5 parent: 2 -- proto: DisposalYJunction - entities: - - uid: 7276 + - uid: 8394 components: - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-15.5 + pos: -14.5,-19.5 + parent: 2 + - uid: 16393 + components: + - type: Transform + pos: -18.5,-26.5 + parent: 2 +- proto: DisposalYJunction + entities: + - uid: 2705 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-14.5 parent: 2 - uid: 7277 components: @@ -53243,12 +53477,6 @@ entities: - type: Transform pos: 14.5,26.5 parent: 2 - - uid: 7280 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-15.5 - parent: 2 - proto: DogBed entities: - uid: 7281 @@ -53295,6 +53523,13 @@ entities: - type: Transform pos: 1.571956,-18.370005 parent: 2 +- proto: DoubleEmergencyOxygenTankFilled + entities: + - uid: 19383 + components: + - type: Transform + pos: -13.377129,-27.273674 + parent: 2 - proto: DresserCaptainFilled entities: - uid: 7289 @@ -53304,10 +53539,10 @@ entities: parent: 2 - proto: DresserChiefEngineerFilled entities: - - uid: 7290 + - uid: 3597 components: - type: Transform - pos: -14.5,-17.5 + pos: -6.5,-21.5 parent: 2 - proto: DresserChiefMedicalOfficerFilled entities: @@ -53388,6 +53623,18 @@ entities: - type: Transform pos: -44.868347,44.457825 parent: 2 +- proto: DrinkBeerglass + entities: + - uid: 12481 + components: + - type: Transform + pos: -0.7253418,-33.993866 + parent: 2 + - uid: 12545 + components: + - type: Transform + pos: -0.3972168,-34.22824 + parent: 2 - proto: DrinkBottleOfNothingFull entities: - uid: 7301 @@ -53955,6 +54202,18 @@ entities: rot: 1.5707963267948966 rad pos: -21.5,9.5 parent: 2 + - uid: 11417 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -26.5,-39.5 + parent: 2 + - uid: 11610 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -19.5,-38.5 + parent: 2 - proto: EmergencyOxygenTank entities: - uid: 901 @@ -54028,30 +54287,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 7405 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-18.5 - parent: 2 - - type: Fixtures - fixtures: {} - - uid: 7406 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-25.5 - parent: 2 - - type: Fixtures - fixtures: {} - - uid: 7407 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -19.5,-29.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 7408 components: - type: Transform @@ -54162,6 +54397,14 @@ entities: fixtures: {} - proto: FaxMachineBase entities: + - uid: 3444 + components: + - type: Transform + pos: -10.5,-23.5 + parent: 2 + - type: FaxMachine + name: Старший инженер + destinationAddress: Старший инженер - uid: 7422 components: - type: Transform @@ -54170,14 +54413,6 @@ entities: - type: FaxMachine name: Научный руководитель destinationAddress: Научный руководитель - - uid: 7423 - components: - - type: Transform - pos: -14.5,-23.5 - parent: 2 - - type: FaxMachine - name: Старший инженер - destinationAddress: Старший инженер - uid: 7424 components: - type: Transform @@ -54510,6 +54745,11 @@ entities: parent: 2 - proto: filingCabinetRandom entities: + - uid: 3643 + components: + - type: Transform + pos: -7.5,-23.5 + parent: 2 - uid: 7476 components: - type: Transform @@ -54520,11 +54760,6 @@ entities: - type: Transform pos: -33.5,16.5 parent: 2 - - uid: 7478 - components: - - type: Transform - pos: -12.5,-22.5 - parent: 2 - uid: 7479 components: - type: Transform @@ -54545,6 +54780,35 @@ entities: - type: Transform pos: 12.5,-1.5 parent: 2 +- proto: FireAlarm + entities: + - uid: 10044 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 24.5,-29.5 + parent: 2 + - type: DeviceList + devices: + - 13567 + - 13564 + - 7519 + - 7520 + - 98 + - type: Fixtures + fixtures: {} + - uid: 13565 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 17.5,-33.5 + parent: 2 + - type: DeviceList + devices: + - 13567 + - 13564 + - type: Fixtures + fixtures: {} - proto: FireAxe entities: - uid: 17923 @@ -54563,11 +54827,11 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 7484 + - uid: 8071 components: - type: Transform rot: -1.5707963267948966 rad - pos: -3.5,-26.5 + pos: -0.5,-30.5 parent: 2 - type: Fixtures fixtures: {} @@ -54636,15 +54900,6 @@ entities: rot: -1.5707963267948966 rad pos: -49.5,41.5 parent: 2 - - uid: 7493 - components: - - type: Transform - pos: 22.5,-29.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - - 99 - uid: 7494 components: - type: Transform @@ -54852,7 +55107,7 @@ entities: - type: DeviceNetwork deviceLists: - 97 - - 98 + - 10044 - uid: 7520 components: - type: Transform @@ -54861,16 +55116,7 @@ entities: - type: DeviceNetwork deviceLists: - 97 - - 98 - - uid: 7521 - components: - - type: Transform - pos: 23.5,-29.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - - 99 + - 10044 - uid: 7522 components: - type: Transform @@ -55386,17 +55632,11 @@ entities: - type: Transform pos: -26.5,-28.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - uid: 7595 components: - type: Transform pos: -24.5,-28.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - uid: 7596 components: - type: Transform @@ -55404,7 +55644,6 @@ entities: parent: 2 - type: DeviceNetwork deviceLists: - - 11 - 12 - uid: 7597 components: @@ -55439,7 +55678,6 @@ entities: - type: DeviceNetwork deviceLists: - 13 - - 14 - uid: 7601 components: - type: Transform @@ -55448,7 +55686,6 @@ entities: - type: DeviceNetwork deviceLists: - 13 - - 14 - uid: 7602 components: - type: Transform @@ -55465,16 +55702,6 @@ entities: - type: DeviceNetwork deviceLists: - 13 - - uid: 7604 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-21.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - 17 - uid: 7605 components: - type: Transform @@ -55483,16 +55710,7 @@ entities: parent: 2 - type: DeviceNetwork deviceLists: - - 15 - 6 - - uid: 7606 - components: - - type: Transform - pos: -7.5,-19.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 10 - uid: 7607 components: - type: Transform @@ -56110,7 +56328,6 @@ entities: - type: DeviceNetwork deviceLists: - 50 - - 99 - uid: 7677 components: - type: Transform @@ -56196,7 +56413,6 @@ entities: parent: 2 - type: DeviceNetwork deviceLists: - - 15 - 57 - uid: 7686 components: @@ -56206,7 +56422,6 @@ entities: parent: 2 - type: DeviceNetwork deviceLists: - - 15 - 57 - uid: 7687 components: @@ -56563,15 +56778,6 @@ entities: - type: DeviceNetwork deviceLists: - 88 - - uid: 7727 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -50.5,31.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 88 - uid: 7728 components: - type: Transform @@ -56772,17 +56978,11 @@ entities: - type: Transform pos: -18.5,-23.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - uid: 7754 components: - type: Transform pos: -16.5,-23.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - uid: 7755 components: - type: Transform @@ -56822,6 +57022,60 @@ entities: parent: 2 - proto: FirelockGlass entities: + - uid: 98 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 23.5,-35.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 10044 + - uid: 3292 + components: + - type: Transform + pos: -17.5,-16.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 + - uid: 3437 + components: + - type: Transform + pos: -11.5,-17.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 + - uid: 3441 + components: + - type: Transform + pos: -16.5,-16.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 + - uid: 3454 + components: + - type: Transform + pos: -11.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 170 + - uid: 3550 + components: + - type: Transform + pos: -11.5,-19.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 - uid: 7760 components: - type: Transform @@ -56847,46 +57101,6 @@ entities: - type: Transform pos: 5.5,9.5 parent: 2 - - uid: 7765 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-16.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - 15 - - uid: 7766 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-16.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - 15 - - uid: 7767 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-13.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - - 9 - - uid: 7768 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-15.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - - 9 - uid: 7769 components: - type: Transform @@ -56923,6 +57137,28 @@ entities: deviceLists: - 91 - 92 + - uid: 13564 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-29.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 13565 + - 1509 + - 10044 + - uid: 13567 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-31.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 13565 + - 1509 + - 10044 - proto: Fireplace entities: - uid: 7773 @@ -57078,6 +57314,13 @@ entities: - type: Transform pos: 56.511456,-6.1529155 parent: 2 +- proto: FloraTree + entities: + - uid: 13589 + components: + - type: Transform + pos: -4.8649893,-18.019133 + parent: 2 - proto: FloraTreeConifer entities: - uid: 7786 @@ -57085,6 +57328,13 @@ entities: - type: Transform pos: 56.481674,-11.007644 parent: 2 +- proto: FloraTreeLarge + entities: + - uid: 8261 + components: + - type: Transform + pos: -7.5,-17.5 + parent: 2 - proto: FloraTreeSnow entities: - uid: 7787 @@ -58074,6 +58324,11 @@ entities: - type: Transform pos: -29.530487,10.600556 parent: 2 + - uid: 11174 + components: + - type: Transform + pos: -13.5,-21.5 + parent: 2 - uid: 16963 components: - type: Transform @@ -58168,6 +58423,16 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: FoodFrozenPopsicleOrange + entities: + - uid: 15589 + components: + - type: Transform + parent: 15588 + - type: Physics + canCollide: False + - type: InsideEntityStorage + storage: 15588 - proto: FoodFrozenSnowcone entities: - uid: 7814 @@ -58175,6 +58440,16 @@ entities: - type: Transform pos: 56.6848,-11.972504 parent: 2 +- proto: FoodFrozenSundae + entities: + - uid: 15590 + components: + - type: Transform + parent: 15588 + - type: Physics + canCollide: False + - type: InsideEntityStorage + storage: 15588 - proto: FoodMealMemoryleek entities: - uid: 7815 @@ -58261,6 +58536,25 @@ entities: - type: Transform pos: 0.4270475,20.289186 parent: 2 +- proto: FoodPieFrostySlice + entities: + - uid: 15598 + components: + - type: Transform + pos: -18.49255,-32.444473 + parent: 2 +- proto: FoodPlateSmall + entities: + - uid: 14506 + components: + - type: Transform + pos: -18.492144,-32.413223 + parent: 2 + - uid: 14546 + components: + - type: Transform + pos: -18.507769,-33.288223 + parent: 2 - proto: FoodRicePudding entities: - uid: 7822 @@ -58321,18 +58615,6 @@ entities: canCollide: False - proto: GasCanisterBrokenBase entities: - - uid: 7836 - components: - - type: Transform - pos: -6.5,-30.5 - parent: 2 - - uid: 7837 - components: - - type: MetaData - name: разбитая канистра плазмы - - type: Transform - pos: -20.5,-42.5 - parent: 2 - uid: 18102 components: - type: Transform @@ -58375,11 +58657,43 @@ entities: parent: 16911 - proto: GasFilter entities: - - uid: 7838 + - uid: 8922 components: - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-28.5 + rot: 1.5707963267948966 rad + pos: -0.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13420 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -9.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13522 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13524 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13525 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-33.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -58397,93 +58711,113 @@ entities: rot: -1.5707963267948966 rad pos: -24.5,16.5 parent: 2 + - uid: 13561 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' +- proto: GasMinerCarbonDioxide + entities: + - uid: 9685 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-40.5 + parent: 2 - proto: GasMinerNitrogenStation entities: - - uid: 7841 - components: - - type: Transform - pos: -8.5,-32.5 - parent: 2 -- proto: GasMinerOxygenStation - entities: - - uid: 7842 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-32.5 - parent: 2 -- proto: GasMinerPlasma - entities: - - uid: 7843 - components: - - type: Transform - pos: 1.5,-32.5 - parent: 2 - - type: GasMiner - spawnAmount: 25 -- proto: GasOutletInjector - entities: - - uid: 7844 + - uid: 3096 components: - type: Transform rot: 3.141592653589793 rad - pos: -1.5,-31.5 + pos: -7.5,-40.5 parent: 2 - - uid: 7845 +- proto: GasMinerOxygenStation + entities: + - uid: 651 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,-40.5 + parent: 2 +- proto: GasMinerPlasma + entities: + - uid: 2839 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-37.5 + parent: 2 +- proto: GasMinerWaterVapor + entities: + - uid: 9832 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-40.5 + parent: 2 +- proto: GasOutletInjector + entities: + - uid: 3097 components: - type: Transform rot: 1.5707963267948966 rad - pos: -14.5,-32.5 + pos: -11.5,-40.5 + parent: 2 + - uid: 4028 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-37.5 + parent: 2 + - uid: 4035 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,-40.5 + parent: 2 + - uid: 7847 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-32.5 + parent: 2 + - uid: 9535 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-40.5 + parent: 2 + - uid: 9667 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9844 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-40.5 + parent: 2 +- proto: GasPassiveVent + entities: + - uid: 4036 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-36.5 parent: 2 - uid: 7846 components: - type: Transform rot: 1.5707963267948966 rad - pos: -12.5,-32.5 - parent: 2 - - uid: 7847 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -9.5,-32.5 - parent: 2 - - uid: 7848 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-32.5 - parent: 2 - - uid: 7849 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 0.5,-32.5 - parent: 2 -- proto: GasPassiveVent - entities: - - uid: 7850 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-31.5 - parent: 2 - - uid: 7851 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-32.5 - parent: 2 - - uid: 7852 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-31.5 - parent: 2 - - uid: 7853 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-32.5 + pos: -5.5,-32.5 parent: 2 - uid: 7854 components: @@ -58514,20 +58848,82 @@ entities: - type: Transform pos: -13.5,1.5 parent: 2 - - uid: 7859 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-31.5 - parent: 2 - uid: 7860 components: - type: Transform rot: 1.5707963267948966 rad pos: 58.5,-8.5 parent: 2 + - uid: 9671 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.5,-39.5 + parent: 2 + - uid: 9673 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-39.5 + parent: 2 + - uid: 9850 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-39.5 + parent: 2 + - uid: 9959 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-39.5 + parent: 2 - proto: GasPipeBend entities: + - uid: 3407 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3431 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4031 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -0.5,-40.5 + parent: 2 + - uid: 4034 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-40.5 + parent: 2 + - uid: 7232 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7767 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 7861 components: - type: Transform @@ -58595,12 +58991,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 7870 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.5,-26.5 - parent: 2 - uid: 7871 components: - type: Transform @@ -58633,30 +59023,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 7875 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -7.5,-32.5 - parent: 2 - - uid: 7876 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -10.5,-32.5 - parent: 2 - - uid: 7877 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-32.5 - parent: 2 - - uid: 7878 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-32.5 - parent: 2 - uid: 7879 components: - type: Transform @@ -58789,11 +59155,11 @@ entities: - uid: 7898 components: - type: Transform - rot: 3.141592653589793 rad - pos: -17.5,-15.5 + rot: -1.5707963267948966 rad + pos: -8.5,-19.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' + color: '#FF0000FF' - uid: 7899 components: - type: Transform @@ -58802,14 +59168,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 7900 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 7901 components: - type: Transform @@ -59217,12 +59575,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 7953 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-32.5 - parent: 2 - uid: 7954 components: - type: Transform @@ -59520,13 +59872,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 7993 - components: - - type: Transform - pos: -52.5,34.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 7994 components: - type: Transform @@ -59866,6 +60211,185 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' + - uid: 8227 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8260 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8389 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -23.5,-37.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 10009 + components: + - type: Transform + pos: -5.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 12755 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-40.5 + parent: 2 + - uid: 12947 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-37.5 + parent: 2 + - uid: 13077 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-40.5 + parent: 2 + - uid: 13523 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-33.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13528 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13593 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14020 + components: + - type: Transform + pos: -14.5,-30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14054 + components: + - type: Transform + pos: -53.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14221 + components: + - type: Transform + pos: -51.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14224 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -52.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14228 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -54.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14271 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -12.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14272 + components: + - type: Transform + pos: -12.5,-33.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16384 + components: + - type: Transform + pos: -22.5,-35.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16385 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 16386 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -21.5,-35.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 16387 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 16390 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -22.5,-37.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16391 + components: + - type: Transform + pos: -21.5,-37.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16392 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -21.5,-38.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPipeFourway entities: - uid: 8038 @@ -59901,13 +60425,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8043 - components: - - type: Transform - pos: -18.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8044 components: - type: Transform @@ -60041,8 +60558,489 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 8206 + components: + - type: Transform + pos: -18.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13722 + components: + - type: Transform + pos: -51.5,30.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - proto: GasPipeStraight entities: + - uid: 14 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 135 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-37.5 + parent: 2 + - uid: 158 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 183 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 647 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 649 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 676 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-39.5 + parent: 2 + - uid: 677 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-38.5 + parent: 2 + - uid: 996 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 997 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 1002 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-36.5 + parent: 2 + - uid: 1003 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,-40.5 + parent: 2 + - uid: 2706 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 2707 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 2708 + components: + - type: Transform + pos: -17.5,-20.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 2709 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 2710 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 2835 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-40.5 + parent: 2 + - uid: 3099 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-38.5 + parent: 2 + - uid: 3100 + components: + - type: Transform + pos: -15.5,-35.5 + parent: 2 + - uid: 3281 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3385 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3386 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3388 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3406 + components: + - type: Transform + pos: -5.5,-38.5 + parent: 2 + - uid: 3427 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3430 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -12.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3432 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3433 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3434 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3436 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3976 + components: + - type: Transform + pos: -2.5,-38.5 + parent: 2 + - uid: 4030 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-39.5 + parent: 2 + - uid: 4046 + components: + - type: Transform + pos: -8.5,-38.5 + parent: 2 + - uid: 4061 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 4062 + components: + - type: Transform + pos: -17.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4063 + components: + - type: Transform + pos: -17.5,-18.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4947 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 4950 + components: + - type: Transform + pos: -17.5,-22.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4951 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 4955 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-14.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4956 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4995 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-39.5 + parent: 2 + - uid: 4996 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-40.5 + parent: 2 + - uid: 6119 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -16.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 6123 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-22.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 6141 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 6437 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 6445 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 6538 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -12.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 6841 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 6999 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7000 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7002 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7003 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7005 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7009 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7012 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7116 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7117 + components: + - type: Transform + pos: -17.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7118 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7273 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7766 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 8063 components: - type: Transform @@ -60072,12 +61070,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8067 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-32.5 - parent: 2 - uid: 8068 components: - type: Transform @@ -60093,18 +61085,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8070 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-30.5 - parent: 2 - - uid: 8071 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-30.5 - parent: 2 - uid: 8072 components: - type: Transform @@ -60143,13 +61123,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8077 - components: - - type: Transform - pos: 22.5,-29.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8078 components: - type: Transform @@ -60188,14 +61161,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8083 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-23.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8084 components: - type: Transform @@ -60204,35 +61169,17 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8085 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-30.5 - parent: 2 - - uid: 8086 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-31.5 - parent: 2 - - uid: 8087 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-30.5 - parent: 2 - uid: 8088 components: - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-31.5 + rot: 1.5707963267948966 rad + pos: -4.5,-32.5 parent: 2 - uid: 8089 components: - type: Transform rot: 1.5707963267948966 rad - pos: -8.5,-32.5 + pos: -0.5,-29.5 parent: 2 - uid: 8090 components: @@ -60250,35 +61197,11 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8092 - components: - - type: Transform - pos: -16.5,-30.5 - parent: 2 - - uid: 8093 - components: - - type: Transform - pos: -16.5,-31.5 - parent: 2 - - uid: 8094 - components: - - type: Transform - pos: -18.5,-31.5 - parent: 2 - uid: 8095 components: - type: Transform - pos: -15.5,-31.5 - parent: 2 - - uid: 8096 - components: - - type: Transform - pos: -18.5,-30.5 - parent: 2 - - uid: 8097 - components: - - type: Transform - pos: -15.5,-30.5 + rot: 1.5707963267948966 rad + pos: -1.5,-29.5 parent: 2 - uid: 8098 components: @@ -60472,12 +61395,8 @@ entities: - uid: 8123 components: - type: Transform - pos: -13.5,-30.5 - parent: 2 - - uid: 8124 - components: - - type: Transform - pos: -13.5,-31.5 + rot: 1.5707963267948966 rad + pos: -8.5,-32.5 parent: 2 - uid: 8125 components: @@ -60814,20 +61733,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8169 - components: - - type: Transform - pos: -52.5,29.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8170 - components: - - type: Transform - pos: -52.5,30.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8171 components: - type: Transform @@ -60957,13 +61862,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8189 - components: - - type: Transform - pos: 23.5,-31.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8190 components: - type: Transform @@ -61021,156 +61919,7 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8198 - components: - - type: Transform - pos: -16.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8199 - components: - - type: Transform - pos: -16.5,-21.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8200 - components: - - type: Transform - pos: -16.5,-19.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8201 - components: - - type: Transform - pos: -18.5,-18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8202 - components: - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8203 - components: - - type: Transform - pos: -16.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8204 - components: - - type: Transform - pos: -18.5,-19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8205 - components: - - type: Transform - pos: -18.5,-17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8206 - components: - - type: Transform - pos: -18.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8207 - components: - - type: Transform - pos: -18.5,-14.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8208 - components: - - type: Transform - pos: -18.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8209 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -17.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8210 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8211 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8212 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8213 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8214 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -12.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8215 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8216 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -14.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8217 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8218 components: - type: Transform rot: 1.5707963267948966 rad @@ -61178,35 +61927,59 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 8202 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8203 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8204 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8214 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8217 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8218 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 8219 components: - type: Transform rot: 1.5707963267948966 rad - pos: -14.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8220 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -12.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8221 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8222 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-15.5 + pos: -16.5,-15.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' @@ -61238,14 +62011,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8227 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8228 components: - type: Transform @@ -61438,118 +62203,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8253 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8254 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8255 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8256 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8257 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8258 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8259 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-21.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8260 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-17.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8261 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-19.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8262 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8263 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-21.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8264 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-18.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8265 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-23.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8266 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-23.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8267 components: - type: Transform @@ -62472,10 +63125,8 @@ entities: components: - type: Transform rot: 1.5707963267948966 rad - pos: -22.5,-35.5 + pos: 0.5,-29.5 parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8388 components: - type: Transform @@ -62484,70 +63135,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8389 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-37.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8390 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -22.5,-37.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8391 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8392 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8393 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8394 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8395 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8396 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8397 components: - type: Transform @@ -62597,18 +63184,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8404 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-30.5 - parent: 2 - - uid: 8405 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-30.5 - parent: 2 - uid: 8406 components: - type: Transform @@ -65017,38 +65592,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8724 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -53.5,28.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8725 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,31.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8726 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,32.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8727 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,33.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8728 components: - type: Transform @@ -66496,17 +67039,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8922 - components: - - type: Transform - pos: 2.5,-31.5 - parent: 2 - - uid: 8923 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 1.5,-32.5 - parent: 2 - uid: 8924 components: - type: Transform @@ -68078,24 +68610,11 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9124 - components: - - type: Transform - pos: -52.5,31.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9125 - components: - - type: Transform - pos: -52.5,32.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9126 components: - type: Transform - pos: -52.5,33.5 + rot: -1.5707963267948966 rad + pos: 22.5,-31.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -69411,11 +69930,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9297 - components: - - type: Transform - pos: 2.5,-30.5 - parent: 2 - uid: 9298 components: - type: Transform @@ -70156,12 +70670,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9398 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-30.5 - parent: 2 - uid: 9399 components: - type: Transform @@ -70340,8 +70848,541 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' + - uid: 9444 + components: + - type: Transform + pos: 22.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9501 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -12.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9503 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9508 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -12.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9583 + components: + - type: Transform + pos: 23.5,-32.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9661 + components: + - type: Transform + pos: -11.5,-38.5 + parent: 2 + - uid: 10010 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10011 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10012 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10026 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10541 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 11829 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 12571 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-40.5 + parent: 2 + - uid: 12572 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-35.5 + parent: 2 + - uid: 12733 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 21.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 12734 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 18.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 12872 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-38.5 + parent: 2 + - uid: 12873 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-39.5 + parent: 2 + - uid: 12927 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-38.5 + parent: 2 + - uid: 13559 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -16.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13562 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-30.5 + parent: 2 + - uid: 13563 + components: + - type: Transform + pos: -18.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13566 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 18.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13581 + components: + - type: Transform + pos: -18.5,-28.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13594 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13623 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 21.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13655 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -51.5,31.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13714 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -51.5,32.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13715 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -51.5,33.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13716 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,33.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13717 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,32.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13718 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13719 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13720 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,29.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13723 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -50.5,30.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13755 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -52.5,28.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14018 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14019 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-28.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14086 + components: + - type: Transform + pos: -14.5,-32.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14087 + components: + - type: Transform + pos: -14.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14088 + components: + - type: Transform + pos: -12.5,-35.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14130 + components: + - type: Transform + pos: -12.5,-34.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14157 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14171 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14205 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14210 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14264 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14268 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14269 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14270 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14283 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14284 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14289 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14293 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14294 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14309 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14310 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14433 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 16388 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -22.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16389 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -22.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - proto: GasPipeTJunction entities: + - uid: 2711 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3424 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3440 + components: + - type: Transform + pos: -16.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4328 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8077 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 22.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8189 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 23.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8205 + components: + - type: Transform + pos: -10.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8213 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8215 + components: + - type: Transform + pos: -16.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8216 + components: + - type: Transform + pos: -8.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 9422 components: - type: Transform @@ -70358,18 +71399,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9424 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -5.5,-26.5 - parent: 2 - - uid: 9425 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-26.5 - parent: 2 - uid: 9426 components: - type: Transform @@ -70390,13 +71419,6 @@ entities: rot: -1.5707963267948966 rad pos: -26.5,17.5 parent: 2 - - uid: 9429 - components: - - type: Transform - pos: -51.5,30.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9430 components: - type: Transform @@ -70458,12 +71480,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9439 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -13.5,-26.5 - parent: 2 - uid: 9440 components: - type: Transform @@ -70495,14 +71511,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9444 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-31.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9445 components: - type: Transform @@ -70711,14 +71719,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9472 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -52.5,28.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9473 components: - type: Transform @@ -70859,14 +71859,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9491 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-21.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9492 components: - type: Transform @@ -70929,75 +71921,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9500 - components: - - type: Transform - pos: -8.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9501 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9502 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -18.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9503 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -18.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9504 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-23.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9505 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-18.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9506 - components: - - type: Transform - pos: -16.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9507 - components: - - type: Transform - pos: -16.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9508 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-14.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9509 components: - type: Transform @@ -71031,7 +71954,7 @@ entities: - uid: 9513 components: - type: Transform - pos: -6.5,-14.5 + pos: -13.5,-21.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -71187,18 +72110,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9534 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-32.5 - parent: 2 - - uid: 9535 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-31.5 - parent: 2 - uid: 9536 components: - type: Transform @@ -71457,14 +72368,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9569 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,30.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9570 components: - type: Transform @@ -71567,14 +72470,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9583 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 23.5,-32.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9584 components: - type: Transform @@ -72162,32 +73057,57 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 13591 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13592 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13721 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,28.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPort entities: - - uid: 9660 - components: - - type: Transform - pos: -5.5,-25.5 - parent: 2 - - uid: 9661 - components: - - type: Transform - pos: -4.5,-25.5 - parent: 2 - uid: 9662 components: - type: Transform pos: -24.5,-12.5 parent: 2 - - uid: 9663 + - uid: 10004 components: - type: Transform - pos: -13.5,-25.5 + rot: -1.5707963267948966 rad + pos: -14.5,-27.5 parent: 2 - - uid: 9664 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10051 components: - type: Transform - pos: -12.5,-25.5 + rot: -1.5707963267948966 rad + pos: -14.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14311 + components: + - type: Transform + pos: -17.5,-29.5 parent: 2 - uid: 18110 components: @@ -72197,58 +73117,55 @@ entities: parent: 16911 - proto: GasPressurePump entities: - - uid: 9665 - components: - - type: Transform - pos: 2.5,-29.5 - parent: 2 - - uid: 9666 + - uid: 5 components: - type: Transform rot: 3.141592653589793 rad - pos: -12.5,-29.5 + pos: -15.5,-34.5 parent: 2 - - uid: 9667 + - uid: 234 components: - type: Transform - pos: -13.5,-29.5 + pos: -9.5,-37.5 parent: 2 - - uid: 9668 + - uid: 3977 + components: + - type: Transform + pos: -13.5,-34.5 + parent: 2 + - uid: 4026 + components: + - type: Transform + pos: -6.5,-37.5 + parent: 2 + - uid: 6038 components: - type: Transform rot: 3.141592653589793 rad - pos: -9.5,-29.5 + pos: -5.5,-37.5 parent: 2 - - uid: 9669 + - uid: 6429 components: - type: Transform rot: 3.141592653589793 rad - pos: -15.5,-29.5 + pos: -2.5,-37.5 + parent: 2 + - uid: 7406 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-37.5 + parent: 2 + - uid: 7953 + components: + - type: Transform + pos: -3.5,-37.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9670 components: - type: Transform - pos: -7.5,-29.5 - parent: 2 - - uid: 9671 - components: - - type: Transform - pos: -10.5,-29.5 - parent: 2 - - uid: 9672 - components: - - type: Transform - pos: -16.5,-29.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9673 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-29.5 + rot: 1.5707963267948966 rad + pos: -9.5,-32.5 parent: 2 - uid: 9674 components: @@ -72260,12 +73177,6 @@ entities: - type: Transform pos: -22.5,-14.5 parent: 2 - - uid: 9676 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-26.5 - parent: 2 - uid: 9677 components: - type: MetaData @@ -72286,17 +73197,32 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9679 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-26.5 - parent: 2 - - uid: 9680 + - uid: 9992 components: - type: Transform rot: 3.141592653589793 rad - pos: 0.5,-29.5 + pos: -11.5,-37.5 + parent: 2 + - uid: 12552 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 12553 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13411 + components: + - type: Transform + pos: -0.5,-37.5 parent: 2 - proto: GasThermoMachineFreezer entities: @@ -72318,6 +73244,16 @@ entities: rot: -1.5707963267948966 rad pos: 22.5,22.5 parent: 2 + - uid: 11156 + components: + - type: Transform + pos: -1.5,-25.5 + parent: 2 + - uid: 11157 + components: + - type: Transform + pos: -1.5,-26.5 + parent: 2 - proto: GasThermoMachineFreezerEnabled entities: - uid: 9684 @@ -72330,13 +73266,64 @@ entities: targetTemperature: 73.15 - proto: GasThermoMachineHeater entities: - - uid: 9685 + - uid: 11155 components: - type: Transform - pos: -7.5,-25.5 + pos: -2.5,-25.5 parent: 2 + - uid: 11183 + components: + - type: Transform + pos: -2.5,-26.5 + parent: 2 +- proto: GasValve + entities: + - uid: 7837 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-32.5 + parent: 2 + - uid: 9668 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasVentPump entities: + - uid: 3293 + components: + - type: Transform + pos: -14.5,-18.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3438 + components: + - type: Transform + pos: -15.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4810 + components: + - type: Transform + pos: -7.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 170 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 9686 components: - type: Transform @@ -72458,17 +73445,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9699 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-22.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 10 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9700 components: - type: Transform @@ -72480,17 +73456,6 @@ entities: - 18 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9701 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-18.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9702 components: - type: Transform @@ -72566,42 +73531,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,-38.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9710 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -21.5,-37.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9711 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -18.5,-14.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9712 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-20.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 17 - type: AtmosPipeColor color: '#0000FFFF' - uid: 9713 @@ -72816,28 +73745,6 @@ entities: - 97 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9733 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 24.5,-25.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9734 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 23.5,-31.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 99 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9735 components: - type: Transform @@ -73351,13 +74258,6 @@ entities: - 71 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9785 - components: - - type: Transform - pos: -50.5,34.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9786 components: - type: Transform @@ -73695,13 +74595,86 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 9922 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 17.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - type: DeviceNetwork + deviceLists: + - 1509 + - uid: 15613 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -20.5,-35.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 18111 components: - type: Transform pos: 15.5,0.5 parent: 16911 + - uid: 19370 + components: + - type: Transform + pos: -52.5,35.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 19372 + - type: AtmosPipeColor + color: '#0000FFFF' - proto: GasVentScrubber entities: + - uid: 169 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 170 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 1507 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 17.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - type: DeviceNetwork + deviceLists: + - 1509 + - uid: 3291 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-18.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3443 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 9821 components: - type: Transform @@ -73720,14 +74693,6 @@ entities: - 66 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9823 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9824 components: - type: Transform @@ -73813,23 +74778,6 @@ entities: - 91 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9832 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-31.5 - parent: 2 - - uid: 9833 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-22.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 10 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9834 components: - type: Transform @@ -73841,17 +74789,6 @@ entities: - 18 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9835 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-20.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9836 components: - type: Transform @@ -73928,31 +74865,6 @@ entities: rot: -1.5707963267948966 rad pos: -25.5,-36.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9844 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -21.5,-35.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9845 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -16.5,-14.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - type: AtmosPipeColor color: '#FF0000FF' - uid: 9846 @@ -73985,18 +74897,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9849 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-32.5 - parent: 2 - - uid: 9850 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-32.5 - parent: 2 - uid: 9851 components: - type: Transform @@ -74144,28 +75044,6 @@ entities: - 96 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9865 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 23.5,-26.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9866 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-32.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 99 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9867 components: - type: Transform @@ -74720,14 +75598,6 @@ entities: - 71 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9922 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -53.5,34.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9923 components: - type: Transform @@ -75082,14 +75952,26 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' -- proto: GasVolumePump - entities: - - uid: 9959 + - uid: 15612 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -6.5,-26.5 + rot: -1.5707963267948966 rad + pos: -20.5,-38.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 19371 + components: + - type: Transform + pos: -54.5,35.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 19372 + - type: AtmosPipeColor + color: '#FF0000FF' +- proto: GasVolumePump + entities: - uid: 9960 components: - type: Transform @@ -75254,20 +76136,220 @@ entities: - type: Transform pos: -35.5,-31.5 parent: 2 -- proto: GrenadeStinger - entities: - - uid: 9973 - components: - - type: Transform - pos: -10.396269,-22.727524 - parent: 2 - - uid: 9974 - components: - - type: Transform - pos: -10.599394,-22.555649 - parent: 2 - proto: Grille entities: + - uid: 99 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-29.5 + parent: 2 + - uid: 419 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-30.5 + parent: 2 + - uid: 425 + components: + - type: Transform + pos: 3.5,-42.5 + parent: 2 + - uid: 456 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 16.5,-33.5 + parent: 2 + - uid: 759 + components: + - type: Transform + pos: -10.5,-38.5 + parent: 2 + - uid: 762 + components: + - type: Transform + pos: -6.5,-31.5 + parent: 2 + - uid: 866 + components: + - type: Transform + pos: -12.5,-43.5 + parent: 2 + - uid: 968 + components: + - type: Transform + pos: -7.5,-41.5 + parent: 2 + - uid: 978 + components: + - type: Transform + pos: -11.5,-41.5 + parent: 2 + - uid: 979 + components: + - type: Transform + pos: -7.5,-38.5 + parent: 2 + - uid: 988 + components: + - type: Transform + pos: -4.5,-38.5 + parent: 2 + - uid: 1001 + components: + - type: Transform + pos: -5.5,-31.5 + parent: 2 + - uid: 1226 + components: + - type: Transform + pos: -1.5,-42.5 + parent: 2 + - uid: 1274 + components: + - type: Transform + pos: 3.5,-43.5 + parent: 2 + - uid: 1275 + components: + - type: Transform + pos: -18.5,-43.5 + parent: 2 + - uid: 1276 + components: + - type: Transform + pos: -20.5,-47.5 + parent: 2 + - uid: 1279 + components: + - type: Transform + pos: -20.5,-45.5 + parent: 2 + - uid: 2818 + components: + - type: Transform + pos: -2.5,-41.5 + parent: 2 + - uid: 2841 + components: + - type: Transform + pos: -4.5,-32.5 + parent: 2 + - uid: 2842 + components: + - type: Transform + pos: -8.5,-41.5 + parent: 2 + - uid: 2845 + components: + - type: Transform + pos: -10.5,-41.5 + parent: 2 + - uid: 2863 + components: + - type: Transform + pos: -13.5,-37.5 + parent: 2 + - uid: 3089 + components: + - type: Transform + pos: -14.5,-35.5 + parent: 2 + - uid: 3103 + components: + - type: Transform + pos: -9.5,-42.5 + parent: 2 + - uid: 3104 + components: + - type: Transform + pos: -7.5,-42.5 + parent: 2 + - uid: 3418 + components: + - type: Transform + pos: -5.5,-42.5 + parent: 2 + - uid: 4011 + components: + - type: Transform + pos: -16.5,-43.5 + parent: 2 + - uid: 4027 + components: + - type: Transform + pos: -4.5,-41.5 + parent: 2 + - uid: 4038 + components: + - type: Transform + pos: -1.5,-38.5 + parent: 2 + - uid: 5744 + components: + - type: Transform + pos: -7.5,-33.5 + parent: 2 + - uid: 5745 + components: + - type: Transform + pos: -6.5,-33.5 + parent: 2 + - uid: 7004 + components: + - type: Transform + pos: -14.5,-43.5 + parent: 2 + - uid: 7407 + components: + - type: Transform + pos: -5.5,-33.5 + parent: 2 + - uid: 7768 + components: + - type: Transform + pos: -3.5,-42.5 + parent: 2 + - uid: 7870 + components: + - type: Transform + pos: -13.5,-36.5 + parent: 2 + - uid: 8390 + components: + - type: Transform + pos: -1.5,-41.5 + parent: 2 + - uid: 8923 + components: + - type: Transform + pos: -7.5,-31.5 + parent: 2 + - uid: 9733 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 18.5,-33.5 + parent: 2 + - uid: 9734 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 18.5,-27.5 + parent: 2 + - uid: 9865 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-31.5 + parent: 2 + - uid: 9866 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 16.5,-27.5 + parent: 2 - uid: 9975 components: - type: Transform @@ -75348,26 +76430,6 @@ entities: - type: Transform pos: 2.5,-10.5 parent: 2 - - uid: 9991 - components: - - type: Transform - pos: -12.5,-35.5 - parent: 2 - - uid: 9992 - components: - - type: Transform - pos: -1.5,-35.5 - parent: 2 - - uid: 9993 - components: - - type: Transform - pos: -10.5,-35.5 - parent: 2 - - uid: 9994 - components: - - type: Transform - pos: 0.5,-35.5 - parent: 2 - uid: 9995 components: - type: Transform @@ -75388,96 +76450,11 @@ entities: - type: Transform pos: 3.5,-35.5 parent: 2 - - uid: 9999 - components: - - type: Transform - pos: 2.5,-35.5 - parent: 2 - uid: 10000 components: - type: Transform pos: 16.5,-17.5 parent: 2 - - uid: 10001 - components: - - type: Transform - pos: 1.5,-35.5 - parent: 2 - - uid: 10002 - components: - - type: Transform - pos: -9.5,-35.5 - parent: 2 - - uid: 10003 - components: - - type: Transform - pos: -11.5,-35.5 - parent: 2 - - uid: 10004 - components: - - type: Transform - pos: -4.5,-35.5 - parent: 2 - - uid: 10005 - components: - - type: Transform - pos: -8.5,-35.5 - parent: 2 - - uid: 10006 - components: - - type: Transform - pos: -3.5,-35.5 - parent: 2 - - uid: 10007 - components: - - type: Transform - pos: -2.5,-35.5 - parent: 2 - - uid: 10008 - components: - - type: Transform - pos: -10.5,-20.5 - parent: 2 - - uid: 10009 - components: - - type: Transform - pos: -9.5,-20.5 - parent: 2 - - uid: 10010 - components: - - type: Transform - pos: -9.5,-17.5 - parent: 2 - - uid: 10011 - components: - - type: Transform - pos: -10.5,-17.5 - parent: 2 - - uid: 10012 - components: - - type: Transform - pos: -11.5,-18.5 - parent: 2 - - uid: 10013 - components: - - type: Transform - pos: -5.5,-17.5 - parent: 2 - - uid: 10014 - components: - - type: Transform - pos: -4.5,-17.5 - parent: 2 - - uid: 10015 - components: - - type: Transform - pos: -5.5,-20.5 - parent: 2 - - uid: 10016 - components: - - type: Transform - pos: -4.5,-20.5 - parent: 2 - uid: 10017 components: - type: Transform @@ -75498,11 +76475,6 @@ entities: - type: Transform pos: 14.5,-6.5 parent: 2 - - uid: 10021 - components: - - type: Transform - pos: -13.5,-35.5 - parent: 2 - uid: 10022 components: - type: Transform @@ -75517,17 +76489,7 @@ entities: - uid: 10024 components: - type: Transform - pos: -14.5,-35.5 - parent: 2 - - uid: 10025 - components: - - type: Transform - pos: -0.5,-35.5 - parent: 2 - - uid: 10026 - components: - - type: Transform - pos: -51.5,36.5 + pos: -5.5,-41.5 parent: 2 - uid: 10027 components: @@ -75586,24 +76548,6 @@ entities: rot: 3.141592653589793 rad pos: -26.5,-11.5 parent: 2 - - uid: 10037 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-14.5 - parent: 2 - - uid: 10038 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -13.5,-14.5 - parent: 2 - - uid: 10039 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -14.5,-14.5 - parent: 2 - uid: 10040 components: - type: Transform @@ -75628,11 +76572,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,-6.5 parent: 2 - - uid: 10044 - components: - - type: Transform - pos: -54.5,35.5 - parent: 2 - uid: 10045 components: - type: Transform @@ -75651,69 +76590,11 @@ entities: rot: 3.141592653589793 rad pos: -24.5,-4.5 parent: 2 - - uid: 10048 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-33.5 - parent: 2 - - uid: 10049 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-33.5 - parent: 2 - - uid: 10050 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -11.5,-33.5 - parent: 2 - - uid: 10051 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-33.5 - parent: 2 - uid: 10052 components: - type: Transform pos: -27.5,-36.5 parent: 2 - - uid: 10053 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -11.5,-30.5 - parent: 2 - - uid: 10054 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-30.5 - parent: 2 - - uid: 10055 - components: - - type: Transform - pos: -5.5,-35.5 - parent: 2 - - uid: 10056 - components: - - type: Transform - pos: -15.5,-35.5 - parent: 2 - - uid: 10057 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-33.5 - parent: 2 - - uid: 10058 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 1.5,-33.5 - parent: 2 - uid: 10059 components: - type: Transform @@ -75757,11 +76638,6 @@ entities: rot: -1.5707963267948966 rad pos: 11.5,-8.5 parent: 2 - - uid: 10067 - components: - - type: Transform - pos: -17.5,-37.5 - parent: 2 - uid: 10068 components: - type: Transform @@ -75833,16 +76709,6 @@ entities: - type: Transform pos: 2.5,-13.5 parent: 2 - - uid: 10082 - components: - - type: Transform - pos: -17.5,-36.5 - parent: 2 - - uid: 10083 - components: - - type: Transform - pos: -17.5,-38.5 - parent: 2 - uid: 10084 components: - type: Transform @@ -76009,26 +76875,6 @@ entities: - type: Transform pos: 21.5,-26.5 parent: 2 - - uid: 10117 - components: - - type: Transform - pos: 21.5,-27.5 - parent: 2 - - uid: 10118 - components: - - type: Transform - pos: 21.5,-32.5 - parent: 2 - - uid: 10119 - components: - - type: Transform - pos: 21.5,-31.5 - parent: 2 - - uid: 10120 - components: - - type: Transform - pos: 21.5,-33.5 - parent: 2 - uid: 10121 components: - type: Transform @@ -76136,11 +76982,6 @@ entities: - type: Transform pos: -27.5,-34.5 parent: 2 - - uid: 10141 - components: - - type: Transform - pos: -7.5,-35.5 - parent: 2 - uid: 10142 components: - type: Transform @@ -76151,11 +76992,6 @@ entities: - type: Transform pos: -27.5,-37.5 parent: 2 - - uid: 10144 - components: - - type: Transform - pos: -6.5,-35.5 - parent: 2 - uid: 10145 components: - type: Transform @@ -76336,11 +77172,6 @@ entities: - type: Transform pos: -35.5,-7.5 parent: 2 - - uid: 10181 - components: - - type: Transform - pos: -2.5,-30.5 - parent: 2 - uid: 10182 components: - type: Transform @@ -76546,11 +77377,6 @@ entities: rot: 1.5707963267948966 rad pos: 36.5,-3.5 parent: 2 - - uid: 10221 - components: - - type: Transform - pos: -17.5,-35.5 - parent: 2 - uid: 10222 components: - type: Transform @@ -76606,16 +77432,6 @@ entities: - type: Transform pos: 26.5,-8.5 parent: 2 - - uid: 10233 - components: - - type: Transform - pos: -17.5,-30.5 - parent: 2 - - uid: 10234 - components: - - type: Transform - pos: -14.5,-30.5 - parent: 2 - uid: 10235 components: - type: Transform @@ -76631,11 +77447,6 @@ entities: - type: Transform pos: -16.5,-24.5 parent: 2 - - uid: 10238 - components: - - type: Transform - pos: -16.5,-35.5 - parent: 2 - uid: 10239 components: - type: Transform @@ -77082,11 +77893,6 @@ entities: - type: Transform pos: 38.5,-22.5 parent: 2 - - uid: 10322 - components: - - type: Transform - pos: -11.5,-19.5 - parent: 2 - uid: 10323 components: - type: Transform @@ -77122,12 +77928,6 @@ entities: - type: Transform pos: 46.5,-4.5 parent: 2 - - uid: 10330 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 1.5,-30.5 - parent: 2 - uid: 10331 components: - type: Transform @@ -78199,28 +78999,14 @@ entities: - uid: 10540 components: - type: Transform - pos: -54.5,33.5 - parent: 2 - - uid: 10541 - components: - - type: Transform - pos: -53.5,36.5 - parent: 2 - - uid: 10542 - components: - - type: Transform - pos: -57.5,33.5 + rot: -1.5707963267948966 rad + pos: -53.5,37.5 parent: 2 - uid: 10543 components: - type: Transform pos: -52.5,39.5 parent: 2 - - uid: 10544 - components: - - type: Transform - pos: -51.5,39.5 - parent: 2 - uid: 10545 components: - type: Transform @@ -78567,11 +79353,6 @@ entities: - type: Transform pos: 4.5,-49.5 parent: 2 - - uid: 10613 - components: - - type: Transform - pos: 3.5,-44.5 - parent: 2 - uid: 10614 components: - type: Transform @@ -78582,11 +79363,6 @@ entities: - type: Transform pos: 18.5,-49.5 parent: 2 - - uid: 10616 - components: - - type: Transform - pos: 3.5,-42.5 - parent: 2 - uid: 10617 components: - type: Transform @@ -78704,11 +79480,6 @@ entities: - type: Transform pos: -44.5,47.5 parent: 2 - - uid: 10640 - components: - - type: Transform - pos: -19.5,-38.5 - parent: 2 - uid: 10641 components: - type: Transform @@ -78960,37 +79731,33 @@ entities: - type: Transform pos: 3.5,-21.5 parent: 2 - - uid: 10691 + - uid: 12732 components: - type: Transform rot: -1.5707963267948966 rad - pos: -2.5,-33.5 + pos: -51.5,35.5 parent: 2 - - uid: 10692 + - uid: 12735 + components: + - type: Transform + pos: -54.5,39.5 + parent: 2 + - uid: 14281 components: - type: Transform rot: -1.5707963267948966 rad - pos: -1.5,-33.5 + pos: 1.5,-36.5 parent: 2 - - uid: 10693 + - uid: 15322 components: - type: Transform - pos: -19.5,-34.5 + pos: -57.5,36.5 parent: 2 - - uid: 10694 + - uid: 15604 components: - type: Transform - pos: -19.5,-35.5 - parent: 2 - - uid: 10695 - components: - - type: Transform - pos: -19.5,-37.5 - parent: 2 - - uid: 10696 - components: - - type: Transform - pos: -19.5,-36.5 + rot: -1.5707963267948966 rad + pos: -55.5,35.5 parent: 2 - uid: 18119 components: @@ -79758,6 +80525,81 @@ entities: parent: 2 - proto: GrilleSpawner entities: + - uid: 1082 + components: + - type: Transform + pos: -15.5,-45.5 + parent: 2 + - uid: 1242 + components: + - type: Transform + pos: -18.5,-47.5 + parent: 2 + - uid: 1273 + components: + - type: Transform + pos: 0.5,-44.5 + parent: 2 + - uid: 1277 + components: + - type: Transform + pos: -17.5,-45.5 + parent: 2 + - uid: 7900 + components: + - type: Transform + pos: -7.5,-44.5 + parent: 2 + - uid: 8043 + components: + - type: Transform + pos: -12.5,-45.5 + parent: 2 + - uid: 8083 + components: + - type: Transform + pos: -11.5,-45.5 + parent: 2 + - uid: 8093 + components: + - type: Transform + pos: -13.5,-45.5 + parent: 2 + - uid: 8170 + components: + - type: Transform + pos: -1.5,-44.5 + parent: 2 + - uid: 8198 + components: + - type: Transform + pos: -4.5,-44.5 + parent: 2 + - uid: 8199 + components: + - type: Transform + pos: -5.5,-44.5 + parent: 2 + - uid: 8200 + components: + - type: Transform + pos: -0.5,-44.5 + parent: 2 + - uid: 8254 + components: + - type: Transform + pos: -3.5,-44.5 + parent: 2 + - uid: 8259 + components: + - type: Transform + pos: -9.5,-44.5 + parent: 2 + - uid: 10008 + components: + - type: Transform + pos: -16.5,-45.5 + parent: 2 - uid: 10728 components: - type: Transform @@ -79858,21 +80700,11 @@ entities: - type: Transform pos: -0.5,3.5 parent: 2 - - uid: 10748 - components: - - type: Transform - pos: -15.5,-49.5 - parent: 2 - uid: 10749 components: - type: Transform pos: -0.5,-0.5 parent: 2 - - uid: 10750 - components: - - type: Transform - pos: -16.5,-49.5 - parent: 2 - uid: 10751 components: - type: Transform @@ -80873,11 +81705,6 @@ entities: - type: Transform pos: 43.5,-46.5 parent: 2 - - uid: 10951 - components: - - type: Transform - pos: 2.5,-37.5 - parent: 2 - uid: 10952 components: - type: Transform @@ -80953,96 +81780,6 @@ entities: - type: Transform pos: 23.5,-47.5 parent: 2 - - uid: 10967 - components: - - type: Transform - pos: 1.5,-37.5 - parent: 2 - - uid: 10968 - components: - - type: Transform - pos: 0.5,-37.5 - parent: 2 - - uid: 10969 - components: - - type: Transform - pos: -1.5,-37.5 - parent: 2 - - uid: 10970 - components: - - type: Transform - pos: -3.5,-37.5 - parent: 2 - - uid: 10971 - components: - - type: Transform - pos: -2.5,-37.5 - parent: 2 - - uid: 10972 - components: - - type: Transform - pos: -13.5,-37.5 - parent: 2 - - uid: 10973 - components: - - type: Transform - pos: -5.5,-37.5 - parent: 2 - - uid: 10974 - components: - - type: Transform - pos: -7.5,-37.5 - parent: 2 - - uid: 10975 - components: - - type: Transform - pos: -6.5,-37.5 - parent: 2 - - uid: 10976 - components: - - type: Transform - pos: -9.5,-37.5 - parent: 2 - - uid: 10977 - components: - - type: Transform - pos: -11.5,-37.5 - parent: 2 - - uid: 10978 - components: - - type: Transform - pos: -10.5,-37.5 - parent: 2 - - uid: 10979 - components: - - type: Transform - pos: -14.5,-37.5 - parent: 2 - - uid: 10980 - components: - - type: Transform - pos: -14.5,-47.5 - parent: 2 - - uid: 10981 - components: - - type: Transform - pos: -14.5,-46.5 - parent: 2 - - uid: 10982 - components: - - type: Transform - pos: -14.5,-48.5 - parent: 2 - - uid: 10983 - components: - - type: Transform - pos: -17.5,-49.5 - parent: 2 - - uid: 10984 - components: - - type: Transform - pos: -19.5,-49.5 - parent: 2 - uid: 10985 components: - type: Transform @@ -81051,8 +81788,9 @@ entities: - uid: 10986 components: - type: Transform - pos: -20.5,-49.5 - parent: 2 + anchored: False + pos: -23.286892,-48.197464 + parent: 1 - uid: 10987 components: - type: Transform @@ -81880,36 +82618,26 @@ entities: - type: Transform pos: -32.5,-41.5 parent: 2 - - uid: 11152 + - uid: 11310 components: - type: Transform - pos: -14.5,-44.5 + pos: -18.5,-46.5 parent: 2 - - uid: 11153 + - uid: 11337 components: - type: Transform - pos: -14.5,-42.5 + pos: -18.5,-48.5 parent: 2 - - uid: 11154 + - uid: 13653 components: - type: Transform - pos: -14.5,-43.5 + pos: -8.5,-44.5 parent: 2 - - uid: 11155 + - uid: 14230 components: - type: Transform - pos: -14.5,-40.5 - parent: 2 - - uid: 11156 - components: - - type: Transform - pos: -14.5,-38.5 - parent: 2 - - uid: 11157 - components: - - type: Transform - pos: -14.5,-39.5 - parent: 2 + pos: -19.5,-49.5 + parent: 14229 - proto: GunSafe entities: - uid: 1149 @@ -81928,18 +82656,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -82103,20 +82821,20 @@ entities: parent: 16911 - proto: HighSecCommandLocked entities: + - uid: 193 + components: + - type: Transform + pos: -52.5,33.5 + parent: 2 - uid: 11173 components: - type: Transform pos: -35.5,-25.5 parent: 2 - - uid: 11174 + - uid: 13006 components: - type: Transform - pos: -7.5,-20.5 - parent: 2 - - uid: 11175 - components: - - type: Transform - pos: -7.5,-17.5 + pos: -52.5,31.5 parent: 2 - proto: HolopadCargoBay entities: @@ -82160,6 +82878,13 @@ entities: - type: Transform pos: 5.5,-7.5 parent: 2 +- proto: HolopadCommandVault + entities: + - uid: 4661 + components: + - type: Transform + pos: -53.5,35.5 + parent: 2 - proto: HolopadEngineeringAME entities: - uid: 11182 @@ -82167,26 +82892,26 @@ entities: - type: Transform pos: -23.5,-19.5 parent: 2 -- proto: HolopadEngineeringAtmosMain +- proto: HolopadEngineeringBreakroom entities: - - uid: 11183 + - uid: 12017 components: - type: Transform - pos: -15.5,-25.5 + pos: -14.5,-22.5 parent: 2 -- proto: HolopadEngineeringAtmosTeg +- proto: HolopadEngineeringFront entities: - - uid: 11184 + - uid: 12018 components: - type: Transform - pos: -2.5,-25.5 + pos: -15.5,-18.5 parent: 2 - proto: HolopadEngineeringMain entities: - - uid: 11185 + - uid: 11351 components: - type: Transform - pos: -23.5,-32.5 + pos: -22.5,-39.5 parent: 2 - proto: HolopadEngineeringTechVault entities: @@ -82223,6 +82948,13 @@ entities: - type: Transform pos: -73.5,17.5 parent: 2 +- proto: HolopadGeneralEVAStorage + entities: + - uid: 19362 + components: + - type: Transform + pos: 16.5,-30.5 + parent: 2 - proto: HolopadGeneralTheater entities: - uid: 11191 @@ -82571,13 +83303,6 @@ entities: - type: Transform pos: -2.492794,-20.447254 parent: 2 -- proto: IngotGold - entities: - - uid: 11247 - components: - - type: Transform - pos: -5.482785,-21.352592 - parent: 2 - proto: IngotGold1 entities: - uid: 6316 @@ -82669,16 +83394,6 @@ entities: - type: Transform pos: 1.5838315,-22.161379 parent: 2 -- proto: IntercomEngineering - entities: - - uid: 11255 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-16.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: IntercomMedical entities: - uid: 11256 @@ -82738,6 +83453,18 @@ entities: - type: Transform pos: 0.5,33.5 parent: 2 +- proto: JetpackMiniFilled + entities: + - uid: 19357 + components: + - type: Transform + pos: 15.811466,-29.435337 + parent: 2 + - uid: 19358 + components: + - type: Transform + pos: 15.311466,-29.450962 + parent: 2 - proto: Jug entities: - uid: 11263 @@ -82776,6 +83503,11 @@ entities: - type: Transform pos: 53.5,-12.5 parent: 2 + - uid: 12409 + components: + - type: Transform + pos: -12.5,-21.5 + parent: 2 - uid: 18233 components: - type: Transform @@ -82817,6 +83549,12 @@ entities: parent: 2 - proto: Lamp entities: + - uid: 1280 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.520041,-23.278364 + parent: 2 - uid: 11275 components: - type: Transform @@ -82824,12 +83562,6 @@ entities: parent: 2 - type: Physics canCollide: True - - uid: 11276 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.479099,-23.272646 - parent: 2 - uid: 11277 components: - type: Transform @@ -82955,30 +83687,6 @@ entities: - type: Transform pos: -27.420113,4.4986906 parent: 2 -- proto: LiquidNitrogenCanister - entities: - - uid: 11298 - components: - - type: Transform - pos: -5.5,-31.5 - parent: 2 - - uid: 11299 - components: - - type: Transform - pos: -8.5,-31.5 - parent: 2 -- proto: LiquidOxygenCanister - entities: - - uid: 11300 - components: - - type: Transform - pos: -5.5,-32.5 - parent: 2 - - uid: 11301 - components: - - type: Transform - pos: -11.5,-31.5 - parent: 2 - proto: LiveLetLiveCircuitBoard entities: - uid: 642 @@ -83010,6 +83718,101 @@ entities: fixtures: {} - proto: LockableButtonAtmospherics entities: + - uid: 2853 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-38.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 2851: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 2861 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-35.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 683: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 3085 + components: + - type: Transform + pos: -8.5,-33.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 19388: + - - Pressed + - Toggle + 19390: + - - Pressed + - Toggle + 19389: + - - Pressed + - Toggle + 19394: + - - Pressed + - Toggle + 19391: + - - Pressed + - Toggle + 19392: + - - Pressed + - Toggle + 19393: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 8087 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-38.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 685: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 10049 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-38.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 2862: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 10050 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-38.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 2836: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} - uid: 11303 components: - type: Transform @@ -83025,36 +83828,6 @@ entities: - Toggle - type: Fixtures fixtures: {} - - uid: 11304 - components: - - type: MetaData - desc: Эта кнопка открывает вход в цистерну и закрывает подачу газа. Не забудьте опустошить цистерну. - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-30.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 968: - - - Pressed - - Toggle - - type: Fixtures - fixtures: {} - - uid: 11305 - components: - - type: MetaData - desc: Эта кнопка открывает вход в цистерну и закрывает подачу газа. Не забудьте опустошить цистерну. - - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-30.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 967: - - - Pressed - - Toggle - - type: Fixtures - fixtures: {} - proto: LockableButtonBar entities: - uid: 11306 @@ -83144,48 +83917,6 @@ entities: - Toggle - type: Fixtures fixtures: {} -- proto: LockableButtonCommand - entities: - - uid: 11309 - components: - - type: MetaData - desc: Эта кнопка выключает режим тревоги. - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-24.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 12017: - - - Pressed - - Off - 12018: - - - Pressed - - Off - - type: Fixtures - fixtures: {} - - uid: 11310 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-17.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 994: - - - Pressed - - Toggle - 993: - - - Pressed - - Toggle - 997: - - - Pressed - - Toggle - 996: - - - Pressed - - Toggle - - type: Fixtures - fixtures: {} - proto: LockableButtonEngineering entities: - uid: 11311 @@ -83530,18 +84261,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -83628,10 +84349,10 @@ entities: parent: 2 - proto: LockerChiefEngineerFilled entities: - - uid: 11337 + - uid: 3450 components: - type: Transform - pos: -12.5,-17.5 + pos: -4.5,-23.5 parent: 2 - proto: LockerChiefMedicalOfficerFilled entities: @@ -83646,18 +84367,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -83682,18 +84393,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -83733,37 +84434,37 @@ entities: - type: Transform pos: 22.5,-43.5 parent: 2 -- proto: LockerEngineerFilled +- proto: LockerEngineerFilledHardsuit entities: - - uid: 11350 + - uid: 10141 components: - type: Transform - pos: -20.5,-35.5 + pos: -17.5,-37.5 parent: 2 - - uid: 11351 + - uid: 10144 components: - type: Transform - pos: -20.5,-37.5 + pos: -18.5,-37.5 parent: 2 - - uid: 11352 + - uid: 10181 components: - type: Transform - pos: -20.5,-36.5 + pos: -19.5,-37.5 parent: 2 - - uid: 11353 + - uid: 10221 components: - type: Transform - pos: -22.5,-36.5 + pos: -17.5,-35.5 parent: 2 - - uid: 11354 + - uid: 10233 components: - type: Transform - pos: -22.5,-37.5 + pos: -18.5,-35.5 parent: 2 - - uid: 11355 + - uid: 10234 components: - type: Transform - pos: -22.5,-35.5 + pos: -19.5,-35.5 parent: 2 - proto: LockerEvidence entities: @@ -83815,29 +84516,14 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: LockerFreezerVaultFilled entities: - - uid: 11364 + - uid: 15319 components: - type: Transform - pos: -6.5,-21.5 - parent: 2 - - uid: 11365 - components: - - type: Transform - pos: -8.5,-21.5 + pos: -52.5,36.5 parent: 2 - proto: LockerHeadOfPersonnelFilled entities: @@ -83852,18 +84538,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -83924,18 +84600,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84020,18 +84686,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - uid: 11384 components: - type: Transform @@ -84062,18 +84718,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84101,18 +84747,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84180,18 +84816,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84219,18 +84845,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84394,16 +85010,6 @@ entities: parent: 2 - proto: MachineFrame entities: - - uid: 11416 - components: - - type: Transform - pos: -9.5,-25.5 - parent: 2 - - uid: 11417 - components: - - type: Transform - pos: -8.5,-25.5 - parent: 2 - uid: 11418 components: - type: Transform @@ -84412,6 +85018,11 @@ entities: parent: 2 - proto: MachineFrameDestroyed entities: + - uid: 10692 + components: + - type: Transform + pos: -13.5,-39.5 + parent: 2 - uid: 18235 components: - type: Transform @@ -84424,10 +85035,10 @@ entities: parent: 16911 - proto: MachineMaterialSilo entities: - - uid: 11419 + - uid: 14161 components: - type: Transform - pos: -4.5,-22.5 + pos: -54.5,36.5 parent: 2 - proto: MagazineBoxPistol entities: @@ -84673,6 +85284,13 @@ entities: - type: Transform pos: -23.199554,64.70748 parent: 2 +- proto: MaterialBones1 + entities: + - uid: 15603 + components: + - type: Transform + pos: -18.445675,-33.381973 + parent: 2 - proto: MaterialCloth1 entities: - uid: 11448 @@ -84697,13 +85315,6 @@ entities: - type: Transform pos: -24.293915,64.147194 parent: 2 -- proto: MedalCase - entities: - - uid: 11452 - components: - - type: Transform - pos: -10.374162,-21.397001 - parent: 2 - proto: MedicalBed entities: - uid: 11453 @@ -85751,6 +86362,21 @@ entities: parent: 2 - proto: NitrogenCanister entities: + - uid: 2833 + components: + - type: Transform + pos: -5.5,-25.5 + parent: 2 + - uid: 5743 + components: + - type: Transform + pos: -7.5,-39.5 + parent: 2 + - uid: 7851 + components: + - type: Transform + pos: -5.5,-26.5 + parent: 2 - uid: 11491 components: - type: Transform @@ -85776,11 +86402,6 @@ entities: - type: Transform pos: -59.5,33.5 parent: 2 - - uid: 11496 - components: - - type: Transform - pos: -53.5,33.5 - parent: 2 - uid: 11497 components: - type: Transform @@ -85806,6 +86427,11 @@ entities: - type: Transform pos: -25.5,38.5 parent: 2 + - uid: 16719 + components: + - type: Transform + pos: 18.5,-30.5 + parent: 2 - proto: NitrousOxideCanister entities: - uid: 11502 @@ -85826,18 +86452,8 @@ entities: immutable: False temperature: 2.7 moles: - - 1310.1974 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 561.5131 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1310.1974 + NitrousOxide: 561.5131 - proto: NitrousOxideTankFilled entities: - uid: 11504 @@ -85921,7 +86537,8 @@ entities: - uid: 11513 components: - type: Transform - pos: -7.5,-23.5 + rot: 1.5707963267948966 rad + pos: -53.5,36.5 parent: 2 - proto: NuclearBombKeg entities: @@ -85985,6 +86602,16 @@ entities: parent: 2 - proto: OxygenCanister entities: + - uid: 2848 + components: + - type: Transform + pos: -6.5,-25.5 + parent: 2 + - uid: 5742 + components: + - type: Transform + pos: -10.5,-39.5 + parent: 2 - uid: 11519 components: - type: Transform @@ -86020,11 +86647,6 @@ entities: - type: Transform pos: -27.5,59.5 parent: 2 - - uid: 11526 - components: - - type: Transform - pos: -53.5,32.5 - parent: 2 - uid: 11527 components: - type: Transform @@ -86065,6 +86687,11 @@ entities: - type: Transform pos: 39.5,-42.5 parent: 2 + - uid: 16720 + components: + - type: Transform + pos: 17.5,-30.5 + parent: 2 - proto: PaladinCircuitBoard entities: - uid: 631 @@ -86076,6 +86703,18 @@ entities: - type: InsideEntityStorage - proto: Paper entities: + - uid: 4077 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.238791,-23.35649 + parent: 2 + - uid: 7423 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.457541,-23.54399 + parent: 2 - uid: 11535 components: - type: Transform @@ -86368,11 +87007,6 @@ entities: - type: Transform pos: 34.5,-4.5 parent: 2 - - uid: 11569 - components: - - type: Transform - pos: -14.5,-22.5 - parent: 2 - uid: 11570 components: - type: Transform @@ -86567,11 +87201,6 @@ entities: rot: 1.5707963267948966 rad pos: 28.331724,2.1021113 parent: 2 - - uid: 11601 - components: - - type: Transform - pos: -13.918377,-23.281319 - parent: 2 - uid: 11602 components: - type: Transform @@ -86611,6 +87240,12 @@ entities: parent: 2 - proto: PianoInstrument entities: + - uid: 8262 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-19.5 + parent: 2 - uid: 11608 components: - type: Transform @@ -86795,10 +87430,15 @@ entities: canCollide: False - proto: PlasmaCanister entities: - - uid: 11610 + - uid: 2826 components: - type: Transform - pos: -5.5,-30.5 + pos: -9.5,-25.5 + parent: 2 + - uid: 7844 + components: + - type: Transform + pos: -14.5,-36.5 parent: 2 - uid: 11611 components: @@ -86810,31 +87450,8 @@ entities: - type: Transform pos: -24.5,-12.5 parent: 2 - - uid: 11613 - components: - - type: Transform - pos: 1.5,-31.5 - parent: 2 - proto: PlasmaReinforcedWindowDirectional entities: - - uid: 11614 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-24.5 - parent: 2 - - uid: 11615 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -8.5,-23.5 - parent: 2 - - uid: 11616 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -6.5,-23.5 - parent: 2 - uid: 11617 components: - type: Transform @@ -87050,17 +87667,24 @@ entities: - type: Transform pos: 36.5,-30.5 parent: 2 +- proto: PortableGeneratorJrPacman + entities: + - uid: 19387 + components: + - type: Transform + pos: -15.5,-25.5 + parent: 2 - proto: PortableScrubber entities: - - uid: 11652 + - uid: 12201 components: - type: Transform - pos: -13.5,-25.5 + pos: -14.5,-27.5 parent: 2 - - uid: 11653 + - uid: 12202 components: - type: Transform - pos: -12.5,-25.5 + pos: -14.5,-26.5 parent: 2 - proto: PosterContrabandCommunistState entities: @@ -87372,6 +87996,26 @@ entities: ent: 6383 - proto: PottedPlantRandom entities: + - uid: 3538 + components: + - type: Transform + pos: -9.5,-21.5 + parent: 2 + - uid: 5982 + components: + - type: Transform + pos: 21.5,-28.5 + parent: 2 + - uid: 7010 + components: + - type: Transform + pos: -18.5,-20.5 + parent: 2 + - uid: 10038 + components: + - type: Transform + pos: -15.5,-23.5 + parent: 2 - uid: 11690 components: - type: Transform @@ -87472,6 +88116,11 @@ entities: - type: Transform pos: -9.5,31.5 parent: 2 + - uid: 19367 + components: + - type: Transform + pos: 21.5,-32.5 + parent: 2 - proto: PottedPlantRandomPlastic entities: - uid: 11710 @@ -87565,6 +88214,11 @@ entities: parent: 2 - proto: PowerCellRecharger entities: + - uid: 6998 + components: + - type: Transform + pos: -17.5,-16.5 + parent: 2 - uid: 11727 components: - type: Transform @@ -87601,6 +88255,11 @@ entities: - type: Transform pos: 21.5,47.5 parent: 2 + - uid: 12480 + components: + - type: Transform + pos: -0.5,-33.5 + parent: 2 - proto: PowerCellSmallPrinted entities: - uid: 11734 @@ -87637,6 +88296,87 @@ entities: parent: 2 - proto: Poweredlight entities: + - uid: 440 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-23.5 + parent: 2 + - uid: 680 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-35.5 + parent: 2 + - uid: 758 + components: + - type: Transform + pos: -12.5,-25.5 + parent: 2 + - uid: 969 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-32.5 + parent: 2 + - uid: 973 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-29.5 + parent: 2 + - uid: 2854 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-30.5 + parent: 2 + - uid: 8724 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-18.5 + parent: 2 + - uid: 9676 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-28.5 + parent: 2 + - uid: 9679 + components: + - type: Transform + pos: -4.5,-34.5 + parent: 2 + - uid: 9699 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-20.5 + parent: 2 + - uid: 11353 + components: + - type: Transform + pos: -21.5,-34.5 + parent: 2 + - uid: 11354 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-36.5 + parent: 2 + - uid: 11355 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -21.5,-40.5 + parent: 2 + - uid: 11416 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-40.5 + parent: 2 - uid: 11740 components: - type: Transform @@ -87671,12 +88411,6 @@ entities: - type: Transform pos: -23.5,-19.5 parent: 2 - - uid: 11746 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -14.5,-19.5 - parent: 2 - uid: 11747 components: - type: Transform @@ -87787,23 +88521,6 @@ entities: - type: Transform pos: -25.5,-29.5 parent: 2 - - uid: 11766 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-38.5 - parent: 2 - - uid: 11767 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -18.5,-28.5 - parent: 2 - - uid: 11768 - components: - - type: Transform - pos: -11.5,-25.5 - parent: 2 - uid: 11769 components: - type: Transform @@ -87921,29 +88638,11 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,-19.5 parent: 2 - - uid: 11789 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-22.5 - parent: 2 - uid: 11790 components: - type: Transform rot: 3.141592653589793 rad - pos: -17.5,-15.5 - parent: 2 - - uid: 11791 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-31.5 - parent: 2 - - uid: 11792 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-31.5 + pos: -14.5,-19.5 parent: 2 - uid: 11793 components: @@ -88062,11 +88761,6 @@ entities: - type: Transform pos: -9.5,26.5 parent: 2 - - uid: 11813 - components: - - type: Transform - pos: -19.5,-40.5 - parent: 2 - uid: 11814 components: - type: Transform @@ -88154,11 +88848,6 @@ entities: rot: 3.141592653589793 rad pos: -40.5,28.5 parent: 2 - - uid: 11829 - components: - - type: Transform - pos: -52.5,35.5 - parent: 2 - uid: 11830 components: - type: Transform @@ -88586,6 +89275,17 @@ entities: rot: 3.141592653589793 rad pos: 24.5,-13.5 parent: 2 + - uid: 13600 + components: + - type: Transform + pos: -14.5,-21.5 + parent: 2 + - uid: 13654 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,34.5 + parent: 2 - uid: 18341 components: - type: Transform @@ -88748,6 +89448,12 @@ entities: rot: 3.141592653589793 rad pos: -0.5,-5.5 parent: 16911 + - uid: 19361 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,-30.5 + parent: 2 - proto: PoweredlightEmpty entities: - uid: 11904 @@ -88910,6 +89616,47 @@ entities: parent: 16911 - proto: PoweredSmallLight entities: + - uid: 760 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-40.5 + parent: 2 + - uid: 763 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-40.5 + parent: 2 + - uid: 2850 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-37.5 + parent: 2 + - uid: 2852 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-40.5 + parent: 2 + - uid: 2856 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-40.5 + parent: 2 + - uid: 7765 + components: + - type: Transform + pos: -51.5,32.5 + parent: 2 + - uid: 9701 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-18.5 + parent: 2 - uid: 11927 components: - type: Transform @@ -88922,29 +89669,11 @@ entities: rot: -1.5707963267948966 rad pos: 24.5,28.5 parent: 2 - - uid: 11929 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-31.5 - parent: 2 - - uid: 11930 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-31.5 - parent: 2 - uid: 11931 components: - type: Transform pos: -41.5,37.5 parent: 2 - - uid: 11932 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -5.5,-31.5 - parent: 2 - uid: 11933 components: - type: Transform @@ -89349,18 +90078,6 @@ entities: rot: 1.5707963267948966 rad pos: 23.5,18.5 parent: 2 - - uid: 12003 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-31.5 - parent: 2 - - uid: 12004 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-31.5 - parent: 2 - uid: 12005 components: - type: Transform @@ -89482,22 +90199,6 @@ entities: ent: 11286 - type: ApcPowerReceiver powerLoad: 0 - - uid: 12017 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-23.5 - parent: 2 - - type: DeviceLinkSink - invokeCounter: 1 - - uid: 12018 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-16.5 - parent: 2 - - type: DeviceLinkSink - invokeCounter: 1 - proto: PresentRandom entities: - uid: 6022 @@ -90006,6 +90707,17 @@ entities: parent: 16911 - proto: Rack entities: + - uid: 650 + components: + - type: Transform + pos: -7.5,-21.5 + parent: 2 + - uid: 8393 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-17.5 + parent: 2 - uid: 12032 components: - type: Transform @@ -90017,21 +90729,6 @@ entities: - type: Transform pos: -21.5,6.5 parent: 2 - - uid: 12034 - components: - - type: Transform - pos: -14.5,-19.5 - parent: 2 - - uid: 12035 - components: - - type: Transform - pos: -10.5,-25.5 - parent: 2 - - uid: 12036 - components: - - type: Transform - pos: -11.5,-25.5 - parent: 2 - uid: 12037 components: - type: Transform @@ -90064,11 +90761,6 @@ entities: - type: Transform pos: -22.5,-7.5 parent: 2 - - uid: 12043 - components: - - type: Transform - pos: -14.5,-20.5 - parent: 2 - uid: 12044 components: - type: Transform @@ -90280,37 +90972,50 @@ entities: - type: Transform pos: -25.5,40.5 parent: 2 + - uid: 12204 + components: + - type: Transform + pos: -4.5,-25.5 + parent: 2 + - uid: 13287 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-17.5 + parent: 2 + - uid: 16529 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 15.5,-29.5 + parent: 2 + - uid: 16530 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 15.5,-31.5 + parent: 2 - proto: RadiationCollectorFullTank entities: - - uid: 12085 + - uid: 10616 components: - type: Transform - pos: -20.5,-45.5 + pos: -13.5,-41.5 parent: 2 - - uid: 12086 + - uid: 10640 components: - type: Transform - pos: -18.5,-44.5 + pos: -14.5,-41.5 parent: 2 - - uid: 12087 + - uid: 10693 components: - type: Transform - pos: -20.5,-44.5 + pos: -14.5,-39.5 parent: 2 - - uid: 12088 + - uid: 10694 components: - type: Transform - pos: -18.5,-45.5 - parent: 2 - - uid: 12089 - components: - - type: Transform - pos: -18.5,-43.5 - parent: 2 - - uid: 12090 - components: - - type: Transform - pos: -20.5,-43.5 + pos: -15.5,-39.5 parent: 2 - proto: RadioHandheld entities: @@ -90335,24 +91040,40 @@ entities: parent: 2 - proto: Railing entities: + - uid: 4327 + components: + - type: Transform + pos: -4.5,-16.5 + parent: 2 + - uid: 8392 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-19.5 + parent: 2 + - uid: 9472 + components: + - type: Transform + pos: -7.5,-16.5 + parent: 2 + - uid: 9505 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-18.5 + parent: 2 + - uid: 9507 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-17.5 + parent: 2 - uid: 12094 components: - type: Transform rot: -1.5707963267948966 rad pos: 24.5,26.5 parent: 2 - - uid: 12095 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-29.5 - parent: 2 - - uid: 12096 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -0.5,-29.5 - parent: 2 - uid: 12097 components: - type: Transform @@ -90694,42 +91415,6 @@ entities: rot: 3.141592653589793 rad pos: -35.5,-30.5 parent: 2 - - uid: 12155 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -9.5,-29.5 - parent: 2 - - uid: 12156 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-29.5 - parent: 2 - - uid: 12157 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-29.5 - parent: 2 - - uid: 12158 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-29.5 - parent: 2 - - uid: 12159 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-29.5 - parent: 2 - - uid: 12160 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -12.5,-29.5 - parent: 2 - uid: 12161 components: - type: Transform @@ -90808,6 +91493,42 @@ entities: rot: 3.141592653589793 rad pos: 39.5,22.5 parent: 2 + - uid: 12567 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -9.5,-37.5 + parent: 2 + - uid: 12775 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-37.5 + parent: 2 + - uid: 13088 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-37.5 + parent: 2 + - uid: 13408 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-37.5 + parent: 2 + - uid: 13409 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-37.5 + parent: 2 + - uid: 13410 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-37.5 + parent: 2 - uid: 18446 components: - type: Transform @@ -91595,17 +92316,35 @@ entities: parent: 16911 - proto: RailingCornerSmall entities: - - uid: 12188 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-28.5 - parent: 2 - - uid: 12189 + - uid: 437 components: - type: Transform rot: 1.5707963267948966 rad - pos: 0.5,-28.5 + pos: -2.5,-36.5 + parent: 2 + - uid: 2827 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-36.5 + parent: 2 + - uid: 4029 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-36.5 + parent: 2 + - uid: 4044 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-36.5 + parent: 2 + - uid: 9504 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-16.5 parent: 2 - uid: 12190 components: @@ -91669,42 +92408,6 @@ entities: rot: 1.5707963267948966 rad pos: 24.5,27.5 parent: 2 - - uid: 12201 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -16.5,-28.5 - parent: 2 - - uid: 12202 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -13.5,-28.5 - parent: 2 - - uid: 12203 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-28.5 - parent: 2 - - uid: 12204 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -9.5,-28.5 - parent: 2 - - uid: 12205 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -12.5,-28.5 - parent: 2 - - uid: 12206 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-28.5 - parent: 2 - uid: 12207 components: - type: Transform @@ -91733,6 +92436,24 @@ entities: rot: -1.5707963267948966 rad pos: 37.5,20.5 parent: 2 + - uid: 12408 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-16.5 + parent: 2 + - uid: 13412 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-36.5 + parent: 2 + - uid: 13413 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-36.5 + parent: 2 - uid: 18570 components: - type: Transform @@ -91966,6 +92687,11 @@ entities: parent: 2 - proto: RandomPosterLegit entities: + - uid: 8266 + components: + - type: Transform + pos: -14.5,-20.5 + parent: 2 - uid: 12245 components: - type: Transform @@ -92457,12 +93183,6 @@ entities: - type: Transform pos: -18.5,3.5 parent: 2 - - uid: 12333 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-39.5 - parent: 2 - uid: 12334 components: - type: Transform @@ -92474,12 +93194,6 @@ entities: rot: -1.5707963267948966 rad pos: -49.5,34.5 parent: 2 - - uid: 12336 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -54.5,34.5 - parent: 2 - uid: 12337 components: - type: Transform @@ -92566,6 +93280,21 @@ entities: rot: 3.141592653589793 rad pos: -27.5,7.5 parent: 2 + - uid: 19384 + components: + - type: Transform + pos: -4.5,-33.5 + parent: 2 + - uid: 19385 + components: + - type: Transform + pos: 5.5,-24.5 + parent: 2 + - uid: 19386 + components: + - type: Transform + pos: -6.5,-24.5 + parent: 2 - proto: RandomSpawner entities: - uid: 12352 @@ -92597,12 +93326,6 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-26.5 parent: 2 - - uid: 12357 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-20.5 - parent: 2 - uid: 12358 components: - type: Transform @@ -92615,18 +93338,6 @@ entities: rot: -1.5707963267948966 rad pos: 32.5,-18.5 parent: 2 - - uid: 12360 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -9.5,-23.5 - parent: 2 - - uid: 12361 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -5.5,-23.5 - parent: 2 - uid: 12362 components: - type: Transform @@ -93353,6 +94064,11 @@ entities: parent: 2 - proto: RandomVendingDrinks entities: + - uid: 8391 + components: + - type: Transform + pos: -18.5,-19.5 + parent: 2 - uid: 12366 components: - type: Transform @@ -93572,20 +94288,20 @@ entities: parent: 16911 - proto: RCDAmmo entities: - - uid: 12408 + - uid: 4949 components: - type: Transform - pos: -14.603725,-19.388529 + pos: -7.66646,-21.566923 parent: 2 - - uid: 12409 + - uid: 6537 components: - type: Transform - pos: -14.436941,-20.340593 + pos: -7.494585,-21.363798 parent: 2 - - uid: 12410 + - uid: 7290 components: - type: Transform - pos: -14.593191,-20.356218 + pos: -7.307085,-21.566923 parent: 2 - proto: Recycler entities: @@ -93659,6 +94375,138 @@ entities: parent: 19351 - proto: ReinforcedPlasmaWindow entities: + - uid: 2772 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-41.5 + parent: 2 + - uid: 2815 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-31.5 + parent: 2 + - uid: 2816 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-31.5 + parent: 2 + - uid: 2819 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-38.5 + parent: 2 + - uid: 2821 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-41.5 + parent: 2 + - uid: 2829 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-31.5 + parent: 2 + - uid: 2830 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-32.5 + parent: 2 + - uid: 7848 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-38.5 + parent: 2 + - uid: 7849 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-37.5 + parent: 2 + - uid: 7859 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-41.5 + parent: 2 + - uid: 7875 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-41.5 + parent: 2 + - uid: 7877 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-41.5 + parent: 2 + - uid: 8067 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-38.5 + parent: 2 + - uid: 9398 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-35.5 + parent: 2 + - uid: 9424 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-33.5 + parent: 2 + - uid: 9664 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-33.5 + parent: 2 + - uid: 9665 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-41.5 + parent: 2 + - uid: 9666 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-33.5 + parent: 2 + - uid: 9993 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-41.5 + parent: 2 + - uid: 10002 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-41.5 + parent: 2 + - uid: 10006 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-38.5 + parent: 2 + - uid: 10048 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-36.5 + parent: 2 - uid: 12413 components: - type: Transform @@ -93683,120 +94531,63 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-15.5 parent: 2 - - uid: 12417 - components: - - type: Transform - pos: -5.5,-24.5 - parent: 2 - - uid: 12418 - components: - - type: Transform - pos: -9.5,-24.5 - parent: 2 - - uid: 12419 - components: - - type: Transform - pos: -5.5,-20.5 - parent: 2 - - uid: 12420 - components: - - type: Transform - pos: -9.5,-20.5 - parent: 2 - - uid: 12421 - components: - - type: Transform - pos: -4.5,-20.5 - parent: 2 - - uid: 12422 - components: - - type: Transform - pos: -10.5,-20.5 - parent: 2 - - uid: 12423 - components: - - type: Transform - pos: -7.5,-24.5 - parent: 2 - - uid: 12473 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-33.5 - parent: 2 - - uid: 12474 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-33.5 - parent: 2 - - uid: 12475 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-30.5 - parent: 2 - - uid: 12476 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -12.5,-33.5 - parent: 2 - - uid: 12477 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-33.5 - parent: 2 - - uid: 12478 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-30.5 - parent: 2 - - uid: 12480 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -9.5,-33.5 - parent: 2 - - uid: 12481 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-33.5 - parent: 2 - - uid: 12571 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-30.5 - parent: 2 - - uid: 12572 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-30.5 - parent: 2 - - uid: 12755 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-30.5 - parent: 2 - - uid: 12775 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-30.5 - parent: 2 -- proto: ReinforcedWindow +- proto: ReinforcedUraniumWindow entities: - - uid: 12424 + - uid: 10013 components: - type: Transform rot: 3.141592653589793 rad - pos: -11.5,-19.5 + pos: -9.5,-24.5 + parent: 2 + - uid: 10014 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.5,-24.5 + parent: 2 + - uid: 10039 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -7.5,-24.5 + parent: 2 +- proto: ReinforcedWindow + entities: + - uid: 7493 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-29.5 + parent: 2 + - uid: 7521 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-30.5 + parent: 2 + - uid: 9663 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-36.5 + parent: 2 + - uid: 10118 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 18.5,-33.5 + parent: 2 + - uid: 10119 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-31.5 + parent: 2 + - uid: 10120 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 16.5,-27.5 parent: 2 - uid: 12425 components: @@ -93898,26 +94689,6 @@ entities: - type: Transform pos: -6.5,2.5 parent: 2 - - uid: 12445 - components: - - type: Transform - pos: -10.5,-17.5 - parent: 2 - - uid: 12446 - components: - - type: Transform - pos: -9.5,-17.5 - parent: 2 - - uid: 12447 - components: - - type: Transform - pos: -5.5,-17.5 - parent: 2 - - uid: 12448 - components: - - type: Transform - pos: -4.5,-17.5 - parent: 2 - uid: 12449 components: - type: Transform @@ -94286,16 +95057,6 @@ entities: rot: -1.5707963267948966 rad pos: 8.5,-17.5 parent: 2 - - uid: 12526 - components: - - type: Transform - pos: 21.5,-33.5 - parent: 2 - - uid: 12527 - components: - - type: Transform - pos: 21.5,-27.5 - parent: 2 - uid: 12528 components: - type: Transform @@ -94304,12 +95065,14 @@ entities: - uid: 12529 components: - type: Transform - pos: 21.5,-32.5 + rot: 1.5707963267948966 rad + pos: 18.5,-27.5 parent: 2 - uid: 12530 components: - type: Transform - pos: 21.5,-31.5 + rot: 1.5707963267948966 rad + pos: 16.5,-33.5 parent: 2 - uid: 12531 components: @@ -94376,26 +95139,11 @@ entities: - type: Transform pos: -4.5,-5.5 parent: 2 - - uid: 12544 - components: - - type: Transform - pos: -54.5,35.5 - parent: 2 - - uid: 12545 - components: - - type: Transform - pos: -19.5,-38.5 - parent: 2 - uid: 12546 components: - type: Transform pos: -33.5,-19.5 parent: 2 - - uid: 12547 - components: - - type: Transform - pos: -19.5,-36.5 - parent: 2 - uid: 12548 components: - type: Transform @@ -94416,16 +95164,6 @@ entities: - type: Transform pos: -37.5,-25.5 parent: 2 - - uid: 12552 - components: - - type: Transform - pos: -19.5,-34.5 - parent: 2 - - uid: 12553 - components: - - type: Transform - pos: -19.5,-35.5 - parent: 2 - uid: 12554 components: - type: Transform @@ -94494,11 +95232,6 @@ entities: - type: Transform pos: -39.5,-28.5 parent: 2 - - uid: 12567 - components: - - type: Transform - pos: -19.5,-37.5 - parent: 2 - uid: 12568 components: - type: Transform @@ -94946,11 +95679,6 @@ entities: - type: Transform pos: -30.5,19.5 parent: 2 - - uid: 12657 - components: - - type: Transform - pos: -11.5,-18.5 - parent: 2 - uid: 12658 components: - type: Transform @@ -95321,26 +96049,6 @@ entities: - type: Transform pos: 19.5,34.5 parent: 2 - - uid: 12732 - components: - - type: Transform - pos: -54.5,33.5 - parent: 2 - - uid: 12733 - components: - - type: Transform - pos: -53.5,36.5 - parent: 2 - - uid: 12734 - components: - - type: Transform - pos: -51.5,36.5 - parent: 2 - - uid: 12735 - components: - - type: Transform - pos: -51.5,39.5 - parent: 2 - uid: 12736 components: - type: Transform @@ -95349,7 +96057,7 @@ entities: - uid: 12737 components: - type: Transform - pos: -57.5,33.5 + pos: -57.5,36.5 parent: 2 - uid: 12738 components: @@ -95768,6 +96476,29 @@ entities: - type: Transform pos: -13.5,69.5 parent: 2 + - uid: 13624 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -51.5,35.5 + parent: 2 + - uid: 15320 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -55.5,35.5 + parent: 2 + - uid: 15328 + components: + - type: Transform + pos: -54.5,39.5 + parent: 2 + - uid: 15605 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -53.5,37.5 + parent: 2 - proto: RemoteSignaller entities: - uid: 12822 @@ -97138,6 +97869,16 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage + - uid: 12205 + components: + - type: Transform + pos: -4.5,-25.5 + parent: 2 + - uid: 12206 + components: + - type: Transform + pos: -4.5,-25.5 + parent: 2 - uid: 12869 components: - type: Transform @@ -97153,16 +97894,6 @@ entities: - type: Transform pos: 3.3614388,14.621397 parent: 2 - - uid: 12872 - components: - - type: Transform - pos: -10.5,-25.5 - parent: 2 - - uid: 12873 - components: - - type: Transform - pos: -11.5,-25.5 - parent: 2 - uid: 12874 components: - type: Transform @@ -97214,6 +97945,16 @@ entities: - type: Transform pos: -24.602211,64.518074 parent: 2 +- proto: ShelfBar + entities: + - uid: 14488 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.5,-32.5 + parent: 2 + - type: Fixtures + fixtures: {} - proto: ShelfRGlass entities: - uid: 1201 @@ -97578,6 +98319,21 @@ entities: parent: 2 - proto: ShuttersNormalOpen entities: + - uid: 3446 + components: + - type: Transform + pos: -5.5,-24.5 + parent: 2 + - uid: 3557 + components: + - type: Transform + pos: -7.5,-24.5 + parent: 2 + - uid: 3572 + components: + - type: Transform + pos: -9.5,-24.5 + parent: 2 - uid: 12904 components: - type: Transform @@ -97714,20 +98470,20 @@ entities: fixtures: {} - proto: SignalButton entities: - - uid: 12927 + - uid: 548 components: - - type: MetaData - desc: Эта кнопка переключает затворы камеры сгорания, ведущие в космос. - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-29.5 + pos: -5.5,-20.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 973: + 3446: - - Pressed - Toggle - 966: + 3557: + - - Pressed + - Toggle + 3572: - - Pressed - Toggle - type: Fixtures @@ -98018,15 +98774,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} -- proto: SignCans - entities: - - uid: 12947 - components: - - type: Transform - pos: -4.5,-30.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: SignCargoDock entities: - uid: 12948 @@ -98526,12 +99273,31 @@ entities: parent: 2 - type: Fixtures fixtures: {} -- proto: SignEVA +- proto: SignEngineering entities: - - uid: 13006 + - uid: 8207 components: - type: Transform - pos: -51.5,31.5 + rot: 3.141592653589793 rad + pos: -11.5,-18.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 8258 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-16.5 + parent: 2 + - type: Fixtures + fixtures: {} +- proto: SignEVA + entities: + - uid: 19363 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-30.5 parent: 2 - type: Fixtures fixtures: {} @@ -98555,6 +99321,14 @@ entities: fixtures: {} - proto: SignHead entities: + - uid: 2716 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-21.5 + parent: 2 + - type: Fixtures + fixtures: {} - uid: 13009 components: - type: Transform @@ -98712,6 +99486,16 @@ entities: parent: 2 - type: Fixtures fixtures: {} +- proto: SignRadiationMed + entities: + - uid: 10001 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-39.5 + parent: 2 + - type: Fixtures + fixtures: {} - proto: SignRedFive entities: - uid: 13027 @@ -98969,6 +99753,13 @@ entities: fixtures: {} - proto: SignVault entities: + - uid: 194 + components: + - type: Transform + pos: -51.5,31.5 + parent: 2 + - type: Fixtures + fixtures: {} - uid: 13056 components: - type: Transform @@ -98977,14 +99768,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 13057 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-17.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: SignXenobio entities: - uid: 13058 @@ -99082,23 +99865,19 @@ entities: - type: Transform pos: -33.5,9.5 parent: 2 + - type: Construction + containers: + - machine_parts + - machine_board + - smart_fridge_inventory + - entity_storage - type: EntityStorage air: volume: 200 temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 closeSound: !type:SoundPathSpecifier path: /Audio/Effects/closetclose.ogg openSound: !type:SoundPathSpecifier @@ -99112,6 +99891,14 @@ entities: - 7822 - 7820 - 7821 + machine_board: !type:Container + showEnts: False + occludes: True + ents: [] + machine_parts: !type:Container + showEnts: False + occludes: True + ents: [] - uid: 13071 components: - type: MetaData @@ -99151,63 +99938,68 @@ entities: - 16970 - 16971 - 16966 -- proto: SMESBasic + machine_board: !type:Container + showEnts: False + occludes: True + ents: [] + machine_parts: !type:Container + showEnts: False + occludes: True + ents: [] +- proto: SMESAdvanced entities: - - uid: 13073 - components: - - type: Transform - pos: -33.5,-20.5 - parent: 2 - uid: 13074 components: - type: Transform - pos: -25.5,-29.5 + pos: -25.5,-31.5 parent: 2 - uid: 13075 components: - type: Transform - pos: -25.5,-31.5 + pos: -25.5,-29.5 parent: 2 - uid: 13076 components: - type: Transform pos: -25.5,-30.5 parent: 2 - - uid: 13077 +- proto: SMESBasic + entities: + - uid: 598 components: - type: Transform - pos: -18.5,-40.5 + pos: -20.5,29.5 parent: 2 - - uid: 13078 + - uid: 4146 components: - type: Transform - pos: -26.5,-6.5 + pos: 31.5,-15.5 + parent: 2 + - uid: 4424 + components: + - type: Transform + pos: 26.5,18.5 + parent: 2 + - uid: 13073 + components: + - type: Transform + pos: -33.5,-20.5 parent: 2 - uid: 13079 components: - type: Transform pos: -7.5,48.5 parent: 2 - - uid: 13080 - components: - - type: Transform - pos: -20.5,28.5 - parent: 2 - uid: 13081 components: - type: Transform - pos: 27.5,18.5 + pos: -50.5,44.5 parent: 2 - uid: 13082 components: - type: Transform pos: 20.5,-23.5 parent: 2 - - uid: 13083 - components: - - type: Transform - pos: 32.5,-15.5 - parent: 2 - uid: 13084 components: - type: Transform @@ -99223,21 +100015,16 @@ entities: - type: Transform pos: 23.5,-41.5 parent: 2 - - uid: 13087 - components: - - type: Transform - pos: -51.5,44.5 - parent: 2 - - uid: 13088 - components: - - type: Transform - pos: -19.5,-40.5 - parent: 2 - uid: 18873 components: - type: Transform pos: 23.5,-9.5 parent: 16911 + - uid: 19526 + components: + - type: Transform + pos: -25.5,-7.5 + parent: 2 - proto: SMESBasicEmpty entities: - uid: 18874 @@ -100323,6 +101110,23 @@ entities: - type: Transform pos: 58.5,-10.5 parent: 2 +- proto: SpawnMobButterfly + entities: + - uid: 2695 + components: + - type: Transform + pos: -8.5,-15.5 + parent: 2 + - uid: 7008 + components: + - type: Transform + pos: -8.5,-19.5 + parent: 2 + - uid: 8220 + components: + - type: Transform + pos: -5.5,-19.5 + parent: 2 - proto: SpawnMobCat entities: - uid: 13283 @@ -100353,16 +101157,6 @@ entities: parent: 2 - proto: SpawnMobLizard entities: - - uid: 13287 - components: - - type: Transform - pos: -9.5,-22.5 - parent: 2 - - uid: 13288 - components: - - type: Transform - pos: -5.5,-22.5 - parent: 2 - uid: 13289 components: - type: Transform @@ -100561,15 +101355,20 @@ entities: parent: 2 - proto: SpawnPointChiefEngineer entities: - - uid: 13322 + - uid: 2694 components: - type: Transform - pos: -13.5,-22.5 + pos: -5.5,-22.5 parent: 2 - - uid: 13323 + - uid: 7604 components: - type: Transform - pos: -13.5,-17.5 + pos: -9.5,-22.5 + parent: 2 + - uid: 11768 + components: + - type: Transform + pos: -26.5,-30.5 parent: 2 - proto: SpawnPointChiefMedicalOfficer entities: @@ -101006,10 +101805,20 @@ entities: parent: 2 - proto: SpawnPointSeniorEngineer entities: - - uid: 13401 + - uid: 1284 components: - type: Transform - pos: -17.5,-21.5 + pos: -14.5,-23.5 + parent: 2 + - uid: 11766 + components: + - type: Transform + pos: -25.5,-39.5 + parent: 2 + - uid: 11767 + components: + - type: Transform + pos: -22.5,-35.5 parent: 2 - proto: SpawnPointSeniorOfficer entities: @@ -101051,36 +101860,36 @@ entities: parent: 2 - proto: SpawnPointStationEngineer entities: - - uid: 13408 + - uid: 7007 components: - type: Transform - pos: -21.5,-35.5 + pos: -13.5,-18.5 parent: 2 - - uid: 13409 + - uid: 7606 + components: + - type: Transform + pos: -17.5,-20.5 + parent: 2 + - uid: 10067 + components: + - type: Transform + pos: -19.5,-36.5 + parent: 2 + - uid: 10083 + components: + - type: Transform + pos: -18.5,-36.5 + parent: 2 + - uid: 11652 + components: + - type: Transform + pos: -23.5,-37.5 + parent: 2 + - uid: 11653 components: - type: Transform pos: -21.5,-36.5 parent: 2 - - uid: 13410 - components: - - type: Transform - pos: -21.5,-37.5 - parent: 2 - - uid: 13411 - components: - - type: Transform - pos: -25.5,-36.5 - parent: 2 - - uid: 13412 - components: - - type: Transform - pos: -25.5,-38.5 - parent: 2 - - uid: 13413 - components: - - type: Transform - pos: -25.5,-37.5 - parent: 2 - proto: SpawnPointSurgeon entities: - uid: 13414 @@ -101095,10 +101904,25 @@ entities: parent: 2 - proto: SpawnPointTechnicalAssistant entities: - - uid: 13416 + - uid: 15 components: - type: Transform - pos: -17.5,-19.5 + pos: -16.5,-22.5 + parent: 2 + - uid: 2696 + components: + - type: Transform + pos: -16.5,-17.5 + parent: 2 + - uid: 10082 + components: + - type: Transform + pos: -17.5,-36.5 + parent: 2 + - uid: 11613 + components: + - type: Transform + pos: -21.5,-38.5 parent: 2 - uid: 13417 components: @@ -101115,11 +101939,6 @@ entities: - type: Transform pos: -25.5,-25.5 parent: 2 - - uid: 13420 - components: - - type: Transform - pos: -21.5,-38.5 - parent: 2 - proto: SpawnPointWarden entities: - uid: 13421 @@ -101811,6 +102630,12 @@ entities: parent: 2 - proto: Stool entities: + - uid: 8263 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.62008,-19.378508 + parent: 2 - uid: 13500 components: - type: Transform @@ -101943,27 +102768,29 @@ entities: rot: 3.141592653589793 rad pos: -44.5,43.5 parent: 2 + - uid: 14453 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-33.5 + parent: 2 + - uid: 14470 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-32.5 + parent: 2 - proto: StorageCanister entities: - - uid: 13522 + - uid: 970 components: - type: Transform - pos: -5.5,-25.5 + pos: -7.5,-26.5 parent: 2 - - uid: 13523 + - uid: 9680 components: - type: Transform - pos: -4.5,-25.5 - parent: 2 - - uid: 13524 - components: - - type: Transform - pos: -6.5,-25.5 - parent: 2 - - uid: 13525 - components: - - type: Transform - pos: -6.5,-32.5 + pos: -7.5,-25.5 parent: 2 - uid: 13526 components: @@ -101975,11 +102802,6 @@ entities: - type: Transform pos: 28.5,-43.5 parent: 2 - - uid: 13528 - components: - - type: Transform - pos: -17.5,-31.5 - parent: 2 - uid: 13529 components: - type: Transform @@ -102000,6 +102822,11 @@ entities: - type: Transform pos: 34.5,19.5 parent: 2 + - uid: 14312 + components: + - type: Transform + pos: -17.5,-29.5 + parent: 2 - proto: StrangePill entities: - uid: 13533 @@ -102057,6 +102884,20 @@ entities: parent: 2 - proto: SubstationBasic entities: + - uid: 13078 + components: + - type: MetaData + name: подстанция блока снабжения + - type: Transform + pos: 27.5,18.5 + parent: 2 + - uid: 13083 + components: + - type: MetaData + name: подстанция медицинского блока + - type: Transform + pos: -20.5,28.5 + parent: 2 - uid: 13540 components: - type: MetaData @@ -102067,20 +102908,15 @@ entities: - uid: 13541 components: - type: MetaData - name: подстанция медицинского блока + name: северо-западная подстанция - type: Transform - pos: -20.5,29.5 + pos: -51.5,44.5 parent: 2 - uid: 13542 components: - type: Transform pos: -33.5,-24.5 parent: 2 - - uid: 13543 - components: - - type: Transform - pos: -4.5,-23.5 - parent: 2 - uid: 13544 components: - type: MetaData @@ -102095,13 +102931,6 @@ entities: - type: Transform pos: 20.5,-24.5 parent: 2 - - uid: 13546 - components: - - type: MetaData - name: подстанция охранного блока - - type: Transform - pos: 31.5,-15.5 - parent: 2 - uid: 13547 components: - type: MetaData @@ -102119,27 +102948,6 @@ entities: - type: Transform pos: 24.5,-41.5 parent: 2 - - uid: 13550 - components: - - type: MetaData - name: северо-западная подстанция - - type: Transform - pos: -50.5,44.5 - parent: 2 - - uid: 13551 - components: - - type: MetaData - name: подстанция научного блока - - type: Transform - pos: -25.5,-7.5 - parent: 2 - - uid: 13552 - components: - - type: MetaData - name: подстанция блока снабжения - - type: Transform - pos: 26.5,18.5 - parent: 2 - uid: 18915 components: - type: Transform @@ -102150,6 +102958,20 @@ entities: - type: Transform pos: 15.5,-9.5 parent: 16911 + - uid: 19523 + components: + - type: MetaData + name: подстанция охранного блока + - type: Transform + pos: 32.5,-15.5 + parent: 2 + - uid: 19527 + components: + - type: MetaData + name: подстанция научного блока + - type: Transform + pos: -26.5,-7.5 + parent: 2 - proto: SubstationBasicEmpty entities: - uid: 18917 @@ -102207,10 +103029,10 @@ entities: parent: 2 - proto: SuitStorageCE entities: - - uid: 13559 + - uid: 3453 components: - type: Transform - pos: -14.5,-18.5 + pos: -4.5,-21.5 parent: 2 - proto: SuitStorageCMO entities: @@ -102219,44 +103041,42 @@ entities: - type: Transform pos: -25.5,10.5 parent: 2 -- proto: SuitStorageEngi - entities: - - uid: 13561 - components: - - type: Transform - pos: -23.5,-37.5 - parent: 2 - - uid: 13562 - components: - - type: Transform - pos: -23.5,-35.5 - parent: 2 - - uid: 13563 - components: - - type: Transform - pos: -23.5,-36.5 - parent: 2 - proto: SuitStorageEVA entities: - - uid: 13564 + - uid: 12526 components: - type: Transform - pos: -51.5,35.5 + pos: 19.5,-30.5 parent: 2 - - uid: 13565 + - uid: 16527 components: - type: Transform - pos: -50.5,35.5 + pos: 19.5,-28.5 parent: 2 - - uid: 13566 + - uid: 16528 components: - type: Transform - pos: -52.5,35.5 + pos: 17.5,-28.5 parent: 2 - - uid: 13567 + - uid: 16531 components: - type: Transform - pos: -53.5,35.5 + pos: 18.5,-28.5 + parent: 2 + - uid: 16532 + components: + - type: Transform + pos: 19.5,-32.5 + parent: 2 + - uid: 16664 + components: + - type: Transform + pos: 18.5,-32.5 + parent: 2 + - uid: 16666 + components: + - type: Transform + pos: 17.5,-32.5 parent: 2 - uid: 18919 components: @@ -102346,6 +103166,11 @@ entities: - type: InsideEntityStorage - proto: SurveillanceCameraCommand entities: + - uid: 12417 + components: + - type: Transform + pos: 15.5,-32.5 + parent: 2 - uid: 13579 components: - type: Transform @@ -102362,25 +103187,6 @@ entities: - SurveillanceCameraCommand nameSet: True id: Специальное - Ядро ИИ - - uid: 13581 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -4.5,-22.5 - parent: 2 - - type: SurveillanceCamera - id: Хранилище - - uid: 13582 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,35.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraCommand - nameSet: True - id: Хранилище скафандров - uid: 13583 components: - type: Transform @@ -102413,6 +103219,12 @@ entities: - SurveillanceCameraCommand nameSet: True id: Мостик - Вход + - uid: 19369 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -52.5,36.5 + parent: 2 - proto: SurveillanceCameraConstructed entities: - uid: 13586 @@ -102429,6 +103241,35 @@ entities: id: Комната игр - proto: SurveillanceCameraEngineering entities: + - uid: 187 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-33.5 + parent: 2 + - uid: 8094 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-31.5 + parent: 2 + - uid: 9712 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-17.5 + parent: 2 + - uid: 11746 + components: + - type: Transform + pos: -14.5,-23.5 + parent: 2 + - uid: 12475 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -22.5,-34.5 + parent: 2 - uid: 13587 components: - type: Transform @@ -102448,17 +103289,6 @@ entities: - SurveillanceCameraEngineering nameSet: True id: Генератор Гравитации - - uid: 13589 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-20.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Общие - Вход - uid: 13590 components: - type: Transform @@ -102469,50 +103299,6 @@ entities: - SurveillanceCameraEngineering nameSet: True id: Специальное - Двигатель антиматерии - - uid: 13591 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-33.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Общие - Основное помещение - - uid: 13592 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-25.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Атмос - Основное помещение - - uid: 13593 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 1.5,-25.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Атмос - ТЭГ - - uid: 13594 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -22.5,-27.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Атмос - Гардероб - uid: 13595 components: - type: Transform @@ -102537,6 +103323,12 @@ entities: id: Специальное - Телекоммуникации - proto: SurveillanceCameraGeneral entities: + - uid: 11616 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-13.5 + parent: 2 - uid: 13597 components: - type: Transform @@ -102570,17 +103362,6 @@ entities: - SurveillanceCameraGeneral nameSet: True id: Общие - Столовая, Ю/З - - uid: 13600 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -4.5,-14.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraGeneral - nameSet: True - id: Общие - Коридор - uid: 13601 components: - type: Transform @@ -102819,27 +103600,6 @@ entities: - SurveillanceCameraGeneral nameSet: True id: Общие - Отбытие/Прибытие - - uid: 13623 - components: - - type: Transform - pos: -48.5,28.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraGeneral - nameSet: True - id: Общие - Коридор прибытия - - uid: 13624 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -45.5,34.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraGeneral - nameSet: True - id: Общие - Склад инструментов - uid: 13625 components: - type: Transform @@ -103087,34 +103847,6 @@ entities: parent: 2 - proto: SurveillanceCameraScience entities: - - uid: 13653 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -22.5,-12.5 - parent: 2 - - type: SurveillanceCamera - id: Ксеноархеология - - uid: 13654 - components: - - type: Transform - pos: -22.5,-10.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraScience - nameSet: True - id: Робототехника - - uid: 13655 - components: - - type: Transform - pos: -14.5,-11.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraScience - nameSet: True - id: Стойка - uid: 13656 components: - type: Transform @@ -103612,6 +104344,66 @@ entities: parent: 2 - proto: Table entities: + - uid: 3445 + components: + - type: Transform + pos: -10.5,-23.5 + parent: 2 + - uid: 3653 + components: + - type: Transform + pos: -8.5,-23.5 + parent: 2 + - uid: 3654 + components: + - type: Transform + pos: -9.5,-23.5 + parent: 2 + - uid: 7727 + components: + - type: Transform + pos: -53.5,34.5 + parent: 2 + - uid: 9660 + components: + - type: Transform + pos: 0.5,-34.5 + parent: 2 + - uid: 10974 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -22.5,-36.5 + parent: 2 + - uid: 10975 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -22.5,-37.5 + parent: 2 + - uid: 10976 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -22.5,-38.5 + parent: 2 + - uid: 12476 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-33.5 + parent: 2 + - uid: 12477 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-34.5 + parent: 2 + - uid: 13582 + components: + - type: Transform + pos: -54.5,34.5 + parent: 2 - uid: 13702 components: - type: Transform @@ -103675,56 +104467,6 @@ entities: - type: Transform pos: 49.5,-6.5 parent: 2 - - uid: 13714 - components: - - type: Transform - pos: -10.5,-21.5 - parent: 2 - - uid: 13715 - components: - - type: Transform - pos: -10.5,-22.5 - parent: 2 - - uid: 13716 - components: - - type: Transform - pos: -9.5,-21.5 - parent: 2 - - uid: 13717 - components: - - type: Transform - pos: -10.5,-23.5 - parent: 2 - - uid: 13718 - components: - - type: Transform - pos: -5.5,-21.5 - parent: 2 - - uid: 13719 - components: - - type: Transform - pos: -4.5,-21.5 - parent: 2 - - uid: 13720 - components: - - type: Transform - pos: -14.5,-23.5 - parent: 2 - - uid: 13721 - components: - - type: Transform - pos: -13.5,-23.5 - parent: 2 - - uid: 13722 - components: - - type: Transform - pos: -12.5,-23.5 - parent: 2 - - uid: 13723 - components: - - type: Transform - pos: -14.5,-22.5 - parent: 2 - uid: 13724 components: - type: Transform @@ -103889,11 +104631,6 @@ entities: - type: Transform pos: -70.5,36.5 parent: 2 - - uid: 13755 - components: - - type: Transform - pos: -12.5,-19.5 - parent: 2 - uid: 13756 components: - type: Transform @@ -104625,6 +105362,18 @@ entities: - type: Transform pos: 5.5,36.5 parent: 2 + - uid: 14317 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -18.5,-33.5 + parent: 2 + - uid: 14469 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -18.5,-32.5 + parent: 2 - proto: TableCounterMetal entities: - uid: 13891 @@ -104755,6 +105504,42 @@ entities: parent: 2 - proto: TableReinforced entities: + - uid: 678 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-26.5 + parent: 2 + - uid: 682 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-26.5 + parent: 2 + - uid: 7876 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-27.5 + parent: 2 + - uid: 9124 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-16.5 + parent: 2 + - uid: 10005 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-27.5 + parent: 2 + - uid: 13288 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-16.5 + parent: 2 - uid: 13912 components: - type: Transform @@ -105272,6 +106057,16 @@ entities: parent: 16911 - proto: TableWood entities: + - uid: 8265 + components: + - type: Transform + pos: -13.5,-21.5 + parent: 2 + - uid: 12418 + components: + - type: Transform + pos: -12.5,-21.5 + parent: 2 - uid: 14002 components: - type: Transform @@ -105390,29 +106185,26 @@ entities: parent: 2 - proto: TegCenter entities: - - uid: 14018 + - uid: 10613 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-25.5 + pos: 3.5,-28.5 parent: 2 - - type: ApcPowerReceiver - powerDisabled: True - proto: TegCirculator entities: - - uid: 14019 + - uid: 10238 components: - type: Transform rot: -1.5707963267948966 rad - pos: 0.5,-25.5 + pos: 3.5,-29.5 parent: 2 - type: PointLight color: '#FF3300FF' - - uid: 14020 + - uid: 10330 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-25.5 + rot: 1.5707963267948966 rad + pos: 3.5,-27.5 parent: 2 - type: PointLight color: '#FF3300FF' @@ -105711,6 +106503,11 @@ entities: parent: 2 - proto: ToolboxElectricalFilled entities: + - uid: 9506 + components: + - type: Transform + pos: -14.519136,-17.522202 + parent: 2 - uid: 11398 components: - type: Transform @@ -105753,6 +106550,11 @@ entities: - type: Transform pos: -31.45074,64.72638 parent: 2 + - uid: 16667 + components: + - type: Transform + pos: 15.497587,-31.575962 + parent: 2 - uid: 18961 components: - type: Transform @@ -105775,15 +106577,13 @@ entities: - type: Transform pos: 22.538084,4.227873 parent: 16911 -- proto: ToolboxGoldFilled - entities: - - uid: 14054 - components: - - type: Transform - pos: -9.521576,-21.399467 - parent: 2 - proto: ToolboxMechanicalFilled entities: + - uid: 10037 + components: + - type: Transform + pos: -14.503511,-17.225327 + parent: 2 - uid: 11399 components: - type: Transform @@ -105836,6 +106636,11 @@ entities: - type: Transform pos: -24.555336,64.236824 parent: 2 + - uid: 16718 + components: + - type: Transform + pos: 15.497587,-31.279087 + parent: 2 - uid: 18964 components: - type: Transform @@ -106001,87 +106806,6 @@ entities: parent: 2 - proto: TwoWayLever entities: - - uid: 14086 - components: - - type: MetaData - desc: Двухпозиционный рычаг. Правая позиция открывает вход в цистерну, левая - опустошает её. Не забудьте отключить насосы перед входом. - - type: Transform - pos: -14.5,-29.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 979: - - - Left - - Open - - - Middle - - Close - - - Right - - Close - 978: - - - Left - - Open - - - Right - - Close - - - Middle - - Close - 980: - - - Left - - Close - - - Right - - Open - - - Middle - - Close - - uid: 14087 - components: - - type: MetaData - desc: Двухпозиционный рычаг. Правая позиция открывает вход в цистерну, левая - опустошает её. Не забудьте отключить насосы перед входом. - - type: Transform - pos: -17.5,-29.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 969: - - - Left - - Open - - - Right - - Close - - - Middle - - Close - 970: - - - Left - - Open - - - Right - - Close - - - Middle - - Close - 981: - - - Left - - Close - - - Right - - Open - - - Middle - - Close - - uid: 14088 - components: - - type: Transform - pos: 1.5,-29.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 988: - - - Left - - Open - - - Right - - Close - - - Middle - - Close - 1003: - - - Right - - Close - - - Left - - Open - - - Middle - - Open - uid: 14089 components: - type: Transform @@ -106282,13 +107006,6 @@ entities: - Reverse - - Middle - Off - 16516: - - - Left - - Open - - - Right - - Open - - - Middle - - Close 12411: - - Left - Forward @@ -106656,13 +107373,18 @@ entities: parent: 2 - proto: VendingMachineEngiDrobe entities: - - uid: 14130 + - uid: 10984 components: - type: Transform - pos: -22.5,-38.5 + pos: -21.5,-34.5 parent: 2 - proto: VendingMachineEngivend entities: + - uid: 11255 + components: + - type: Transform + pos: -13.5,-19.5 + parent: 2 - uid: 14131 components: - type: Transform @@ -106826,6 +107548,11 @@ entities: parent: 2 - proto: VendingMachineTankDispenserEngineering entities: + - uid: 11352 + components: + - type: Transform + pos: -19.5,-38.5 + parent: 2 - uid: 14156 components: - type: Transform @@ -106833,11 +107560,6 @@ entities: parent: 2 - proto: VendingMachineTankDispenserEVA entities: - - uid: 14157 - components: - - type: Transform - pos: -23.5,-38.5 - parent: 2 - uid: 14158 components: - type: Transform @@ -106853,10 +107575,10 @@ entities: - type: Transform pos: -28.5,68.5 parent: 2 - - uid: 14161 + - uid: 16753 components: - type: Transform - pos: -51.5,32.5 + pos: 15.5,-30.5 parent: 2 - proto: VendingMachineTheater entities: @@ -106912,6 +107634,16 @@ entities: parent: 2 - proto: VendingMachineYouTool entities: + - uid: 11152 + components: + - type: Transform + pos: -22.5,-34.5 + parent: 2 + - uid: 11247 + components: + - type: Transform + pos: -15.5,-19.5 + parent: 2 - uid: 14169 components: - type: Transform @@ -106922,11 +107654,6 @@ entities: - type: Transform pos: -43.5,38.5 parent: 2 - - uid: 14171 - components: - - type: Transform - pos: -20.5,-38.5 - parent: 2 - proto: WallmountGeneratorAPUElectronics entities: - uid: 14172 @@ -106998,6 +107725,531 @@ entities: fixtures: {} - proto: WallReinforced entities: + - uid: 115 + components: + - type: Transform + pos: -3.5,-40.5 + parent: 2 + - uid: 438 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-16.5 + parent: 2 + - uid: 675 + components: + - type: Transform + pos: -4.5,-33.5 + parent: 2 + - uid: 855 + components: + - type: Transform + pos: -0.5,-28.5 + parent: 2 + - uid: 856 + components: + - type: Transform + pos: -0.5,-25.5 + parent: 2 + - uid: 857 + components: + - type: Transform + pos: 1.5,-33.5 + parent: 2 + - uid: 858 + components: + - type: Transform + pos: -0.5,-26.5 + parent: 2 + - uid: 859 + components: + - type: Transform + pos: 0.5,-31.5 + parent: 2 + - uid: 860 + components: + - type: Transform + pos: 0.5,-32.5 + parent: 2 + - uid: 861 + components: + - type: Transform + pos: -0.5,-30.5 + parent: 2 + - uid: 966 + components: + - type: Transform + pos: -16.5,-36.5 + parent: 2 + - uid: 967 + components: + - type: Transform + pos: -16.5,-37.5 + parent: 2 + - uid: 1506 + components: + - type: Transform + pos: -51.5,33.5 + parent: 2 + - uid: 1508 + components: + - type: Transform + pos: -51.5,36.5 + parent: 2 + - uid: 1510 + components: + - type: Transform + pos: -51.5,34.5 + parent: 2 + - uid: 2698 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-20.5 + parent: 2 + - uid: 2699 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-20.5 + parent: 2 + - uid: 2700 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-20.5 + parent: 2 + - uid: 2701 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-18.5 + parent: 2 + - uid: 2703 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-16.5 + parent: 2 + - uid: 2771 + components: + - type: Transform + pos: -18.5,-41.5 + parent: 2 + - uid: 2773 + components: + - type: Transform + pos: -20.5,-42.5 + parent: 2 + - uid: 2807 + components: + - type: Transform + pos: -18.5,-38.5 + parent: 2 + - uid: 2809 + components: + - type: Transform + pos: -16.5,-38.5 + parent: 2 + - uid: 2810 + components: + - type: Transform + pos: -0.5,-40.5 + parent: 2 + - uid: 2811 + components: + - type: Transform + pos: -17.5,-38.5 + parent: 2 + - uid: 2812 + components: + - type: Transform + pos: -13.5,-42.5 + parent: 2 + - uid: 2813 + components: + - type: Transform + pos: -12.5,-38.5 + parent: 2 + - uid: 2814 + components: + - type: Transform + pos: -17.5,-42.5 + parent: 2 + - uid: 2817 + components: + - type: Transform + pos: -14.5,-42.5 + parent: 2 + - uid: 2820 + components: + - type: Transform + pos: -0.5,-41.5 + parent: 2 + - uid: 2822 + components: + - type: Transform + pos: -18.5,-42.5 + parent: 2 + - uid: 2823 + components: + - type: Transform + pos: -19.5,-42.5 + parent: 2 + - uid: 2824 + components: + - type: Transform + pos: -18.5,-39.5 + parent: 2 + - uid: 2825 + components: + - type: Transform + pos: -12.5,-40.5 + parent: 2 + - uid: 2831 + components: + - type: Transform + pos: -8.5,-33.5 + parent: 2 + - uid: 2832 + components: + - type: Transform + pos: -0.5,-31.5 + parent: 2 + - uid: 2834 + components: + - type: Transform + pos: -0.5,-38.5 + parent: 2 + - uid: 2838 + components: + - type: Transform + pos: -9.5,-41.5 + parent: 2 + - uid: 3108 + components: + - type: Transform + pos: -14.5,-38.5 + parent: 2 + - uid: 4016 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-16.5 + parent: 2 + - uid: 4017 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -12.5,-16.5 + parent: 2 + - uid: 4020 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-16.5 + parent: 2 + - uid: 4032 + components: + - type: Transform + pos: 0.5,-33.5 + parent: 2 + - uid: 4033 + components: + - type: Transform + pos: 0.5,-38.5 + parent: 2 + - uid: 4037 + components: + - type: Transform + pos: -13.5,-35.5 + parent: 2 + - uid: 4039 + components: + - type: Transform + pos: -13.5,-38.5 + parent: 2 + - uid: 4075 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-23.5 + parent: 2 + - uid: 4321 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-20.5 + parent: 2 + - uid: 4324 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -12.5,-20.5 + parent: 2 + - uid: 4662 + components: + - type: Transform + pos: -55.5,34.5 + parent: 2 + - uid: 4663 + components: + - type: Transform + pos: -55.5,37.5 + parent: 2 + - uid: 4812 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -4.5,-20.5 + parent: 2 + - uid: 5116 + components: + - type: Transform + pos: -9.5,-38.5 + parent: 2 + - uid: 5336 + components: + - type: Transform + pos: -4.5,-31.5 + parent: 2 + - uid: 5741 + components: + - type: Transform + pos: -16.5,-34.5 + parent: 2 + - uid: 6037 + components: + - type: Transform + pos: -12.5,-41.5 + parent: 2 + - uid: 6447 + components: + - type: Transform + pos: -51.5,37.5 + parent: 2 + - uid: 7484 + components: + - type: Transform + pos: -8.5,-31.5 + parent: 2 + - uid: 7850 + components: + - type: Transform + pos: -6.5,-38.5 + parent: 2 + - uid: 7853 + components: + - type: Transform + pos: -3.5,-41.5 + parent: 2 + - uid: 7993 + components: + - type: Transform + pos: -53.5,33.5 + parent: 2 + - uid: 8257 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-21.5 + parent: 2 + - uid: 8404 + components: + - type: Transform + pos: -17.5,-34.5 + parent: 2 + - uid: 8405 + components: + - type: Transform + pos: -19.5,-34.5 + parent: 2 + - uid: 8725 + components: + - type: Transform + pos: -50.5,31.5 + parent: 2 + - uid: 8726 + components: + - type: Transform + pos: -55.5,36.5 + parent: 2 + - uid: 8727 + components: + - type: Transform + pos: -54.5,37.5 + parent: 2 + - uid: 9125 + components: + - type: Transform + pos: -54.5,33.5 + parent: 2 + - uid: 9297 + components: + - type: Transform + pos: -18.5,-34.5 + parent: 2 + - uid: 9425 + components: + - type: Transform + pos: -16.5,-35.5 + parent: 2 + - uid: 9439 + components: + - type: Transform + pos: -6.5,-41.5 + parent: 2 + - uid: 9500 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -7.5,-20.5 + parent: 2 + - uid: 9534 + components: + - type: Transform + pos: -3.5,-39.5 + parent: 2 + - uid: 9569 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-16.5 + parent: 2 + - uid: 9672 + components: + - type: Transform + pos: 2.5,-33.5 + parent: 2 + - uid: 9710 + components: + - type: Transform + pos: -12.5,-42.5 + parent: 2 + - uid: 9785 + components: + - type: Transform + pos: -52.5,37.5 + parent: 2 + - uid: 9974 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.5,-20.5 + parent: 2 + - uid: 9991 + components: + - type: Transform + pos: -6.5,-39.5 + parent: 2 + - uid: 9994 + components: + - type: Transform + pos: -6.5,-40.5 + parent: 2 + - uid: 10007 + components: + - type: Transform + pos: -15.5,-38.5 + parent: 2 + - uid: 10021 + components: + - type: Transform + pos: -3.5,-38.5 + parent: 2 + - uid: 10025 + components: + - type: Transform + pos: -9.5,-40.5 + parent: 2 + - uid: 10053 + components: + - type: Transform + pos: -12.5,-39.5 + parent: 2 + - uid: 10054 + components: + - type: Transform + pos: -9.5,-39.5 + parent: 2 + - uid: 10055 + components: + - type: Transform + pos: -0.5,-39.5 + parent: 2 + - uid: 10056 + components: + - type: Transform + pos: -16.5,-42.5 + parent: 2 + - uid: 10057 + components: + - type: Transform + pos: -15.5,-42.5 + parent: 2 + - uid: 10117 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,-27.5 + parent: 2 + - uid: 10542 + components: + - type: Transform + pos: -57.5,37.5 + parent: 2 + - uid: 10544 + components: + - type: Transform + pos: -57.5,33.5 + parent: 2 + - uid: 11364 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-20.5 + parent: 2 + - uid: 11365 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-20.5 + parent: 2 + - uid: 11419 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-20.5 + parent: 2 + - uid: 11452 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,-20.5 + parent: 2 + - uid: 12336 + components: + - type: Transform + pos: -55.5,33.5 + parent: 2 + - uid: 12527 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-28.5 + parent: 2 + - uid: 13401 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -19.5,-16.5 + parent: 2 + - uid: 13416 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -19.5,-15.5 + parent: 2 - uid: 14181 components: - type: Transform @@ -107119,11 +108371,6 @@ entities: - type: Transform pos: -23.5,-27.5 parent: 2 - - uid: 14205 - components: - - type: Transform - pos: -3.5,-29.5 - parent: 2 - uid: 14206 components: - type: Transform @@ -107148,11 +108395,6 @@ entities: rot: 3.141592653589793 rad pos: 2.5,-19.5 parent: 2 - - uid: 14210 - components: - - type: Transform - pos: -0.5,-30.5 - parent: 2 - uid: 14211 components: - type: Transform @@ -107208,11 +108450,6 @@ entities: - type: Transform pos: -19.5,-30.5 parent: 2 - - uid: 14221 - components: - - type: Transform - pos: -11.5,-17.5 - parent: 2 - uid: 14222 components: - type: Transform @@ -107223,11 +108460,6 @@ entities: - type: Transform pos: -1.5,-17.5 parent: 2 - - uid: 14224 - components: - - type: Transform - pos: -6.5,-17.5 - parent: 2 - uid: 14225 components: - type: Transform @@ -107245,26 +108477,6 @@ entities: rot: -1.5707963267948966 rad pos: -4.5,-6.5 parent: 2 - - uid: 14228 - components: - - type: Transform - pos: -11.5,-20.5 - parent: 2 - - uid: 14229 - components: - - type: Transform - pos: -11.5,-21.5 - parent: 2 - - uid: 14230 - components: - - type: Transform - pos: -11.5,-22.5 - parent: 2 - - uid: 14231 - components: - - type: Transform - pos: -11.5,-23.5 - parent: 2 - uid: 14232 components: - type: Transform @@ -107285,11 +108497,6 @@ entities: - type: Transform pos: -3.5,-22.5 parent: 2 - - uid: 14236 - components: - - type: Transform - pos: -6.5,-20.5 - parent: 2 - uid: 14237 components: - type: Transform @@ -107425,11 +108632,6 @@ entities: - type: Transform pos: -25.5,-24.5 parent: 2 - - uid: 14264 - components: - - type: Transform - pos: -13.5,-31.5 - parent: 2 - uid: 14265 components: - type: Transform @@ -107445,40 +108647,15 @@ entities: - type: Transform pos: -19.5,-24.5 parent: 2 - - uid: 14268 - components: - - type: Transform - pos: -10.5,-32.5 - parent: 2 - - uid: 14269 - components: - - type: Transform - pos: -10.5,-31.5 - parent: 2 - - uid: 14270 - components: - - type: Transform - pos: -7.5,-30.5 - parent: 2 - - uid: 14271 - components: - - type: Transform - pos: -4.5,-30.5 - parent: 2 - - uid: 14272 - components: - - type: Transform - pos: -4.5,-32.5 - parent: 2 - uid: 14273 components: - type: Transform - pos: -3.5,-28.5 + pos: 1.5,-34.5 parent: 2 - uid: 14274 components: - type: Transform - pos: -3.5,-30.5 + pos: 1.5,-38.5 parent: 2 - uid: 14275 components: @@ -107508,32 +108685,12 @@ entities: - uid: 14280 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-33.5 - parent: 2 - - uid: 14281 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -7.5,-33.5 + pos: 1.5,-37.5 parent: 2 - uid: 14282 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-33.5 - parent: 2 - - uid: 14283 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -10.5,-33.5 - parent: 2 - - uid: 14284 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-33.5 + pos: 1.5,-35.5 parent: 2 - uid: 14285 components: @@ -107557,11 +108714,6 @@ entities: - type: Transform pos: -6.5,4.5 parent: 2 - - uid: 14289 - components: - - type: Transform - pos: -16.5,-31.5 - parent: 2 - uid: 14290 components: - type: Transform @@ -107577,17 +108729,6 @@ entities: - type: Transform pos: -57.5,18.5 parent: 2 - - uid: 14293 - components: - - type: Transform - pos: -16.5,-30.5 - parent: 2 - - uid: 14294 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.5,-33.5 - parent: 2 - uid: 14295 components: - type: Transform @@ -107598,11 +108739,6 @@ entities: - type: Transform pos: -39.5,-39.5 parent: 2 - - uid: 14297 - components: - - type: Transform - pos: -15.5,-19.5 - parent: 2 - uid: 14298 components: - type: Transform @@ -107627,26 +108763,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,-24.5 parent: 2 - - uid: 14302 - components: - - type: Transform - pos: -15.5,-17.5 - parent: 2 - - uid: 14303 - components: - - type: Transform - pos: -15.5,-18.5 - parent: 2 - - uid: 14304 - components: - - type: Transform - pos: -13.5,-16.5 - parent: 2 - - uid: 14305 - components: - - type: Transform - pos: -12.5,-16.5 - parent: 2 - uid: 14306 components: - type: Transform @@ -107662,51 +108778,6 @@ entities: - type: Transform pos: 11.5,-13.5 parent: 2 - - uid: 14309 - components: - - type: Transform - pos: -3.5,-33.5 - parent: 2 - - uid: 14310 - components: - - type: Transform - pos: -13.5,-32.5 - parent: 2 - - uid: 14311 - components: - - type: Transform - pos: -13.5,-30.5 - parent: 2 - - uid: 14312 - components: - - type: Transform - pos: -10.5,-30.5 - parent: 2 - - uid: 14313 - components: - - type: Transform - pos: -7.5,-31.5 - parent: 2 - - uid: 14314 - components: - - type: Transform - pos: -7.5,-32.5 - parent: 2 - - uid: 14315 - components: - - type: Transform - pos: -4.5,-31.5 - parent: 2 - - uid: 14316 - components: - - type: Transform - pos: -3.5,-25.5 - parent: 2 - - uid: 14317 - components: - - type: Transform - pos: -3.5,-26.5 - parent: 2 - uid: 14318 components: - type: Transform @@ -107745,18 +108816,6 @@ entities: rot: 1.5707963267948966 rad pos: 5.5,-21.5 parent: 2 - - uid: 14325 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-22.5 - parent: 2 - - uid: 14326 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-23.5 - parent: 2 - uid: 14327 components: - type: Transform @@ -108165,7 +109224,8 @@ entities: - uid: 14405 components: - type: Transform - pos: 21.5,-28.5 + rot: 1.5707963267948966 rad + pos: 20.5,-32.5 parent: 2 - uid: 14406 components: @@ -108205,7 +109265,8 @@ entities: - uid: 14413 components: - type: Transform - pos: 21.5,-29.5 + rot: 1.5707963267948966 rad + pos: 21.5,-33.5 parent: 2 - uid: 14414 components: @@ -108262,11 +109323,6 @@ entities: - type: Transform pos: -2.5,6.5 parent: 2 - - uid: 14425 - components: - - type: Transform - pos: -50.5,36.5 - parent: 2 - uid: 14426 components: - type: Transform @@ -108302,11 +109358,6 @@ entities: - type: Transform pos: 33.5,-3.5 parent: 2 - - uid: 14433 - components: - - type: Transform - pos: -52.5,36.5 - parent: 2 - uid: 14434 components: - type: Transform @@ -108402,11 +109453,6 @@ entities: - type: Transform pos: -31.5,-33.5 parent: 2 - - uid: 14453 - components: - - type: Transform - pos: -21.5,-39.5 - parent: 2 - uid: 14454 components: - type: Transform @@ -108486,18 +109532,6 @@ entities: - type: Transform pos: 45.5,-22.5 parent: 2 - - uid: 14469 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 2.5,-33.5 - parent: 2 - - uid: 14470 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-31.5 - parent: 2 - uid: 14471 components: - type: Transform @@ -108584,12 +109618,6 @@ entities: - type: Transform pos: -32.5,-30.5 parent: 2 - - uid: 14488 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-32.5 - parent: 2 - uid: 14489 components: - type: Transform @@ -108678,12 +109706,6 @@ entities: - type: Transform pos: -35.5,-34.5 parent: 2 - - uid: 14506 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-32.5 - parent: 2 - uid: 14507 components: - type: Transform @@ -108880,16 +109902,6 @@ entities: - type: Transform pos: 48.5,-20.5 parent: 2 - - uid: 14546 - components: - - type: Transform - pos: -17.5,-44.5 - parent: 2 - - uid: 14547 - components: - - type: Transform - pos: -17.5,-43.5 - parent: 2 - uid: 14548 components: - type: Transform @@ -110024,11 +111036,6 @@ entities: rot: 1.5707963267948966 rad pos: 14.5,-0.5 parent: 2 - - uid: 14768 - components: - - type: Transform - pos: -15.5,-20.5 - parent: 2 - uid: 14769 components: - type: Transform @@ -111185,7 +112192,8 @@ entities: - uid: 14990 components: - type: Transform - pos: 21.5,-30.5 + rot: 1.5707963267948966 rad + pos: 20.5,-30.5 parent: 2 - uid: 14991 components: @@ -112905,30 +113913,10 @@ entities: - type: Transform pos: -16.5,52.5 parent: 2 - - uid: 15318 - components: - - type: Transform - pos: -54.5,36.5 - parent: 2 - - uid: 15319 - components: - - type: Transform - pos: -54.5,32.5 - parent: 2 - - uid: 15320 - components: - - type: Transform - pos: -54.5,34.5 - parent: 2 - uid: 15321 components: - type: Transform - pos: -54.5,39.5 - parent: 2 - - uid: 15322 - components: - - type: Transform - pos: -57.5,37.5 + pos: -51.5,39.5 parent: 2 - uid: 15323 components: @@ -112955,11 +113943,6 @@ entities: - type: Transform pos: -56.5,39.5 parent: 2 - - uid: 15328 - components: - - type: Transform - pos: -57.5,36.5 - parent: 2 - uid: 15329 components: - type: Transform @@ -113459,11 +114442,6 @@ entities: - type: Transform pos: -31.5,-0.5 parent: 2 - - uid: 15428 - components: - - type: Transform - pos: -0.5,-33.5 - parent: 2 - uid: 15429 components: - type: Transform @@ -113484,11 +114462,6 @@ entities: - type: Transform pos: -57.5,14.5 parent: 2 - - uid: 15433 - components: - - type: Transform - pos: -19.5,-39.5 - parent: 2 - uid: 15434 components: - type: Transform @@ -113499,26 +114472,11 @@ entities: - type: Transform pos: -26.5,-8.5 parent: 2 - - uid: 15436 - components: - - type: Transform - pos: -26.5,-7.5 - parent: 2 - uid: 15437 components: - type: Transform pos: 40.5,15.5 parent: 2 - - uid: 15438 - components: - - type: Transform - pos: -5.5,-33.5 - parent: 2 - - uid: 15439 - components: - - type: Transform - pos: -6.5,-33.5 - parent: 2 - uid: 15440 components: - type: Transform @@ -113584,11 +114542,6 @@ entities: - type: Transform pos: -59.5,21.5 parent: 2 - - uid: 15453 - components: - - type: Transform - pos: 2.5,-30.5 - parent: 2 - uid: 15454 components: - type: Transform @@ -113918,11 +114871,6 @@ entities: - type: Transform pos: 51.5,-3.5 parent: 2 - - uid: 15519 - components: - - type: Transform - pos: -17.5,-45.5 - parent: 2 - uid: 15520 components: - type: Transform @@ -114285,23 +115233,6 @@ entities: - type: Transform pos: 51.5,-1.5 parent: 2 - - uid: 15588 - components: - - type: Transform - pos: -19.5,-46.5 - parent: 2 - - uid: 15589 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-31.5 - parent: 2 - - uid: 15590 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-32.5 - parent: 2 - uid: 15591 components: - type: Transform @@ -114337,11 +115268,6 @@ entities: - type: Transform pos: 38.5,-14.5 parent: 2 - - uid: 15598 - components: - - type: Transform - pos: -17.5,-42.5 - parent: 2 - uid: 15599 components: - type: Transform @@ -114362,21 +115288,6 @@ entities: - type: Transform pos: 44.5,-19.5 parent: 2 - - uid: 15603 - components: - - type: Transform - pos: -17.5,-46.5 - parent: 2 - - uid: 15604 - components: - - type: Transform - pos: -18.5,-46.5 - parent: 2 - - uid: 15605 - components: - - type: Transform - pos: -20.5,-46.5 - parent: 2 - uid: 15606 components: - type: Transform @@ -114392,30 +115303,77 @@ entities: - type: Transform pos: 53.5,-1.5 parent: 2 - - uid: 15609 + - uid: 16394 components: - type: Transform - pos: -17.5,-41.5 + rot: 1.5707963267948966 rad + pos: 15.5,-27.5 parent: 2 - - uid: 15610 + - uid: 16395 components: - type: Transform - pos: -17.5,-39.5 + rot: 1.5707963267948966 rad + pos: 14.5,-27.5 parent: 2 - - uid: 15611 + - uid: 16396 components: - type: Transform - pos: -18.5,-39.5 + rot: 1.5707963267948966 rad + pos: 14.5,-28.5 parent: 2 - - uid: 15612 + - uid: 16397 components: - type: Transform - pos: -17.5,-40.5 + rot: 1.5707963267948966 rad + pos: 17.5,-27.5 parent: 2 - - uid: 15613 + - uid: 16398 components: - type: Transform - pos: -20.5,-39.5 + rot: 1.5707963267948966 rad + pos: 19.5,-27.5 + parent: 2 + - uid: 16399 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-27.5 + parent: 2 + - uid: 16400 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-33.5 + parent: 2 + - uid: 16401 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 19.5,-33.5 + parent: 2 + - uid: 16402 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 17.5,-33.5 + parent: 2 + - uid: 16441 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 15.5,-33.5 + parent: 2 + - uid: 16503 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-33.5 + parent: 2 + - uid: 16526 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-32.5 parent: 2 - uid: 16836 components: @@ -115733,8 +116691,20 @@ entities: - type: Transform pos: 10.5,16.5 parent: 16911 + - uid: 19525 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -27.5,-8.5 + parent: 2 - proto: WallReinforcedRust entities: + - uid: 13543 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -19.5,-17.5 + parent: 2 - uid: 15614 components: - type: Transform @@ -115780,31 +116750,6 @@ entities: - type: Transform pos: -27.5,-32.5 parent: 2 - - uid: 15623 - components: - - type: Transform - pos: -11.5,-16.5 - parent: 2 - - uid: 15624 - components: - - type: Transform - pos: -8.5,-17.5 - parent: 2 - - uid: 15625 - components: - - type: Transform - pos: -8.5,-20.5 - parent: 2 - - uid: 15626 - components: - - type: Transform - pos: -15.5,-16.5 - parent: 2 - - uid: 15627 - components: - - type: Transform - pos: -14.5,-16.5 - parent: 2 - uid: 15628 components: - type: Transform @@ -116079,11 +117024,6 @@ entities: rot: -1.5707963267948966 rad pos: -44.5,31.5 parent: 2 - - uid: 15680 - components: - - type: Transform - pos: -15.5,-14.5 - parent: 2 - uid: 15681 components: - type: Transform @@ -117451,16 +118391,6 @@ entities: - type: Transform pos: -4.5,18.5 parent: 2 - - uid: 15941 - components: - - type: Transform - pos: -19.5,-16.5 - parent: 2 - - uid: 15942 - components: - - type: Transform - pos: -19.5,-15.5 - parent: 2 - uid: 15943 components: - type: Transform @@ -119383,12 +120313,6 @@ entities: - type: Transform pos: -10.5,-5.5 parent: 2 - - uid: 16306 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-14.5 - parent: 2 - uid: 16307 components: - type: Transform @@ -120230,16 +121154,6 @@ entities: - type: Transform pos: -70.5,34.5 parent: 2 - - uid: 16357 - components: - - type: Transform - pos: -19.5,-17.5 - parent: 2 - - uid: 16358 - components: - - type: Transform - pos: -17.5,-16.5 - parent: 2 - uid: 16359 components: - type: Transform @@ -120431,18 +121345,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -120524,154 +121428,61 @@ entities: - type: Transform pos: -35.5,38.5 parent: 2 -- proto: WarningAir - entities: - - uid: 16384 - components: - - type: Transform - pos: -13.5,-30.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: WarningN2 entities: - - uid: 16385 + - uid: 8092 components: - type: Transform - pos: -7.5,-30.5 + rot: 3.141592653589793 rad + pos: -6.5,-38.5 parent: 2 - type: Fixtures fixtures: {} - proto: WarningO2 entities: - - uid: 16386 + - uid: 8096 components: - type: Transform - pos: -10.5,-30.5 + rot: 3.141592653589793 rad + pos: -9.5,-38.5 parent: 2 - type: Fixtures fixtures: {} - proto: WarningPlasma entities: - - uid: 16387 + - uid: 8124 components: - type: Transform - pos: 2.5,-30.5 + rot: 3.141592653589793 rad + pos: -13.5,-35.5 parent: 2 - type: Fixtures fixtures: {} - proto: WarningWaste entities: - - uid: 16388 + - uid: 8085 components: - type: Transform - pos: -16.5,-30.5 + rot: 3.141592653589793 rad + pos: -0.5,-38.5 parent: 2 - type: Fixtures fixtures: {} -- proto: WarpPointBombing - entities: - - uid: 16389 + - uid: 8086 components: - type: Transform - pos: 24.5,-42.5 + rot: 3.141592653589793 rad + pos: -3.5,-38.5 parent: 2 - - type: WarpPoint - location: Южные Солнечные панели - - uid: 16390 - components: - - type: Transform - pos: 17.5,-18.5 - parent: 2 - - type: WarpPoint - location: Кабинет АВД - - uid: 16391 - components: - - type: Transform - pos: -12.5,-4.5 - parent: 2 - - type: WarpPoint - location: Лаборатория аномалий - - uid: 16392 - components: - - type: Transform - pos: -19.5,-43.5 - parent: 2 - - type: WarpPoint - location: Ядерный реактор - - uid: 16393 - components: - - type: Transform - pos: -13.5,2.5 - parent: 2 - - type: WarpPoint - location: Серверная НИО - - uid: 16394 - components: - - type: Transform - pos: -35.5,-14.5 - parent: 2 - - type: WarpPoint - location: Телекоммуникации - - uid: 16395 - components: - - type: Transform - pos: -23.5,-20.5 - parent: 2 - - type: WarpPoint - location: Двигатель антиматерии - - uid: 16396 - components: - - type: Transform - pos: 8.5,-22.5 - parent: 2 - - type: WarpPoint - location: Каюта ГП - - uid: 16397 - components: - - type: Transform - pos: -0.5,-19.5 - parent: 2 - - type: WarpPoint - location: Технологическое хранилище - - uid: 16398 - components: - - type: Transform - pos: -2.5,-7.5 - parent: 2 - - type: WarpPoint - location: Золотой унитаз капитана - - uid: 16399 - components: - - type: Transform - pos: -29.5,1.5 - parent: 2 - - type: WarpPoint - follow: True - location: Западные Солнечные панели - - uid: 16400 - components: - - type: Transform - pos: -11.5,39.5 - parent: 2 - - type: WarpPoint - location: Храм - - uid: 16401 - components: - - type: Transform - pos: 36.5,-5.5 - parent: 2 - - type: WarpPoint - location: Офис КМа - - uid: 16402 - components: - - type: Transform - pos: 42.5,6.5 - parent: 2 - - type: WarpPoint - location: Стыковочный док снабжения + - type: Fixtures + fixtures: {} - proto: WaterCooler entities: + - uid: 12203 + components: + - type: Transform + pos: -14.5,-25.5 + parent: 2 - uid: 16403 components: - type: Transform @@ -120895,10 +121706,10 @@ entities: parent: 2 - proto: WaterVaporCanister entities: - - uid: 16441 + - uid: 5337 components: - type: Transform - pos: -6.5,-31.5 + pos: -1.5,-39.5 parent: 2 - uid: 16442 components: @@ -121055,6 +121866,13 @@ entities: - type: Transform pos: 31.5,-28.5 parent: 2 +- proto: WeaponShotgunDoubleBarreled + entities: + - uid: 11526 + components: + - type: Transform + pos: -54.390926,34.710518 + parent: 2 - proto: WeaponShotgunDoubleBarreledRubber entities: - uid: 16464 @@ -121067,11 +121885,6 @@ entities: - type: Transform pos: 42.422455,-25.492567 parent: 2 - - uid: 16466 - components: - - type: Transform - pos: -10.509344,-23.150303 - parent: 2 - proto: WeaponShotgunEnforcer entities: - uid: 16467 @@ -121297,10 +122110,10 @@ entities: parent: 2 - proto: WeldingFuelTankHighCapacity entities: - - uid: 16503 + - uid: 11184 components: - type: Transform - pos: -14.5,-25.5 + pos: -3.5,-25.5 parent: 2 - uid: 16504 components: @@ -121389,11 +122202,6 @@ entities: parent: 2 - proto: WindoorSecure entities: - - uid: 16516 - components: - - type: Transform - pos: 29.5,35.5 - parent: 2 - uid: 16517 components: - type: Transform @@ -121448,48 +122256,6 @@ entities: rot: -1.5707963267948966 rad pos: 32.5,-28.5 parent: 2 -- proto: WindoorSecureAtmosphericsLocked - entities: - - uid: 16526 - components: - - type: Transform - pos: -5.5,-29.5 - parent: 2 - - uid: 16527 - components: - - type: Transform - pos: -6.5,-29.5 - parent: 2 - - uid: 16528 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-31.5 - parent: 2 - - uid: 16529 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-31.5 - parent: 2 - - uid: 16530 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-31.5 - parent: 2 - - uid: 16531 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-31.5 - parent: 2 - - uid: 16532 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-31.5 - parent: 2 - proto: WindoorSecureChemistryLocked entities: - uid: 16533 @@ -121534,6 +122300,16 @@ entities: parent: 2 - proto: WindoorSecureEngineeringLocked entities: + - uid: 9429 + components: + - type: Transform + pos: -17.5,-16.5 + parent: 2 + - uid: 12421 + components: + - type: Transform + pos: -16.5,-16.5 + parent: 2 - uid: 16540 components: - type: Transform @@ -121564,21 +122340,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,15.5 parent: 2 -- proto: WindoorSecurePlasma - entities: - - uid: 16544 - components: - - type: Transform - pos: -7.5,-22.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 12017: - - - DoorStatus - - On - 12018: - - - DoorStatus - - On - proto: WindoorSecureSalvageLocked entities: - uid: 16545 @@ -121726,21 +122487,6 @@ entities: - type: Transform pos: -11.5,-6.5 parent: 2 - - uid: 16569 - components: - - type: Transform - pos: -14.5,-14.5 - parent: 2 - - uid: 16570 - components: - - type: Transform - pos: -13.5,-14.5 - parent: 2 - - uid: 16571 - components: - - type: Transform - pos: -12.5,-14.5 - parent: 2 - uid: 16572 components: - type: Transform @@ -122289,6 +123035,42 @@ entities: parent: 2 - proto: WindowReinforcedDirectional entities: + - uid: 9669 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-27.5 + parent: 2 + - uid: 9849 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-26.5 + parent: 2 + - uid: 9999 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-25.5 + parent: 2 + - uid: 10003 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-25.5 + parent: 2 + - uid: 12188 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-26.5 + parent: 2 + - uid: 12189 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-27.5 + parent: 2 - uid: 16661 components: - type: Transform @@ -122306,29 +123088,11 @@ entities: rot: -1.5707963267948966 rad pos: 32.5,-25.5 parent: 2 - - uid: 16664 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-35.5 - parent: 2 - uid: 16665 components: - type: Transform pos: 36.5,-12.5 parent: 2 - - uid: 16666 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -22.5,-39.5 - parent: 2 - - uid: 16667 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -23.5,-39.5 - parent: 2 - uid: 16668 components: - type: Transform @@ -122619,24 +123383,6 @@ entities: rot: 1.5707963267948966 rad pos: -32.5,-16.5 parent: 2 - - uid: 16718 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-36.5 - parent: 2 - - uid: 16719 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-37.5 - parent: 2 - - uid: 16720 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-38.5 - parent: 2 - uid: 16721 components: - type: Transform @@ -122820,16 +123566,6 @@ entities: rot: -1.5707963267948966 rad pos: -28.5,4.5 parent: 2 - - uid: 16753 - components: - - type: Transform - pos: -53.5,34.5 - parent: 2 - - uid: 16754 - components: - - type: Transform - pos: -51.5,33.5 - parent: 2 - uid: 16755 components: - type: Transform @@ -123163,6 +123899,33 @@ entities: canCollide: False - proto: WoodenBench entities: + - uid: 994 + components: + - type: Transform + pos: -13.5,-13.5 + parent: 2 + - uid: 1282 + components: + - type: Transform + pos: -14.5,-13.5 + parent: 2 + - uid: 4076 + components: + - type: Transform + pos: -12.5,-13.5 + parent: 2 + - uid: 5796 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,-30.5 + parent: 2 + - uid: 8221 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-16.5 + parent: 2 - uid: 16810 components: - type: Transform @@ -123303,6 +124066,11 @@ entities: parent: 16911 - proto: Wrench entities: + - uid: 14313 + components: + - type: Transform + pos: -16.854548,-29.9884 + parent: 2 - uid: 16828 components: - type: Transform diff --git a/Resources/Maps/Corvax/corvax_chloris.yml b/Resources/Maps/Corvax/corvax_chloris.yml index 57fe6cc819..cd5cc31b94 100644 --- a/Resources/Maps/Corvax/corvax_chloris.yml +++ b/Resources/Maps/Corvax/corvax_chloris.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 266.0.0 + engineVersion: 275.2.0 forkId: "" forkVersion: "" - time: 09/13/2025 05:51:48 - entityCount: 51455 + time: 04/18/2026 14:31:37 + entityCount: 51437 maps: - 1 grids: @@ -18017,144 +18017,47 @@ entities: uniqueMixes: - volume: 2500 immutable: True - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + moles: {} - volume: 2500 temperature: 293.15 moles: - - 21.824879 - - 82.10312 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 21.824879 + Nitrogen: 82.10312 - volume: 2500 temperature: 293.15 moles: - - 0 - - 103.92799 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Nitrogen: 103.92799 - volume: 2500 temperature: 235 moles: - - 27.225372 - - 102.419266 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 27.225372 + Nitrogen: 102.419266 + - volume: 2500 + temperature: 293.15 + moles: {} - volume: 2500 temperature: 293.15 moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Nitrogen: 6666.982 - volume: 2500 temperature: 293.15 moles: - - 0 - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 6666.982 - volume: 2500 temperature: 293.15 moles: - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - volume: 2500 - temperature: 293.15 - moles: - - 0 - - 0 - - 0 - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Plasma: 6666.982 - volume: 2500 temperature: 184.23123 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + moles: {} chunkSize: 4 - type: NavMap - type: BecomesStation id: Chloris - type: ImplicitRoof + - type: TileHistory + chunkHistory: {} + - type: ExplosionAirtightGrid - uid: 4 components: - type: MetaData @@ -26684,7 +26587,7 @@ entities: lastSignals: DoorStatus: True - type: Door - secondsUntilStateChange: -194113.25 + secondsUntilStateChange: -194186.08 state: Opening - type: Forensics residues: [] @@ -27535,7 +27438,7 @@ entities: pos: 25.5,132.5 parent: 2 - type: Door - secondsUntilStateChange: -199113.17 + secondsUntilStateChange: -199186 state: Opening - type: DeviceLinkSource lastSignals: @@ -27718,7 +27621,7 @@ entities: pos: 99.5,50.5 parent: 2 - type: Door - secondsUntilStateChange: -116404.414 + secondsUntilStateChange: -116477.24 state: Opening - type: DeviceLinkSource lastSignals: @@ -29101,7 +29004,7 @@ entities: pos: 14.5,132.5 parent: 2 - type: Door - secondsUntilStateChange: -187194.25 + secondsUntilStateChange: -187267.08 state: Opening - type: DeviceLinkSource lastSignals: @@ -34150,124 +34053,6 @@ entities: rot: 3.141592653589793 rad pos: 88.364525,129.74033 parent: 2 -- proto: BaseChemistryEmptyVial - entities: - - uid: 18 - components: - - type: MetaData - name: пробирка семина - - type: Transform - pos: 102.60848,130.69435 - parent: 2 - - type: Tag - tags: - - CentrifugeCompatible - - type: SolutionContainerManager - solutions: null - containers: - - beaker - - type: ContainerContainer - containers: - solution@beaker: !type:ContainerSlot - ent: 19 - - uid: 20 - components: - - type: Transform - pos: 43.725544,126.74478 - parent: 2 - - type: Tag - tags: - - CentrifugeCompatible - - type: SolutionContainerManager - solutions: null - containers: - - beaker - - type: SolutionTransfer - transferAmount: 25 - - type: ContainerContainer - containers: - solution@beaker: !type:ContainerSlot - ent: 21 - - uid: 1868 - components: - - type: Transform - pos: 57.34797,91.7121 - parent: 2 - - uid: 1869 - components: - - type: Transform - pos: 61.26306,142.71603 - parent: 2 - - uid: 1870 - components: - - type: Transform - pos: 61.466034,142.80197 - parent: 2 - - uid: 1871 - components: - - type: Transform - pos: 61.122284,140.72385 - parent: 2 - - uid: 1872 - components: - - type: Transform - pos: 61.26291,140.83322 - parent: 2 - - uid: 1873 - components: - - type: Transform - pos: 57.238594,91.57147 - parent: 2 - - uid: 1874 - components: - - type: Transform - pos: 35.351513,116.66543 - parent: 2 - - uid: 1875 - components: - - type: Transform - pos: 35.476513,116.69668 - parent: 2 - - uid: 1876 - components: - - type: Transform - pos: 44.86971,126.72888 - parent: 2 - - uid: 1877 - components: - - type: Transform - pos: 44.68221,126.74451 - parent: 2 - - uid: 1878 - components: - - type: Transform - pos: 50.39661,73.63436 - parent: 2 - - uid: 1879 - components: - - type: Transform - pos: 50.70911,73.52499 - parent: 2 - - uid: 1880 - components: - - type: Transform - pos: 107.39228,130.74123 - parent: 2 - - uid: 1881 - components: - - type: Transform - pos: 102.470406,130.77248 - parent: 2 - - uid: 1882 - components: - - type: Transform - pos: 17.720512,144.66206 - parent: 2 - - uid: 1883 - components: - - type: Transform - pos: 12.276604,122.647415 - parent: 2 - proto: BaseComputer entities: - uid: 1884 @@ -36394,18 +36179,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: BodyBagFolded entities: - uid: 2191 @@ -114574,6 +114349,124 @@ entities: - type: Transform pos: 43.40096,126.54138 parent: 2 +- proto: ChemistryEmptyVial + entities: + - uid: 18 + components: + - type: MetaData + name: пробирка семина + - type: Transform + pos: 102.60848,130.69435 + parent: 2 + - type: Tag + tags: + - CentrifugeCompatible + - type: SolutionContainerManager + solutions: null + containers: + - beaker + - type: ContainerContainer + containers: + solution@beaker: !type:ContainerSlot + ent: 19 + - uid: 20 + components: + - type: Transform + pos: 43.725544,126.74478 + parent: 2 + - type: Tag + tags: + - CentrifugeCompatible + - type: SolutionContainerManager + solutions: null + containers: + - beaker + - type: SolutionTransfer + transferAmount: 25 + - type: ContainerContainer + containers: + solution@beaker: !type:ContainerSlot + ent: 21 + - uid: 1868 + components: + - type: Transform + pos: 57.34797,91.7121 + parent: 2 + - uid: 1869 + components: + - type: Transform + pos: 61.26306,142.71603 + parent: 2 + - uid: 1870 + components: + - type: Transform + pos: 61.466034,142.80197 + parent: 2 + - uid: 1871 + components: + - type: Transform + pos: 61.122284,140.72385 + parent: 2 + - uid: 1872 + components: + - type: Transform + pos: 61.26291,140.83322 + parent: 2 + - uid: 1873 + components: + - type: Transform + pos: 57.238594,91.57147 + parent: 2 + - uid: 1874 + components: + - type: Transform + pos: 35.351513,116.66543 + parent: 2 + - uid: 1875 + components: + - type: Transform + pos: 35.476513,116.69668 + parent: 2 + - uid: 1876 + components: + - type: Transform + pos: 44.86971,126.72888 + parent: 2 + - uid: 1877 + components: + - type: Transform + pos: 44.68221,126.74451 + parent: 2 + - uid: 1878 + components: + - type: Transform + pos: 50.39661,73.63436 + parent: 2 + - uid: 1879 + components: + - type: Transform + pos: 50.70911,73.52499 + parent: 2 + - uid: 1880 + components: + - type: Transform + pos: 107.39228,130.74123 + parent: 2 + - uid: 1881 + components: + - type: Transform + pos: 102.470406,130.77248 + parent: 2 + - uid: 1882 + components: + - type: Transform + pos: 17.720512,144.66206 + parent: 2 + - uid: 1883 + components: + - type: Transform + pos: 12.276604,122.647415 + parent: 2 - proto: ChemistryHotplate entities: - uid: 17338 @@ -115308,18 +115201,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -115681,18 +115564,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -116061,8 +115934,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: ClosetJanitorFilled @@ -116090,18 +115963,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 17579 components: - type: Transform @@ -116120,18 +115983,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: ClosetL3JanitorFilled entities: - uid: 17581 @@ -116238,18 +116091,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 17599 components: - type: Transform @@ -116450,18 +116293,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 17637 components: - type: Transform @@ -116478,18 +116311,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 17639 components: - type: Transform @@ -116721,18 +116544,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -116755,18 +116568,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -116811,21 +116614,9 @@ entities: volume: 200 immutable: False temperature: 293.14673 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 17688 @@ -116839,18 +116630,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -116965,18 +116746,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -117001,18 +116772,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - proto: ClosetWallEmergencyFilledRandom @@ -117124,18 +116885,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - uid: 17723 @@ -117233,18 +116984,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - uid: 17735 @@ -117269,18 +117010,8 @@ entities: immutable: False temperature: 293.1462 moles: - - 1.606311 - - 6.042789 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.606311 + Nitrogen: 6.042789 - type: ContainerContainer containers: entity_storage: !type:Container @@ -117307,18 +117038,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - uid: 17742 @@ -117332,18 +117053,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - proto: ClosetWallMixed @@ -117360,18 +117071,8 @@ entities: immutable: False temperature: 293.1465 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -117405,18 +117106,8 @@ entities: immutable: False temperature: 293.1462 moles: - - 1.606311 - - 6.042789 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.606311 + Nitrogen: 6.042789 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123121,18 +122812,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123173,8 +122854,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateEmergencyInflatablewall @@ -123380,8 +123061,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateEngineeringAMEControl @@ -123495,18 +123176,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123545,18 +123216,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 18614 components: - type: MetaData @@ -123570,18 +123231,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123610,18 +123261,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 18619 components: - type: Transform @@ -123649,8 +123290,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18620 @@ -123671,18 +123312,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateFunBoxing entities: - uid: 18622 @@ -123708,18 +123339,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateFunPlushie entities: - uid: 18625 @@ -123733,18 +123354,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 18626 components: - type: Transform @@ -123768,18 +123379,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateGenericSteel entities: - uid: 18629 @@ -123803,18 +123404,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123852,18 +123443,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123915,8 +123496,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateHydroponicsSeeds @@ -123932,18 +123513,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateHydroponicsTools entities: - uid: 18641 @@ -123957,18 +123528,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateHydroSecure entities: - uid: 18642 @@ -124071,8 +123632,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateMedicalSecure @@ -124090,18 +123651,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateMedicalSurgery entities: - uid: 18655 @@ -124117,8 +123668,8 @@ entities: pos: 96.5,141.5 parent: 2 - type: EntityStorage - open: True removedMasks: 28 + open: True - type: Fixtures fixtures: fix1: @@ -124197,8 +123748,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CratePrivateSecure @@ -124254,21 +123805,9 @@ entities: volume: 200 immutable: False temperature: 293.14673 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateScience @@ -124305,8 +123844,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18668 @@ -124336,8 +123875,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateScienceSecure @@ -124360,18 +123899,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateSecure entities: - uid: 18671 @@ -124385,18 +123914,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateSecurityHelmet entities: - uid: 18672 @@ -124431,18 +123950,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateServicePersonnel entities: - uid: 18676 @@ -124477,18 +123986,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -124535,8 +124034,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18684 @@ -124550,18 +124049,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -124588,18 +124077,8 @@ entities: immutable: False temperature: 293.1462 moles: - - 1.606311 - - 6.042789 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.606311 + Nitrogen: 6.042789 - proto: CrateTrashCart entities: - uid: 14162 @@ -124613,18 +124092,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -124639,7 +124108,6 @@ entities: - 14163 - 14164 - 14166 - - 14171 paper_label: !type:ContainerSlot showEnts: False occludes: True @@ -124675,21 +124143,9 @@ entities: volume: 200 immutable: False temperature: 293.14673 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18690 @@ -124719,8 +124175,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18691 @@ -124782,8 +124238,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18698 @@ -124828,8 +124284,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18702 @@ -124869,8 +124325,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18705 @@ -124918,18 +124374,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -124955,18 +124401,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -125051,18 +124487,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrewMonitoringServer entities: - uid: 18724 @@ -125361,7 +124787,6 @@ entities: desc: Это плиткорез. ПЛИТКОРЕЗ. Он режет. Придайте разнообразия полу вашей станции с помощью интересных узоров! Не засовывайте внутрь пальцы. name: плиткорез - type: Transform - rot: 3.141592653589793 rad pos: 122.5,65.5 parent: 2 - proto: CyberPen @@ -143103,18 +142528,6 @@ entities: rot: 1.5707963267948966 rad pos: 98.5,77.5 parent: 2 -- proto: FigureSpawner - entities: - - uid: 21777 - components: - - type: Transform - pos: 90.5,81.5 - parent: 2 - - uid: 21778 - components: - - type: Transform - pos: 93.5,79.5 - parent: 2 - proto: filingCabinet entities: - uid: 21779 @@ -239063,6 +238476,8 @@ entities: pos: 105.5,160.5 parent: 2 - type: Openable + sound: !type:SoundCollectionSpecifier + collection: canOpenSounds opened: True - proto: GlassBoxLaserFilled entities: @@ -242237,18 +241652,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242275,18 +241680,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242316,18 +241711,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242358,18 +241743,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242414,8 +241789,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 35132 @@ -242432,18 +241807,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242467,18 +241832,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242508,18 +241863,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: GunSafeDisabler entities: - uid: 35141 @@ -242675,8 +242020,6 @@ entities: - type: Transform pos: 130.49286,129.59427 parent: 2 -- proto: HandheldHealthAnalyzerUnpowered - entities: - uid: 35161 components: - type: Transform @@ -248681,18 +248024,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: LockerAtmosphericsFilled entities: - uid: 17839 @@ -248706,18 +248039,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248740,18 +248063,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248774,18 +248087,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248812,18 +248115,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248908,18 +248201,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248988,18 +248271,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249029,18 +248302,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Lock locked: False - type: ContainerContainer @@ -249069,18 +248332,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: Lock locked: False - uid: 35983 @@ -249094,18 +248347,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: LockerDetectiveFilled entities: - uid: 35164 @@ -249119,18 +248362,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249237,18 +248470,8 @@ entities: immutable: False temperature: 293.1465 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249361,8 +248584,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 36009 @@ -249378,18 +248601,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36010 components: - type: Transform @@ -249403,18 +248616,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36011 components: - type: Transform @@ -249428,18 +248631,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36012 components: - type: Transform @@ -249483,18 +248676,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249561,18 +248744,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249637,18 +248810,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249692,18 +248855,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249742,18 +248895,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249799,18 +248942,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249836,18 +248969,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249876,18 +248999,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249955,18 +249068,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250080,18 +249183,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250186,18 +249279,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250226,18 +249309,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250267,18 +249340,8 @@ entities: immutable: False temperature: 293.14835 moles: - - 1.8977377 - - 7.139109 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8977377 + Nitrogen: 7.139109 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250355,18 +249418,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250478,18 +249531,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250542,21 +249585,9 @@ entities: volume: 200 immutable: False temperature: 293.147 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 36084 @@ -250595,21 +249626,9 @@ entities: volume: 200 immutable: False temperature: 293.147 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 36085 @@ -250686,18 +249705,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - uid: 36094 @@ -250720,18 +249729,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -253311,6 +252310,18 @@ entities: - type: Transform pos: 109.5,164.5 parent: 2 +- proto: MechFigurineSpawner50 + entities: + - uid: 21777 + components: + - type: Transform + pos: 90.5,81.5 + parent: 2 + - uid: 21778 + components: + - type: Transform + pos: 93.5,79.5 + parent: 2 - proto: MedalCase entities: - uid: 36438 @@ -254529,18 +253540,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36547 components: - type: Transform @@ -254553,18 +253554,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36548 components: - type: Transform @@ -256003,7 +254994,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256076,7 +255073,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256149,7 +255152,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256222,7 +255231,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256295,7 +255310,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256385,7 +255406,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256481,7 +255508,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256539,7 +255572,13 @@ entities: - type: Paper content: "[bold]Добро пожаловать на исследовательскую станцию Хлорис![/bold] \nМы рады приветствовать вас на Венере-2. Это уникальное место, где вы сможете насладиться удивительными видами и провести незабываемое время.\n\n[head=3]Краткая информация о станции[/head]\n\n- Высота: 100 километров над уровнем поверхности Венеры-2\n- Основные цели: Научные исследования, наблюдение за атмосферными явлениями, экзобиология и астрофизика.\n \n- Запланировано: Через несколько месяцев станция начнёт постепенный спуск в более плотные слои атмосферы Венеры-2 для углублённых исследований.\n\nНа такой высоте в атмосфере Венеры-2 абсолютно отсутствует давление. Однако внутри нашей станции поддерживается комфортный микроклимат.\n\n[color=#ff0000]Важно помнить:\n\n- Не покидайте пределы станции без разрешения и жестких скафандров.\n- Обязательно носите ваш кислородный баллон и маску при себе на случай аварийных ситуаций.\n[/color]\n\n[head=3]Услуги для пассажиров\n[/head]\n\n1. Залы и парки с удобными креслами и прекраснымм видом на поверхность планеты.\n2. Бар и кухня с высококлассным сервисом и отличным меню.\n3. Баня, бассейн, театр и библиотека для культурного досуга.\nУже сегодня бронируйте \n\n" - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256635,7 +255674,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256731,7 +255776,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256826,7 +255877,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256917,7 +255974,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -256988,7 +256051,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257061,7 +256130,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257134,7 +256209,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257224,7 +256305,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257320,7 +256407,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257416,7 +256509,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257512,7 +256611,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257608,7 +256713,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257704,7 +256815,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257800,7 +256917,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257857,6 +256980,8 @@ entities: name: пропал - type: Transform parent: 49 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -257916,7 +257041,13 @@ entities: [head=1]НАГРАДА - 5.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -258025,7 +257156,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -258114,7 +257251,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -258205,7 +257348,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258300,7 +257449,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258389,7 +257544,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258478,7 +257639,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258567,7 +257734,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258656,7 +257829,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258745,7 +257924,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258834,7 +258019,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258923,7 +258114,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259012,7 +258209,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259101,7 +258304,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259190,7 +258399,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259279,7 +258494,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259368,7 +258589,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259457,7 +258684,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259546,7 +258779,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259635,7 +258874,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259724,7 +258969,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259819,7 +259070,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259914,7 +259171,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -260009,7 +259272,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -260104,7 +259373,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -260199,7 +259474,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -260293,7 +259574,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260389,7 +259676,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260485,7 +259778,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260581,7 +259880,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260677,7 +259982,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260773,7 +260084,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260869,7 +260186,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260959,7 +260282,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261049,7 +260378,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261145,7 +260480,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261241,7 +260582,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261337,7 +260684,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261433,7 +260786,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261513,7 +260872,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -261585,7 +260950,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -261666,7 +261037,13 @@ entities: ↓ ↓ ↓ ↓ ↑ <<- --- --- --- --- --- --- --- << Обратка - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -261744,7 +261121,13 @@ entities: [color=#FFA500FF] ↑ ↓ ↓ ↓ ↓ ↓ ↑ <<- --- --- --- --- --- --- --- << Обратка - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -261793,6 +261176,8 @@ entities: name: пропал - type: Transform parent: 165 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -261852,7 +261237,13 @@ entities: [head=1]НАГРАДА - 5.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -261961,7 +261352,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -262063,7 +261460,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -262194,7 +261597,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -262296,7 +261705,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -262347,6 +261762,8 @@ entities: name: пропал - type: Transform parent: 182 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262406,7 +261823,13 @@ entities: [head=1]НАГРАДА - 5.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262516,7 +261939,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -262566,6 +261995,8 @@ entities: - type: Transform pos: 33.581455,154.6076 parent: 2 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262625,7 +262056,13 @@ entities: [head=1]НАГРАДА - 5.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262681,6 +262118,8 @@ entities: name: работа - type: Transform parent: 193 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262721,7 +262160,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262779,6 +262224,8 @@ entities: name: вакансия - type: Transform parent: 193 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262824,7 +262271,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262882,6 +262335,8 @@ entities: name: вакансия - type: Transform parent: 193 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262927,7 +262382,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262985,6 +262446,8 @@ entities: name: вакансия - type: Transform parent: 193 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -263030,7 +262493,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -265960,7 +265429,13 @@ entities: - type: Pill pillType: 11 - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: ContainerContainer @@ -266474,7 +265949,7 @@ entities: lastSignals: DoorStatus: True - type: Door - secondsUntilStateChange: -182147.42 + secondsUntilStateChange: -182220.25 state: Opening - type: Airlock autoClose: False @@ -266757,6 +266232,53 @@ entities: - type: Transform pos: 109.43205,54.61765 parent: 2 +- proto: PlushieSpawner50 + entities: + - uid: 43855 + components: + - type: Transform + pos: 64.5,119.5 + parent: 2 + - uid: 43856 + components: + - type: Transform + pos: 92.5,42.5 + parent: 2 + - uid: 43857 + components: + - type: Transform + pos: 94.5,41.5 + parent: 2 + - uid: 43858 + components: + - type: Transform + pos: 93.5,39.5 + parent: 2 + - uid: 43859 + components: + - type: Transform + pos: 104.5,46.5 + parent: 2 + - uid: 43860 + components: + - type: Transform + pos: 102.5,46.5 + parent: 2 + - uid: 43861 + components: + - type: Transform + pos: 139.5,74.5 + parent: 2 + - uid: 43862 + components: + - type: Transform + pos: 134.5,25.5 + parent: 2 + - uid: 43863 + components: + - type: Transform + pos: 133.5,25.5 + parent: 2 - proto: PonderingOrb entities: - uid: 37024 @@ -275360,39 +274882,6 @@ entities: - type: Physics canCollide: False bodyType: Static -- proto: PrefilledSyringe - entities: - - uid: 8 - components: - - type: MetaData - name: шприц галоперидола - - type: Transform - parent: 5 - - type: SolutionContainerManager - solutions: null - containers: - - injector - - type: Physics - canCollide: False - - type: ContainerContainer - containers: - solution@injector: !type:ContainerSlot - ent: 9 - - uid: 38406 - components: - - type: Transform - pos: 22.331291,134.61938 - parent: 2 - - uid: 38407 - components: - - type: Transform - pos: 22.456291,134.52216 - parent: 2 - - uid: 38408 - components: - - type: Transform - pos: 22.636848,134.42494 - parent: 2 - proto: PrintedDocumentSentence entities: - uid: 38409 @@ -296139,7 +295628,7 @@ entities: - type: Transform pos: 158.5,98.5 parent: 2 -- proto: SpacemenFigureSpawner +- proto: SpacemenFigurineSpawner90 entities: - uid: 41531 components: @@ -296535,18 +296024,6 @@ entities: - type: Transform pos: 98.5,131.5 parent: 2 -- proto: SpawnPointBoxer - entities: - - uid: 41598 - components: - - type: Transform - pos: 100.5,90.5 - parent: 2 - - uid: 41599 - components: - - type: Transform - pos: 108.5,90.5 - parent: 2 - proto: SpawnPointBrigmedic entities: - uid: 41600 @@ -297044,13 +296521,6 @@ entities: - type: Transform pos: 138.5,116.5 parent: 2 -- proto: SpawnPointZookeeper - entities: - - uid: 41683 - components: - - type: Transform - pos: 96.5,152.5 - parent: 2 - proto: SpeedLoaderCap entities: - uid: 18687 @@ -297076,15 +296546,6 @@ entities: - type: Transform pos: 137.52753,91.59235 parent: 2 -- proto: SpeedLoaderPistolPractice - entities: - - uid: 14171 - components: - - type: Transform - parent: 14162 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: SpiderWeb entities: - uid: 41685 @@ -298944,18 +298405,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -298980,18 +298431,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -299016,18 +298457,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -299049,18 +298480,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -299162,18 +298583,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -302357,6 +301768,25 @@ entities: parent: 2 - proto: Syringe entities: + - uid: 8 + components: + - type: MetaData + name: шприц галоперидола + - type: Transform + parent: 5 + - type: Tag + tags: + - Syringe + - type: SolutionContainerManager + solutions: null + containers: + - injector + - type: Physics + canCollide: False + - type: ContainerContainer + containers: + solution@injector: !type:ContainerSlot + ent: 9 - uid: 2630 components: - type: Transform @@ -302364,6 +301794,21 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage + - uid: 38406 + components: + - type: Transform + pos: 22.331291,134.61938 + parent: 2 + - uid: 38407 + components: + - type: Transform + pos: 22.456291,134.52216 + parent: 2 + - uid: 38408 + components: + - type: Transform + pos: 22.636848,134.42494 + parent: 2 - uid: 42313 components: - type: Transform @@ -310677,80 +310122,6 @@ entities: - type: Transform pos: 143.49774,83.59558 parent: 2 -- proto: TowelColorBlue - entities: - - uid: 43797 - components: - - type: Transform - pos: 107.586754,89.60101 - parent: 2 - - uid: 43798 - components: - - type: Transform - pos: 107.38363,89.50726 - parent: 2 -- proto: TowelColorDarkGreen - entities: - - uid: 43799 - components: - - type: Transform - pos: 86.47167,135.47217 - parent: 2 - - uid: 43800 - components: - - type: Transform - pos: 86.4873,135.6128 - parent: 2 -- proto: TowelColorGold - entities: - - uid: 43801 - components: - - type: Transform - pos: 105.49299,102.53097 - parent: 2 -- proto: TowelColorOrange - entities: - - uid: 43802 - components: - - type: Transform - pos: 102.496445,53.633625 - parent: 2 - - uid: 43803 - components: - - type: Transform - pos: 102.669525,53.584015 - parent: 2 -- proto: TowelColorPink - entities: - - uid: 43804 - components: - - type: Transform - pos: 103.48082,53.64925 - parent: 2 - - uid: 43805 - components: - - type: Transform - pos: 103.544525,53.56839 - parent: 2 -- proto: TowelColorRed - entities: - - uid: 43806 - components: - - type: Transform - pos: 101.430504,89.52289 - parent: 2 - - uid: 43807 - components: - - type: Transform - pos: 101.586754,89.66351 - parent: 2 -- proto: TowelColorSilver - entities: - - uid: 43808 - components: - - type: Transform - pos: 103.50861,102.53097 - parent: 2 - proto: TowelColorWhite entities: - uid: 17884 @@ -311089,53 +310460,6 @@ entities: - type: Transform pos: 153.52248,107.49803 parent: 2 -- proto: ToySpawner - entities: - - uid: 43855 - components: - - type: Transform - pos: 64.5,119.5 - parent: 2 - - uid: 43856 - components: - - type: Transform - pos: 92.5,42.5 - parent: 2 - - uid: 43857 - components: - - type: Transform - pos: 94.5,41.5 - parent: 2 - - uid: 43858 - components: - - type: Transform - pos: 93.5,39.5 - parent: 2 - - uid: 43859 - components: - - type: Transform - pos: 104.5,46.5 - parent: 2 - - uid: 43860 - components: - - type: Transform - pos: 102.5,46.5 - parent: 2 - - uid: 43861 - components: - - type: Transform - pos: 139.5,74.5 - parent: 2 - - uid: 43862 - components: - - type: Transform - pos: 134.5,25.5 - parent: 2 - - uid: 43863 - components: - - type: Transform - pos: 133.5,25.5 - parent: 2 - proto: TrackingImplanter entities: - uid: 2540 @@ -348624,18 +347948,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -348660,18 +347974,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -348784,18 +348088,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -348820,18 +348114,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -348879,18 +348163,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 50863 components: - type: Transform @@ -348914,18 +348188,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: WardrobeMedicalDoctorFilled entities: - uid: 50866 @@ -349087,18 +348351,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -349128,18 +348382,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -349192,18 +348436,8 @@ entities: immutable: False temperature: 293.1465 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -349326,22 +348560,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} -- proto: WarpPointBombing - entities: - - uid: 50903 - components: - - type: Transform - pos: 142.5,111.5 - parent: 2 - - type: WarpPoint - location: Оружейная - - uid: 50904 - components: - - type: Transform - pos: 147.5,32.5 - parent: 2 - - type: WarpPoint - location: ТЭГ - proto: WaterCooler entities: - uid: 50905 diff --git a/Resources/Maps/Corvax/corvax_outpost.yml b/Resources/Maps/Corvax/corvax_outpost.yml index 37bdb4b2d8..bbf83a6b3b 100644 --- a/Resources/Maps/Corvax/corvax_outpost.yml +++ b/Resources/Maps/Corvax/corvax_outpost.yml @@ -53612,331 +53612,6 @@ entities: rot: 1.5707963267948966 rad pos: -48.5,18.5 parent: 2 -- proto: FigureSpawner - entities: - - uid: 7795 - components: - - type: MetaData - name: которт или хлебопес - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - type: RandomSpawner - prototypes: - - MobCatCake - - MobBreadDog - - uid: 19626 - components: - - type: Transform - anchored: False - pos: 669.5,-1.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19627 - components: - - type: Transform - anchored: False - pos: -405.5,373.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19628 - components: - - type: Transform - anchored: False - pos: 957.5,120.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19629 - components: - - type: Transform - anchored: False - pos: 35.5,-950.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19630 - components: - - type: Transform - anchored: False - pos: 613.5,-583.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19631 - components: - - type: Transform - anchored: False - pos: -400.5,-142.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19632 - components: - - type: Transform - anchored: False - pos: -71.5,-181.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19633 - components: - - type: Transform - anchored: False - pos: -219.5,-161.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19634 - components: - - type: Transform - anchored: False - pos: -876.5,-13.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19635 - components: - - type: Transform - anchored: False - pos: -282.5,142.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19636 - components: - - type: Transform - anchored: False - pos: -195.5,184.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19637 - components: - - type: Transform - anchored: False - pos: -193.5,-217.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - proto: filingCabinetDrawerRandom entities: - uid: 7796 diff --git a/Resources/Maps/Corvax/corvax_paper.yml b/Resources/Maps/Corvax/corvax_paper.yml index 2ce9ae02c8..17df6cef9c 100644 --- a/Resources/Maps/Corvax/corvax_paper.yml +++ b/Resources/Maps/Corvax/corvax_paper.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 270.1.0 - forkId: syndicate - forkVersion: 80acc36543c98a5773da7bd8c7011b5fa7e9b105 - time: 01/25/2026 10:50:07 - entityCount: 18367 + engineVersion: 275.2.0 + forkId: "" + forkVersion: "" + time: 04/18/2026 14:34:20 + entityCount: 18327 maps: - 1 grids: @@ -8888,6 +8888,8 @@ entities: - type: NavMap - type: ImplicitRoof - type: ExplosionAirtightGrid + - type: TileHistory + chunkHistory: {} - uid: 4 components: - type: MetaData @@ -49649,7 +49651,6 @@ entities: - uid: 7306 components: - type: Transform - rot: -1.5707963267948966 rad pos: 48.5,73.5 parent: 2 - proto: DefaultStationBeacon @@ -55213,7 +55214,7 @@ entities: - type: Label currentLabel: кофейный ликёр - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.55700105 + sprayFizzinessThresholdRoll: 0.68399715 - type: NameModifier baseName: бутылка кофейного ликёра - type: ContainerContainer @@ -55520,7 +55521,7 @@ entities: - type: Label currentLabel: ром - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.7229936 + sprayFizzinessThresholdRoll: 0.6197575 - type: NameModifier baseName: кубинский пряный ром капитана Пита - type: ContainerContainer @@ -55562,7 +55563,7 @@ entities: - type: Label currentLabel: текила - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.5990371 + sprayFizzinessThresholdRoll: 0.6505251 - type: NameModifier baseName: бутылка текилы Каккаво гарантированного качества - type: ContainerContainer @@ -55603,7 +55604,7 @@ entities: - type: Label currentLabel: вермут - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.08046895 + sprayFizzinessThresholdRoll: 0.0931613 - type: NameModifier baseName: бутылка вермута Золотой глаз - type: ContainerContainer @@ -55626,7 +55627,7 @@ entities: - type: Label currentLabel: водка - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.025131963 + sprayFizzinessThresholdRoll: 0.12908888 - type: NameModifier baseName: бутылка водки - type: ContainerContainer @@ -55666,7 +55667,7 @@ entities: - type: Label currentLabel: вино - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.19528572 + sprayFizzinessThresholdRoll: 0.117122024 - type: NameModifier baseName: особое двухбородое бородатое вино - type: ContainerContainer @@ -59549,7 +59550,7 @@ entities: pos: 39.5,13.5 parent: 2 - type: Door - secondsUntilStateChange: -51279.234 + secondsUntilStateChange: -51309.305 state: Closing - type: DeviceNetwork deviceLists: @@ -109995,212 +109996,6 @@ entities: - type: Transform pos: 62.5,41.5 parent: 2 -- proto: TowelColorBlack - entities: - - uid: 7028 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7052 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorBlue - entities: - - uid: 7029 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7030 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7053 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7054 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorBrown - entities: - - uid: 7031 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7055 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorDarkBlue - entities: - - uid: 7032 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7056 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorDarkGreen - entities: - - uid: 7033 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7057 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorGold - entities: - - uid: 7034 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7058 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorGray - entities: - - uid: 7035 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7059 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorGreen - entities: - - uid: 7036 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7060 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorLightBlue - entities: - - uid: 7037 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7061 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorLightBrown - entities: - - uid: 7038 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7062 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorMaroon - entities: - - uid: 7039 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7063 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorMime - entities: - - uid: 7040 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7064 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: TowelColorNT entities: - uid: 7041 @@ -110217,86 +110012,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: TowelColorOrange - entities: - - uid: 7042 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7066 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorPink - entities: - - uid: 7043 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7067 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorPurple - entities: - - uid: 7044 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7068 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorRed - entities: - - uid: 7045 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7069 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorSilver - entities: - - uid: 7046 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7070 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: TowelColorSyndicate entities: - uid: 7071 @@ -110306,22 +110021,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: TowelColorTeal - entities: - - uid: 7047 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7072 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: TowelColorWhite entities: - uid: 7048 @@ -110338,22 +110037,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: TowelColorYellow - entities: - - uid: 7049 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7074 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: ToyFigurineEngineer entities: - uid: 15711 @@ -123068,29 +122751,9 @@ entities: showEnts: False occludes: True ents: - - 7047 - - 7039 - - 7037 - - 7045 - 7041 - - 7031 - - 7040 - 7048 - - 7033 - 7027 - - 7046 - - 7032 - - 7029 - - 7035 - - 7043 - - 7034 - - 7036 - - 7044 - - 7042 - - 7049 - - 7030 - - 7038 - - 7028 paper_label: !type:ContainerSlot showEnts: False occludes: True @@ -123114,30 +122777,10 @@ entities: showEnts: False occludes: True ents: - - 7056 - - 7057 - - 7068 - - 7062 - 7051 - - 7070 - - 7067 - - 7054 - - 7052 - - 7053 - - 7059 - 7065 - - 7069 - - 7058 - - 7061 - 7071 - - 7072 - - 7060 - - 7066 - - 7055 - - 7074 - 7073 - - 7063 - - 7064 paper_label: !type:ContainerSlot showEnts: False occludes: True diff --git a/Resources/Maps/Corvax/corvax_pearl.yml b/Resources/Maps/Corvax/corvax_pearl.yml index fa22e6ffb4..4a65dd62b5 100644 --- a/Resources/Maps/Corvax/corvax_pearl.yml +++ b/Resources/Maps/Corvax/corvax_pearl.yml @@ -4,8 +4,8 @@ meta: engineVersion: 270.1.0 forkId: "" forkVersion: "" - time: 03/10/2026 11:10:13 - entityCount: 21696 + time: 04/15/2026 20:13:35 + entityCount: 21738 maps: - 1 grids: @@ -349,7 +349,7 @@ entities: version: 7 5,0: ind: 5,0 - tiles: BAAAAAAAAAEAAAAAAAAoAAAAAAAAgQAAAAAAAIEAAAAAAAAoAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAgQAAAAAAACgAAAAAAAAoAAAAAAAAgQAAAAAAACgAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAATAAAAAAAAEwAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAACBAAAAAAAAWAAAAAAAAFgAAAAAAABYAAAAAAAAgQAAAAAAAIEAAAAAAABMAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAgQAAAAAAAFgAAAAAAABYAAAAAAAAWAAAAAAAAFgAAAAAAACBAAAAAAAATAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAAIEAAAAAAABYAAAAAAAAWAAAAAAAAFgAAAAAAABYAAAAAAAAgQAAAAAAAEwAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAACgAAAAAAAIEAAAAAAAAKAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAWgAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== + tiles: BAAAAAAAAAEAAAAAAAAoAAAAAAAAgQAAAAAAAIEAAAAAAAAoAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAgQAAAAAAACgAAAAAAAAoAAAAAAAAgQAAAAAAACgAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAATAAAAAAAAEwAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAACBAAAAAAAAWAAAAAAAAFgAAAAAAABYAAAAAAAAgQAAAAAAAIEAAAAAAABMAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAgQAAAAAAAFgAAAAAAABYAAAAAAAAWAAAAAAAAFgAAAAAAACBAAAAAAAATAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAAIEAAAAAAABYAAAAAAAAWAAAAAAAAFgAAAAAAABYAAAAAAAAgQAAAAAAAEwAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAACgAAAAAAAIEAAAAAAAAKAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAWgAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== version: 7 5,1: ind: 5,1 @@ -369,7 +369,7 @@ entities: version: 7 6,1: ind: 6,1 - tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 4,-3: ind: 4,-3 @@ -389,7 +389,7 @@ entities: version: 7 6,0: ind: 6,0 - tiles: BAAAAAAAAAQAAAAAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAAA+AAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAPgAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: BAAAAAAAAAQAAAAAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAAA+AAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAPgAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 6,-1: ind: 6,-1 @@ -43391,22 +43391,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 11 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 101.5,21.5 - parent: 2 - - type: Fixtures - fixtures: {} - - uid: 12 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 101.5,19.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 13 components: - type: Transform @@ -43542,8 +43526,6 @@ entities: - 13632 - 13739 - 7174 - - 7167 - - 7175 - 7176 - 7168 - 7166 @@ -44872,6 +44854,22 @@ entities: - 13800 - type: Fixtures fixtures: {} + - uid: 11833 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 104.5,19.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 16991 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 104.5,21.5 + parent: 2 + - type: Fixtures + fixtures: {} - proto: AirAlarmFreezer entities: - uid: 112 @@ -45306,22 +45304,30 @@ entities: parent: 20154 - proto: AirlockExternalGlassAtmosphericsLocked entities: - - uid: 181 - components: - - type: Transform - pos: 97.5,21.5 - parent: 2 - - uid: 182 - components: - - type: Transform - pos: 97.5,19.5 - parent: 2 - uid: 183 components: - type: Transform rot: -1.5707963267948966 rad pos: 97.5,26.5 parent: 2 + - uid: 16992 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,15.5 + parent: 2 + - uid: 21727 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 100.5,19.5 + parent: 2 + - uid: 21728 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 100.5,21.5 + parent: 2 - proto: AirlockExternalGlassShuttleArrivals entities: - uid: 184 @@ -47000,6 +47006,11 @@ entities: - type: Transform pos: -10.444789,53.65996 parent: 2 + - uid: 13744 + components: + - type: Transform + pos: 100.33421,25.563883 + parent: 2 - proto: Ashtray entities: - uid: 391 @@ -48026,12 +48037,13 @@ entities: - uid: 544 components: - type: Transform + rot: 1.5707963267948966 rad pos: 97.5,31.5 parent: 2 - - uid: 545 + - uid: 16311 components: - type: Transform - rot: 3.141592653589793 rad + rot: 1.5707963267948966 rad pos: 96.5,12.5 parent: 2 - proto: BlastDoorBridgeOpen @@ -48193,6 +48205,24 @@ entities: - type: Transform pos: 27.5,28.5 parent: 2 + - uid: 14626 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 93.5,13.5 + parent: 2 + - uid: 16993 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 94.5,13.5 + parent: 2 + - uid: 18190 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 95.5,13.5 + parent: 2 - uid: 20214 components: - type: Transform @@ -48211,6 +48241,24 @@ entities: rot: 3.141592653589793 rad pos: 12.5,-9.5 parent: 20154 + - uid: 21734 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 96.5,30.5 + parent: 2 + - uid: 21735 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 95.5,30.5 + parent: 2 + - uid: 21736 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 94.5,30.5 + parent: 2 - proto: BlockGameArcade entities: - uid: 576 @@ -49128,6 +49176,18 @@ entities: rot: 1.5707963267948966 rad pos: -15.5,-23.5 parent: 2 + - uid: 14625 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,30.5 + parent: 2 + - uid: 16989 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 96.5,13.5 + parent: 2 - proto: ButtonFrameCautionSecurity entities: - uid: 694 @@ -49234,6 +49294,11 @@ entities: parent: 2 - proto: CableApcExtension entities: + - uid: 181 + components: + - type: Transform + pos: 101.5,20.5 + parent: 2 - uid: 709 components: - type: Transform @@ -57029,6 +57094,46 @@ entities: - type: Transform pos: -22.5,28.5 parent: 2 + - uid: 13943 + components: + - type: Transform + pos: 99.5,20.5 + parent: 2 + - uid: 14094 + components: + - type: Transform + pos: 103.5,20.5 + parent: 2 + - uid: 16285 + components: + - type: Transform + pos: 102.5,20.5 + parent: 2 + - uid: 17139 + components: + - type: Transform + pos: 100.5,20.5 + parent: 2 + - uid: 18138 + components: + - type: Transform + pos: 95.5,20.5 + parent: 2 + - uid: 18170 + components: + - type: Transform + pos: 97.5,20.5 + parent: 2 + - uid: 18189 + components: + - type: Transform + pos: 98.5,20.5 + parent: 2 + - uid: 18203 + components: + - type: Transform + pos: 96.5,20.5 + parent: 2 - uid: 20230 components: - type: Transform @@ -69118,6 +69223,12 @@ entities: buckledEntities: [] - proto: Catwalk entities: + - uid: 182 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,17.5 + parent: 2 - uid: 4260 components: - type: Transform @@ -70825,6 +70936,84 @@ entities: - type: Transform pos: 64.5,46.5 parent: 2 + - uid: 7167 + components: + - type: Transform + pos: 101.5,27.5 + parent: 2 + - uid: 11830 + components: + - type: Transform + pos: 100.5,25.5 + parent: 2 + - uid: 11835 + components: + - type: Transform + pos: 100.5,27.5 + parent: 2 + - uid: 12126 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,26.5 + parent: 2 + - uid: 13441 + components: + - type: Transform + pos: 101.5,26.5 + parent: 2 + - uid: 13454 + components: + - type: Transform + pos: 100.5,26.5 + parent: 2 + - uid: 13604 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,27.5 + parent: 2 + - uid: 13745 + components: + - type: Transform + pos: 101.5,25.5 + parent: 2 + - uid: 13747 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,25.5 + parent: 2 + - uid: 13942 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,18.5 + parent: 2 + - uid: 14095 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,21.5 + parent: 2 + - uid: 14920 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,23.5 + parent: 2 + - uid: 16272 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,20.5 + parent: 2 + - uid: 16276 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,22.5 + parent: 2 - uid: 20617 components: - type: Transform @@ -71364,6 +71553,18 @@ entities: rot: 3.141592653589793 rad pos: 86.894714,26.663887 parent: 2 + - uid: 11834 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 99.5,25.5 + parent: 2 + - uid: 13749 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,25.5 + parent: 2 - uid: 20628 components: - type: Transform @@ -72800,6 +73001,16 @@ entities: - type: Transform pos: -15.240647,-37.425087 parent: 2 + - uid: 13443 + components: + - type: Transform + pos: 100.55296,25.548258 + parent: 2 + - uid: 13746 + components: + - type: Transform + pos: 100.50609,25.657633 + parent: 2 - uid: 20642 components: - type: Transform @@ -74929,6 +75140,17 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-24.5 parent: 2 + - uid: 5169 + components: + - type: Transform + pos: 20.5,27.5 + parent: 2 + - uid: 5170 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 27.5,-46.5 + parent: 2 - proto: ComputerCriminalRecords entities: - uid: 5157 @@ -75015,19 +75237,6 @@ entities: rot: 1.5707963267948966 rad pos: 17.5,23.5 parent: 2 -- proto: ComputerMedicalRecords - entities: - - uid: 5169 - components: - - type: Transform - pos: 20.5,27.5 - parent: 2 - - uid: 5170 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 27.5,-46.5 - parent: 2 - proto: ComputerPowerMonitoring entities: - uid: 5171 @@ -75280,6 +75489,12 @@ entities: - type: Transform pos: 0.5,60.5 parent: 2 + - uid: 21737 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,48.5 + parent: 2 - proto: CorporateCircuitBoard entities: - uid: 5209 @@ -76479,10 +76694,8 @@ entities: - uid: 5394 components: - type: Transform - pos: 94.5,21.5 + pos: 96.5,20.5 parent: 2 - - type: WarpPoint - location: Атмос - proto: DefaultStationBeaconBar entities: - uid: 5395 @@ -82722,6 +82935,16 @@ entities: - type: Transform pos: 26.461136,-53.451836 parent: 2 + - uid: 13453 + components: + - type: Transform + pos: 100.81859,25.548258 + parent: 2 + - uid: 18213 + components: + - type: Transform + pos: 100.25609,25.720133 + parent: 2 - proto: DrinkMilkCarton entities: - uid: 6410 @@ -88044,15 +88267,6 @@ entities: deviceLists: - 21 - 13 - - uid: 7167 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 97.5,21.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 21 - uid: 7168 components: - type: Transform @@ -88119,15 +88333,6 @@ entities: - type: DeviceNetwork deviceLists: - 21 - - uid: 7175 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 97.5,19.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 21 - uid: 7176 components: - type: Transform @@ -89998,11 +90203,6 @@ entities: - type: Transform pos: 91.5,9.5 parent: 2 - - uid: 7400 - components: - - type: Transform - pos: 92.5,10.5 - parent: 2 - uid: 7401 components: - type: Transform @@ -109923,11 +110123,6 @@ entities: - type: Transform pos: 98.5,14.5 parent: 2 - - uid: 11001 - components: - - type: Transform - pos: 98.5,15.5 - parent: 2 - uid: 11002 components: - type: Transform @@ -112619,11 +112814,6 @@ entities: - type: Transform pos: 91.5,7.5 parent: 2 - - uid: 11481 - components: - - type: Transform - pos: 94.5,10.5 - parent: 2 - uid: 11482 components: - type: Transform @@ -112649,21 +112839,6 @@ entities: - type: Transform pos: 89.5,4.5 parent: 2 - - uid: 11487 - components: - - type: Transform - pos: 100.5,15.5 - parent: 2 - - uid: 11488 - components: - - type: Transform - pos: 101.5,15.5 - parent: 2 - - uid: 11489 - components: - - type: Transform - pos: 99.5,15.5 - parent: 2 - uid: 11490 components: - type: Transform @@ -112675,21 +112850,11 @@ entities: rot: -1.5707963267948966 rad pos: -9.5,25.5 parent: 2 - - uid: 11492 - components: - - type: Transform - pos: 93.5,10.5 - parent: 2 - uid: 11493 components: - type: Transform pos: 91.5,6.5 parent: 2 - - uid: 11494 - components: - - type: Transform - pos: 95.5,10.5 - parent: 2 - uid: 11495 components: - type: Transform @@ -116315,21 +116480,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 11816 - components: - - type: Transform - pos: 99.5,20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 11817 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 97.5,20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - uid: 11818 components: - type: Transform @@ -116403,69 +116553,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 11828 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 97.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 11829 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 98.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 11830 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 97.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 11831 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 99.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 11832 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 99.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 11833 - components: - - type: Transform - pos: 99.5,24.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 11834 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 97.5,24.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 11835 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 98.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - uid: 11836 components: - type: Transform @@ -118697,6 +118784,76 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' + - uid: 14792 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 16279 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 102.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 17955 + components: + - type: Transform + pos: 102.5,20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18191 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 100.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18205 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 101.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18324 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,24.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19722 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 101.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19723 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 102.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20016 + components: + - type: Transform + pos: 102.5,24.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - uid: 21069 components: - type: Transform @@ -118738,20 +118895,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF1212FF' - - uid: 12126 - components: - - type: Transform - pos: 97.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 12127 - components: - - type: Transform - pos: 97.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - uid: 12128 components: - type: Transform @@ -118878,6 +119021,13 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' + - uid: 18141 + components: + - type: Transform + pos: 100.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' - uid: 21071 components: - type: Transform @@ -129200,61 +129350,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 13437 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13438 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13439 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13440 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 97.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13441 - components: - - type: Transform - pos: 98.5,20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13442 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13443 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - uid: 13444 components: - type: Transform @@ -129318,45 +129413,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 13452 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13453 - components: - - type: Transform - pos: 98.5,24.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13454 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 97.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13455 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13456 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - uid: 13457 components: - type: Transform @@ -130398,6 +130454,116 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF1212FF' + - uid: 16990 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 101.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 17954 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18136 + components: + - type: Transform + pos: 101.5,20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18204 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18208 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 101.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18210 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18381 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 100.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 18409 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 101.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 18410 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 101.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19717 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19718 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19720 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19721 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20015 + components: + - type: Transform + pos: 101.5,24.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - uid: 21121 components: - type: Transform @@ -130510,22 +130676,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#03FCD3FF' - - uid: 13604 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 96.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13605 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 96.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - uid: 13606 components: - type: Transform @@ -130542,6 +130692,22 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF1212FF' + - uid: 14820 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 99.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 21726 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 99.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - proto: GasThermoMachineFreezer entities: - uid: 13608 @@ -130556,6 +130722,11 @@ entities: - type: Transform pos: 49.5,76.5 parent: 2 + - uid: 21729 + components: + - type: Transform + pos: 99.5,23.5 + parent: 2 - proto: GasThermoMachineFreezerEnabled entities: - uid: 13610 @@ -130566,6 +130737,13 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' +- proto: GasThermoMachineHeater + entities: + - uid: 21730 + components: + - type: Transform + pos: 98.5,23.5 + parent: 2 - proto: GasValve entities: - uid: 13611 @@ -131923,150 +132101,6 @@ entities: - 21 - type: AtmosPipeColor color: '#FF1212FF' - - uid: 13740 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13741 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13742 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13743 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13744 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13745 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13746 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13747 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13748 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13749 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13750 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13751 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13752 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13753 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13754 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13755 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13756 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13757 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - uid: 13758 components: - type: Transform @@ -133042,6 +133076,110 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF1212FF' + - uid: 14833 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18214 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18216 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18217 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18218 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18265 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18288 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18298 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18315 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 20017 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20018 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20019 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20020 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - uid: 21124 components: - type: Transform @@ -133092,6 +133230,46 @@ entities: parent: 20154 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 21721 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 21722 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 21723 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 21724 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 21725 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - proto: GasVentScrubberFreezer entities: - uid: 13851 @@ -133187,6 +133365,26 @@ entities: parent: 20154 - proto: Grille entities: + - uid: 12 + components: + - type: Transform + pos: 100.5,18.5 + parent: 2 + - uid: 11828 + components: + - type: Transform + pos: 100.5,17.5 + parent: 2 + - uid: 13755 + components: + - type: Transform + pos: 100.5,22.5 + parent: 2 + - uid: 13756 + components: + - type: Transform + pos: 100.5,23.5 + parent: 2 - uid: 13859 components: - type: Transform @@ -133612,16 +133810,6 @@ entities: - type: Transform pos: 88.5,11.5 parent: 2 - - uid: 13942 - components: - - type: Transform - pos: 97.5,18.5 - parent: 2 - - uid: 13943 - components: - - type: Transform - pos: 97.5,23.5 - parent: 2 - uid: 13944 components: - type: Transform @@ -134406,16 +134594,6 @@ entities: - type: Transform pos: 91.5,20.5 parent: 2 - - uid: 14094 - components: - - type: Transform - pos: 97.5,22.5 - parent: 2 - - uid: 14095 - components: - - type: Transform - pos: 97.5,17.5 - parent: 2 - uid: 14096 components: - type: Transform @@ -136174,7 +136352,7 @@ entities: - uid: 14382 components: - type: Transform - pos: 95.5,21.5 + pos: 94.5,24.5 parent: 2 - proto: HolopadEngineeringFront entities: @@ -137798,31 +137976,65 @@ entities: fixtures: {} - proto: LockableButtonAtmospherics entities: - - uid: 14625 + - uid: 545 components: - - type: MetaData - desc: Эта кнопка переключает гермозатвор камеры смешивания. - - type: Transform - pos: 96.5,28.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 544: - - - Pressed - - Toggle - - type: Fixtures - fixtures: {} - - uid: 14626 - components: - - type: MetaData - desc: Эта кнопка переключает гермозатвор камеры смешивания. - type: Transform rot: 3.141592653589793 rad pos: 96.5,15.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 545: + 14626: + - - Pressed + - Toggle + 16993: + - - Pressed + - Toggle + 18190: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 16296 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 96.5,13.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 16311: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 21732 + components: + - type: Transform + pos: 96.5,28.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 21734: + - - Pressed + - Toggle + 21735: + - - Pressed + - Toggle + 21736: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 21733 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,30.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 544: - - Pressed - Toggle - type: Fixtures @@ -139457,11 +139669,6 @@ entities: parent: 2 - proto: NitrogenCanister entities: - - uid: 14792 - components: - - type: Transform - pos: 96.5,18.5 - parent: 2 - uid: 14793 components: - type: Transform @@ -139654,11 +139861,6 @@ entities: parent: 2 - proto: OxygenCanister entities: - - uid: 14820 - components: - - type: Transform - pos: 96.5,22.5 - parent: 2 - uid: 14821 components: - type: Transform @@ -139743,34 +139945,6 @@ entities: "я устал, я ухожу" и ухожу я не просто так, а потому что я правда устал. - - uid: 14833 - components: - - type: Transform - pos: 96.51457,20.54572 - parent: 2 - - type: Paper - content: >- - Добрый сосед атмос с прошлой смены передает привет! - - - Так как данная станция окружена нормальной атмосферой, а газодобытчиков кроме плазмы нам и не установили, мной было принято решение соорудить свой газодобытчик на основе скрубберов! - - - Вот только настроить я его не успел, срочно собрали на эвакуацию по неизвестным причинам, а потому пишу данное послание перед отлетом! - - - Настройки камер достаточно простые! - - 1) Подключите скрубберы к воздушной сигнализации своей камеры. - - 2) Настройте фильтрацию скрубберов на кислород и азот соответственно. - - 3) Наслаждайтесь стабильным потоком свежих газов прямиком из атмосферы! - - - При желании вы даже можете подключить её к системе проложенной дистры, какой-то безумец проложил её по всей станции!!! - - [italic]Приятной вам смены, от доброго соседа атмоса.[/italic] - uid: 14834 components: - type: Transform @@ -140206,6 +140380,13 @@ entities: - type: Transform pos: -12.382263,-13.281158 parent: 20154 +- proto: PartRodMetal + entities: + - uid: 17953 + components: + - type: Transform + pos: 102.46753,26.483398 + parent: 2 - proto: Pen entities: - uid: 14881 @@ -141466,11 +141647,11 @@ entities: parent: 2 - proto: PoweredDimSmallLight entities: - - uid: 15065 + - uid: 14919 components: - type: Transform rot: 3.141592653589793 rad - pos: 94.5,12.5 + pos: 94.5,11.5 parent: 2 - uid: 15066 components: @@ -141611,12 +141792,6 @@ entities: rot: -1.5707963267948966 rad pos: 96.5,25.5 parent: 2 - - uid: 15090 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 96.5,20.5 - parent: 2 - uid: 15091 components: - type: Transform @@ -144103,6 +144278,18 @@ entities: parent: 2 - proto: Railing entities: + - uid: 13456 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,25.5 + parent: 2 + - uid: 15065 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 101.5,27.5 + parent: 2 - uid: 15454 components: - type: Transform @@ -145153,8 +145340,8 @@ entities: - uid: 15635 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,25.5 + rot: 3.141592653589793 rad + pos: 100.5,27.5 parent: 2 - uid: 15636 components: @@ -145344,18 +145531,6 @@ entities: rot: 3.141592653589793 rad pos: 34.5,12.5 parent: 2 - - uid: 15669 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,27.5 - parent: 2 - - uid: 15670 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,26.5 - parent: 2 - uid: 15671 components: - type: Transform @@ -145960,6 +146135,12 @@ entities: parent: 20154 - proto: RailingCorner entities: + - uid: 13743 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,27.5 + parent: 2 - uid: 15727 components: - type: Transform @@ -146351,6 +146532,12 @@ entities: parent: 20154 - proto: RailingCornerSmall entities: + - uid: 15090 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 99.5,27.5 + parent: 2 - uid: 15792 components: - type: Transform @@ -148995,6 +149182,23 @@ entities: parent: 2 - proto: ReinforcedPlasmaWindow entities: + - uid: 15669 + components: + - type: Transform + pos: 95.5,30.5 + parent: 2 + - uid: 15670 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 94.5,13.5 + parent: 2 + - uid: 16233 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 95.5,13.5 + parent: 2 - uid: 16235 components: - type: Transform @@ -149025,8 +149229,44 @@ entities: rot: -1.5707963267948966 rad pos: 83.5,10.5 parent: 2 + - uid: 16259 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 93.5,13.5 + parent: 2 + - uid: 16270 + components: + - type: Transform + pos: 94.5,30.5 + parent: 2 + - uid: 16271 + components: + - type: Transform + pos: 96.5,30.5 + parent: 2 - proto: ReinforcedWindow entities: + - uid: 13439 + components: + - type: Transform + pos: 100.5,23.5 + parent: 2 + - uid: 13452 + components: + - type: Transform + pos: 100.5,18.5 + parent: 2 + - uid: 13605 + components: + - type: Transform + pos: 100.5,22.5 + parent: 2 + - uid: 13757 + components: + - type: Transform + pos: 100.5,17.5 + parent: 2 - uid: 16240 components: - type: Transform @@ -149127,11 +149367,6 @@ entities: - type: Transform pos: 66.5,84.5 parent: 2 - - uid: 16259 - components: - - type: Transform - pos: 95.5,30.5 - parent: 2 - uid: 16260 components: - type: Transform @@ -149182,21 +149417,6 @@ entities: - type: Transform pos: 95.5,15.5 parent: 2 - - uid: 16270 - components: - - type: Transform - pos: 94.5,13.5 - parent: 2 - - uid: 16271 - components: - - type: Transform - pos: 93.5,13.5 - parent: 2 - - uid: 16272 - components: - - type: Transform - pos: 95.5,13.5 - parent: 2 - uid: 16273 components: - type: Transform @@ -149214,11 +149434,6 @@ entities: rot: 3.141592653589793 rad pos: -11.5,25.5 parent: 2 - - uid: 16276 - components: - - type: Transform - pos: 96.5,30.5 - parent: 2 - uid: 16277 components: - type: Transform @@ -149229,11 +149444,6 @@ entities: - type: Transform pos: 63.5,24.5 parent: 2 - - uid: 16279 - components: - - type: Transform - pos: 94.5,30.5 - parent: 2 - uid: 16280 components: - type: Transform @@ -149262,21 +149472,11 @@ entities: - type: Transform pos: 93.5,15.5 parent: 2 - - uid: 16285 - components: - - type: Transform - pos: 97.5,18.5 - parent: 2 - uid: 16286 components: - type: Transform pos: 86.5,11.5 parent: 2 - - uid: 16287 - components: - - type: Transform - pos: 97.5,17.5 - parent: 2 - uid: 16288 components: - type: Transform @@ -149318,11 +149518,6 @@ entities: rot: 3.141592653589793 rad pos: -7.5,59.5 parent: 2 - - uid: 16296 - components: - - type: Transform - pos: 97.5,23.5 - parent: 2 - uid: 16297 components: - type: Transform @@ -149399,11 +149594,6 @@ entities: - type: Transform pos: 86.5,19.5 parent: 2 - - uid: 16311 - components: - - type: Transform - pos: 97.5,22.5 - parent: 2 - uid: 16312 components: - type: Transform @@ -156759,6 +156949,12 @@ entities: parent: 2 - type: SurveillanceCamera id: Брифинговая + - uid: 21731 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 99.5,20.5 + parent: 2 - proto: SurveillanceCameraGeneral entities: - uid: 17380 @@ -157830,6 +158026,12 @@ entities: parent: 2 - proto: Table entities: + - uid: 13442 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,25.5 + parent: 2 - uid: 17491 components: - type: Transform @@ -160661,10 +160863,9 @@ entities: - Reverse - - Middle - Off - - uid: 17954 + - uid: 16287 components: - type: Transform - rot: 3.141592653589793 rad pos: -13.5,47.5 parent: 2 - type: DeviceLinkSource @@ -160676,7 +160877,7 @@ entities: - Forward - - Middle - Off - 16234: + 21737: - - Left - Reverse - - Right @@ -160704,6 +160905,13 @@ entities: - Forward - - Middle - Off + 16234: + - - Right + - Reverse + - - Left + - Forward + - - Middle + - Off - proto: UniformPrinter entities: - uid: 17956 @@ -161540,6 +161748,11 @@ entities: fixtures: {} - proto: WallReinforced entities: + - uid: 11 + components: + - type: Transform + pos: 101.5,20.5 + parent: 2 - uid: 65 components: - type: Transform @@ -161555,6 +161768,17 @@ entities: - type: Transform pos: -0.5,58.5 parent: 2 + - uid: 7175 + components: + - type: Transform + pos: 104.5,18.5 + parent: 2 + - uid: 7400 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 92.5,10.5 + parent: 2 - uid: 8829 components: - type: Transform @@ -161575,6 +161799,139 @@ entities: - type: Transform pos: -0.5,60.5 parent: 2 + - uid: 11001 + components: + - type: Transform + pos: 99.5,15.5 + parent: 2 + - uid: 11481 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 94.5,10.5 + parent: 2 + - uid: 11487 + components: + - type: Transform + pos: 97.5,24.5 + parent: 2 + - uid: 11488 + components: + - type: Transform + pos: 98.5,24.5 + parent: 2 + - uid: 11489 + components: + - type: Transform + pos: 101.5,24.5 + parent: 2 + - uid: 11492 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 93.5,10.5 + parent: 2 + - uid: 11494 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 95.5,10.5 + parent: 2 + - uid: 11816 + components: + - type: Transform + pos: 103.5,16.5 + parent: 2 + - uid: 11817 + components: + - type: Transform + pos: 104.5,16.5 + parent: 2 + - uid: 11829 + components: + - type: Transform + pos: 99.5,24.5 + parent: 2 + - uid: 11831 + components: + - type: Transform + pos: 103.5,24.5 + parent: 2 + - uid: 11832 + components: + - type: Transform + pos: 104.5,20.5 + parent: 2 + - uid: 12127 + components: + - type: Transform + pos: 104.5,17.5 + parent: 2 + - uid: 13437 + components: + - type: Transform + pos: 102.5,16.5 + parent: 2 + - uid: 13438 + components: + - type: Transform + pos: 101.5,16.5 + parent: 2 + - uid: 13440 + components: + - type: Transform + pos: 103.5,20.5 + parent: 2 + - uid: 13455 + components: + - type: Transform + pos: 104.5,23.5 + parent: 2 + - uid: 13740 + components: + - type: Transform + pos: 104.5,24.5 + parent: 2 + - uid: 13741 + components: + - type: Transform + pos: 102.5,20.5 + parent: 2 + - uid: 13742 + components: + - type: Transform + pos: 100.5,24.5 + parent: 2 + - uid: 13748 + components: + - type: Transform + pos: 100.5,15.5 + parent: 2 + - uid: 13750 + components: + - type: Transform + pos: 104.5,22.5 + parent: 2 + - uid: 13751 + components: + - type: Transform + pos: 104.5,21.5 + parent: 2 + - uid: 13752 + components: + - type: Transform + pos: 104.5,19.5 + parent: 2 + - uid: 13753 + components: + - type: Transform + pos: 100.5,16.5 + parent: 2 + - uid: 13754 + components: + - type: Transform + pos: 100.5,20.5 + parent: 2 - uid: 18067 components: - type: Transform @@ -161935,21 +162292,11 @@ entities: rot: -1.5707963267948966 rad pos: 81.5,81.5 parent: 2 - - uid: 18136 - components: - - type: Transform - pos: 95.5,11.5 - parent: 2 - uid: 18137 components: - type: Transform pos: 96.5,11.5 parent: 2 - - uid: 18138 - components: - - type: Transform - pos: 93.5,11.5 - parent: 2 - uid: 18139 components: - type: Transform @@ -161960,11 +162307,6 @@ entities: - type: Transform pos: 96.5,13.5 parent: 2 - - uid: 18141 - components: - - type: Transform - pos: 94.5,11.5 - parent: 2 - uid: 18142 components: - type: Transform @@ -162108,11 +162450,6 @@ entities: - type: Transform pos: 89.5,12.5 parent: 2 - - uid: 18170 - components: - - type: Transform - pos: 98.5,24.5 - parent: 2 - uid: 18171 components: - type: Transform @@ -162201,24 +162538,8 @@ entities: - uid: 18188 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,20.5 - parent: 2 - - uid: 18189 - components: - - type: Transform - pos: 101.5,21.5 - parent: 2 - - uid: 18190 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 100.5,20.5 - parent: 2 - - uid: 18191 - components: - - type: Transform - pos: 101.5,22.5 + rot: -1.5707963267948966 rad + pos: 96.5,10.5 parent: 2 - uid: 18192 components: @@ -162277,46 +162598,21 @@ entities: - type: Transform pos: 85.5,13.5 parent: 2 - - uid: 18203 - components: - - type: Transform - pos: 101.5,20.5 - parent: 2 - - uid: 18204 - components: - - type: Transform - pos: 99.5,16.5 - parent: 2 - - uid: 18205 - components: - - type: Transform - pos: 101.5,16.5 - parent: 2 - uid: 18206 components: - type: Transform - pos: 100.5,16.5 + pos: 98.5,15.5 parent: 2 - uid: 18207 components: - type: Transform - pos: 101.5,19.5 - parent: 2 - - uid: 18208 - components: - - type: Transform - pos: 99.5,24.5 + pos: 84.5,10.5 parent: 2 - uid: 18209 components: - type: Transform pos: 93.5,30.5 parent: 2 - - uid: 18210 - components: - - type: Transform - pos: 101.5,18.5 - parent: 2 - uid: 18211 components: - type: Transform @@ -162325,39 +162621,13 @@ entities: - uid: 18212 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,20.5 - parent: 2 - - uid: 18213 - components: - - type: Transform - pos: 101.5,23.5 - parent: 2 - - uid: 18214 - components: - - type: Transform - pos: 101.5,17.5 + pos: 102.5,24.5 parent: 2 - uid: 18215 components: - type: Transform pos: 90.5,13.5 parent: 2 - - uid: 18216 - components: - - type: Transform - pos: 98.5,16.5 - parent: 2 - - uid: 18217 - components: - - type: Transform - pos: 101.5,24.5 - parent: 2 - - uid: 18218 - components: - - type: Transform - pos: 100.5,24.5 - parent: 2 - uid: 18219 components: - type: Transform @@ -162588,11 +162858,6 @@ entities: - type: Transform pos: 60.5,81.5 parent: 2 - - uid: 18265 - components: - - type: Transform - pos: 97.5,16.5 - parent: 2 - uid: 18266 components: - type: Transform @@ -162761,11 +163026,6 @@ entities: - type: Transform pos: 84.5,30.5 parent: 2 - - uid: 18298 - components: - - type: Transform - pos: 97.5,15.5 - parent: 2 - uid: 18299 components: - type: Transform @@ -162846,11 +163106,6 @@ entities: - type: Transform pos: 79.5,11.5 parent: 2 - - uid: 18315 - components: - - type: Transform - pos: 84.5,10.5 - parent: 2 - uid: 18316 components: - type: Transform @@ -162891,11 +163146,6 @@ entities: - type: Transform pos: 97.5,28.5 parent: 2 - - uid: 18324 - components: - - type: Transform - pos: 97.5,20.5 - parent: 2 - uid: 18325 components: - type: Transform @@ -163177,11 +163427,6 @@ entities: rot: -1.5707963267948966 rad pos: 96.5,28.5 parent: 2 - - uid: 18381 - components: - - type: Transform - pos: 97.5,24.5 - parent: 2 - uid: 18382 components: - type: Transform @@ -171448,26 +171693,6 @@ entities: showEnts: False occludes: True ent: null -- proto: WarningN2 - entities: - - uid: 19717 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 97.5,16.5 - parent: 2 - - type: Fixtures - fixtures: {} -- proto: WarningO2 - entities: - - uid: 19718 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 97.5,24.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: WarningPlasma entities: - uid: 19719 diff --git a/Resources/Maps/Corvax/corvax_silly.yml b/Resources/Maps/Corvax/corvax_silly.yml index 0d58c69d0b..7cb40d3e76 100644 --- a/Resources/Maps/Corvax/corvax_silly.yml +++ b/Resources/Maps/Corvax/corvax_silly.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 267.3.0 + engineVersion: 270.1.0 forkId: "" forkVersion: "" - time: 11/08/2025 06:48:43 - entityCount: 8631 + time: 04/18/2026 07:55:59 + entityCount: 8634 maps: - 1 grids: @@ -38,7 +38,6 @@ tilemap: 22: FloorBrokenWood 28: FloorClown 29: FloorConcrete - 18: FloorConcreteOutside 32: FloorDark 33: FloorDarkDiagonal 34: FloorDarkDiagonalMini @@ -53,9 +52,7 @@ tilemap: 53: FloorGrassLight 54: FloorGrayConcrete 55: FloorGrayConcreteMono - 5: FloorGrayConcreteOutside - 6: FloorGrayConcreteOutsideSmooth - 56: FloorGrayConcreteSmooth + 6: FloorGrayConcreteSmooth 61: FloorHydro 20: FloorIce 23: FloorJungleAstroGrass @@ -159,7 +156,7 @@ entities: version: 7 -1,-1: ind: -1,-1 - tiles: fgAAAAAAAH0AAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAIABgAAAAACAH0AAAAAAAB9AAAAAAAABgAAAAAAAAYAAAAAAwAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAADAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAgAGAAAAAAIABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAGAAAAAAMAfgAAAAAAAAUAAAAAAAAGAAAAAAIABgAAAAABAAUAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAABgAAAAADAH4AAAAAAAAFAAAAAAAABgAAAAADAAYAAAAAAQAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAYAAAAAAgB+AAAAAAAABQAAAAAAAAYAAAAAAQAGAAAAAAIABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAGAAAAAAIABQAAAAABAAUAAAAAAQAGAAAAAAEABgAAAAACAAUAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAABgAAAAABAAYAAAAAAQAGAAAAAAEABgAAAAADAAYAAAAAAwAFAAAAAAMABQAAAAADAAUAAAAAAgAFAAAAAAAABQAAAAADAAUAAAAAAAAFAAAAAAMABQAAAAABAAUAAAAAAgAFAAAAAAMABQAAAAADAAYAAAAAAwAGAAAAAAMABgAAAAABAAYAAAAAAAAGAAAAAAMABgAAAAACAAYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAQAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAABAAYAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAgAGAAAAAAIABgAAAAADAAYAAAAAAwAGAAAAAAIABgAAAAACAAYAAAAAAwAGAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAMABgAAAAADADgAAAAAAgCBAAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAgQAAAAAAAHMAAAAAAAB3AAAAAAAAeQAAAAAAAGAAAAAAAgBgAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAIEAAAAAAABzAAAAAAAAdwAAAAAAAHcAAAAAAABgAAAAAAEAawAAAAABAIEAAAAAAABSAAAAAAAAUgAAAAAAAIEAAAAAAABgAAAAAAAAOAAAAAAAADgAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAACBAAAAAAAAcwAAAAAAAHcAAAAAAAB3AAAAAAAAagAAAAABAGAAAAAAAgCBAAAAAAAAUgAAAAAAAFIAAAAAAABgAAAAAAMAYAAAAAAAADgAAAAAAAA4AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAwBgAAAAAAEAgQAAAAAAAFIAAAAAAABSAAAAAAAAgQAAAAAAAGAAAAAAAgA2AAAAAAIANgAAAAAAADYAAAAAAACBAAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAMAfAAAAAADAHwAAAAAAgBgAAAAAAMAawAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAH0AAAAAAgB8AAAAAAMAfAAAAAAAAHwAAAAAAgB8AAAAAAMAYAAAAAAAAGoAAAAAAQCBAAAAAAAAYAAAAAACAGAAAAAAAwCBAAAAAAAAYAAAAAADAA== + tiles: fgAAAAAAAH0AAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAIABgAAAAACAH0AAAAAAAB9AAAAAAAABgAAAAAAAAYAAAAAAwA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAADAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAgAGAAAAAAIANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAGAAAAAAMAfgAAAAAAADYAAAAAAAAGAAAAAAIABgAAAAABADYAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAABgAAAAADAH4AAAAAAAA2AAAAAAAABgAAAAADAAYAAAAAAQA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACAAYAAAAAAgB+AAAAAAAANgAAAAAAAAYAAAAAAQAGAAAAAAIANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAGAAAAAAIANgAAAAABADYAAAAAAQAGAAAAAAEABgAAAAACADYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAABgAAAAABAAYAAAAAAQAGAAAAAAEABgAAAAADAAYAAAAAAwA2AAAAAAMANgAAAAADADYAAAAAAgA2AAAAAAAANgAAAAADADYAAAAAAAA2AAAAAAMANgAAAAABADYAAAAAAgA2AAAAAAMANgAAAAADAAYAAAAAAwAGAAAAAAMABgAAAAABAAYAAAAAAAAGAAAAAAMABgAAAAACAAYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAQAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAABAAYAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAgAGAAAAAAIABgAAAAADAAYAAAAAAwAGAAAAAAIABgAAAAACAAYAAAAAAwAGAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAMABgAAAAADAAYAAAAAAgCBAAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAgQAAAAAAAHMAAAAAAAB3AAAAAAAAeQAAAAAAAGAAAAAAAgBgAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAIEAAAAAAABzAAAAAAAAdwAAAAAAAHcAAAAAAABgAAAAAAEAawAAAAABAIEAAAAAAABSAAAAAAAAUgAAAAAAAIEAAAAAAABgAAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAACBAAAAAAAAcwAAAAAAAHcAAAAAAAB3AAAAAAAAagAAAAABAGAAAAAAAgCBAAAAAAAAUgAAAAAAAFIAAAAAAABgAAAAAAMAYAAAAAAAAAYAAAAAAAAGAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAwBgAAAAAAEAgQAAAAAAAFIAAAAAAABSAAAAAAAAgQAAAAAAAGAAAAAAAgA2AAAAAAIANgAAAAAAADYAAAAAAACBAAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAMAfAAAAAADAHwAAAAAAgBgAAAAAAMAawAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAH0AAAAAAgB8AAAAAAMAfAAAAAAAAHwAAAAAAgB8AAAAAAMAYAAAAAAAAGoAAAAAAQCBAAAAAAAAYAAAAAACAGAAAAAAAwCBAAAAAAAAYAAAAAADAA== version: 7 0,-1: ind: 0,-1 @@ -171,31 +168,31 @@ entities: version: 7 1,0: ind: 1,0 - tiles: YAAAAAAAAGAAAAAAAwCBAAAAAAAAYAAAAAAAAGAAAAAAAQBgAAAAAAIAYAAAAAACAGAAAAAAAgBgAAAAAAMAYAAAAAACAGAAAAAAAgBgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABsAAAAAAAAYAAAAAAAAGAAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAbAAAAAAAAGwAAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAwCBAAAAAAAAYAAAAAACAGAAAAAAAgBgAAAAAAMAgQAAAAAAAGAAAAAAAQCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABqAAAAAAEAgQAAAAAAAIEAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAAAAAUAAAAAAQBqAAAAAAMAagAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAACBAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAQAGAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAABAGAAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAAABgAAAAADAH0AAAAAAQB9AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGoAAAAAAwBqAAAAAAEAawAAAAACAGAAAAAAAQCBAAAAAAAABgAAAAADAAYAAAAAAgAGAAAAAAMABgAAAAACAAYAAAAAAAB9AAAAAAMAfQAAAAADAIEAAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAABgAAAAAAAAYAAAAAABAGAAAAAAAQBgAAAAAAMAgQAAAAAAAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAQAGAAAAAAIAYAAAAAADAIEAAAAAAACBAAAAAAAAfQAAAAAAAH0AAAAAAwB9AAAAAAIAYAAAAAABAGAAAAAAAwBgAAAAAAMAagAAAAACAIEAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAQAGAAAAAAIABgAAAAADAGAAAAAAAQBgAAAAAAEAagAAAAABAGoAAAAAAQBrAAAAAAMAYAAAAAAAAGAAAAAAAQBgAAAAAAEAYAAAAAADAGsAAAAAAAA2AAAAAAEABgAAAAADAAYAAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAQBgAAAAAAMAYAAAAAACAGAAAAAAAwBgAAAAAAAAYAAAAAAAAGoAAAAAAABrAAAAAAAAYAAAAAAAAGAAAAAAAwBgAAAAAAMAgQAAAAAAAAYAAAAAAQAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAMAfQAAAAABAH0AAAAAAwB9AAAAAAEAagAAAAAAAFAAAAAAAwBoAAAAAAEAagAAAAACAFAAAAAAAQBrAAAAAAIAYAAAAAABADYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAIABgAAAAAAAH0AAAAAAwB9AAAAAAMAfQAAAAABAFAAAAAAAQBqAAAAAAIAUAAAAAACAGgAAAAAAABoAAAAAAAAagAAAAACAGoAAAAAAACBAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAMABgAAAAACAAYAAAAAAAB9AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACkAAAAAAgCBAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAQAGAAAAAAMAfQAAAAAAAH0AAAAAAAB9AAAAAAAAKQAAAAABAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAADAAYAAAAAAgAGAAAAAAAABgAAAAACAA== + tiles: YAAAAAAAAGAAAAAAAwCBAAAAAAAAYAAAAAAAAGAAAAAAAQBgAAAAAAIAYAAAAAACAGAAAAAAAgBgAAAAAAMAYAAAAAACAGAAAAAAAgBgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABsAAAAAAAAYAAAAAAAAGAAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAbAAAAAAAAGwAAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAwCBAAAAAAAAYAAAAAACAGAAAAAAAgBgAAAAAAMAgQAAAAAAAGAAAAAAAQCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABqAAAAAAEAgQAAAAAAAIEAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAAAADYAAAAAAQBqAAAAAAMAagAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAACBAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAQAGAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAABAGAAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAAABgAAAAADAH0AAAAAAQB9AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGoAAAAAAwBqAAAAAAEAawAAAAACAGAAAAAAAQCBAAAAAAAABgAAAAADAAYAAAAAAgAGAAAAAAMABgAAAAACAAYAAAAAAAB9AAAAAAMAfQAAAAADAIEAAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAABgAAAAAAAAYAAAAAABAGAAAAAAAQBgAAAAAAMAgQAAAAAAAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAQAGAAAAAAIAYAAAAAADAIEAAAAAAACBAAAAAAAAfQAAAAAAAH0AAAAAAwB9AAAAAAIAYAAAAAABAGAAAAAAAwBgAAAAAAMAagAAAAACAIEAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAQAGAAAAAAIABgAAAAADAGAAAAAAAQBgAAAAAAEAagAAAAABAGoAAAAAAQBrAAAAAAMAYAAAAAAAAGAAAAAAAQBgAAAAAAEAYAAAAAADAGsAAAAAAAA2AAAAAAEABgAAAAADAAYAAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAQBgAAAAAAMAYAAAAAACAGAAAAAAAwBgAAAAAAAAYAAAAAAAAGoAAAAAAABrAAAAAAAAYAAAAAAAAGAAAAAAAwBgAAAAAAMAgQAAAAAAAAYAAAAAAQAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAMAfQAAAAABAH0AAAAAAwB9AAAAAAEAagAAAAAAAFAAAAAAAwBoAAAAAAEAagAAAAACAFAAAAAAAQBrAAAAAAIAYAAAAAABADYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAIABgAAAAAAAH0AAAAAAwB9AAAAAAMAfQAAAAABAFAAAAAAAQBqAAAAAAIAUAAAAAACAGgAAAAAAABoAAAAAAAAagAAAAACAGoAAAAAAACBAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAMABgAAAAACAAYAAAAAAAB9AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACkAAAAAAgCBAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAQAGAAAAAAMAfQAAAAAAAH0AAAAAAAB9AAAAAAAAKQAAAAABAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAADAAYAAAAAAgAGAAAAAAAABgAAAAACAA== version: 7 -1,1: ind: -1,1 - tiles: KQAAAAADACkAAAAAAwAkAAAAAAMAJAAAAAACACkAAAAAAgAXAAAAAAAAFwAAAAAAAGsAAAAAAQBnAAAAAAAAZwAAAAAAAGAAAAAAAwBgAAAAAAAAYAAAAAABAGAAAAAAAwBgAAAAAAEAgQAAAAAAACkAAAAAAQApAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAJAAAAAACAIEAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAkAAAAAAMAgQAAAAAAAIEAAAAAAAApAAAAAAEAKQAAAAACACkAAAAAAQApAAAAAAMAKQAAAAABACUAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAXAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAIAKQAAAAADACkAAAAAAQApAAAAAAAAKQAAAAADACQAAAAAAgApAAAAAAMAgQAAAAAAAIEAAAAAAABqAAAAAAAAYAAAAAAAABcAAAAAAABgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAABACkAAAAAAgApAAAAAAIAKQAAAAABACIAAAAAAAApAAAAAAIAKQAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAIAKQAAAAACACkAAAAAAgApAAAAAAIAJAAAAAACACkAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAAYAAAAAAAAGAAAAAAAAfgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAFAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAABQAAAAAAAH4AAAAAAwB+AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAIAfgAAAAACAAcAAAAAAwBPAAAAAAAATwAAAAAAAE8AAAAAAAAFAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAABgAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAAcAAAAAAAAHAAAAAAAATgAAAAAAAAYAAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAHAAcAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAA== + tiles: KQAAAAADACkAAAAAAwAkAAAAAAMAJAAAAAACACkAAAAAAgAXAAAAAAAAFwAAAAAAAGsAAAAAAQBnAAAAAAAAZwAAAAAAAGAAAAAAAwBgAAAAAAAAYAAAAAABAGAAAAAAAwBgAAAAAAEAgQAAAAAAACkAAAAAAQApAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAJAAAAAACAIEAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAkAAAAAAMAgQAAAAAAAIEAAAAAAAApAAAAAAEAKQAAAAACACkAAAAAAQApAAAAAAMAKQAAAAABACUAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAXAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAIAKQAAAAADACkAAAAAAQApAAAAAAAAKQAAAAADACQAAAAAAgApAAAAAAMAgQAAAAAAAIEAAAAAAABqAAAAAAAAYAAAAAAAABcAAAAAAABgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAABACkAAAAAAgApAAAAAAIAKQAAAAABACIAAAAAAAApAAAAAAIAKQAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAIAKQAAAAACACkAAAAAAgApAAAAAAIAJAAAAAACACkAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAAYAAAAAAAAGAAAAAAAAfgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAANgAAAAAAAH4AAAAAAwB+AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAIAfgAAAAACAAcAAAAAAwBPAAAAAAAATwAAAAAAAE8AAAAAAAA2AAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAABgAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAAcAAAAAAAAHAAAAAAAATgAAAAAAAAYAAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAHAAcAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAA== version: 7 0,1: ind: 0,1 - tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAcwAAAAAAAIEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAACBAAAAAAAAfQAAAAAAAIEAAAAAAABzAAAAAAAAcwAAAAAAAHMAAAAAAACBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAIEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAgQAAAAAAAH0AAAAAAACBAAAAAAAAeAAAAAAAAHgAAAAAAABzAAAAAAAAgQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAACBAAAAAAAAQQAAAAAAACkAAAAAAAApAAAAAAIAgQAAAAAAAIEAAAAAAAApAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABPAAAAAAAATwAAAAAAAGAAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAACBAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAgQAAAAAAAIEAAAAAAAAFAAAAAAAABQAAAAAAAAYAAAAAAAAFAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAIEAAAAAAAAFAAAAAAAATwAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAAApAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAACQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAFAAAAAAAABgAAAAAAAAYAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAAAFAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAABQAAAAAAAAYAAAAAAAAGAAAAAAAAKQAAAAAAACkAAAAAAAAkAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAABwAAAAAAAAMAAAAAAABOAAAAAAAATwAAAAAAAH4AAAAAAAAaAAAAAAAAGgAAAAAAAAUAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAAMAAAAAAAADAAAAAAAATwAAAAAAAH4AAAAAAAB9AAAAAAAAGgAAAAAAAAAAAAAAAAAFAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAIAAAAAAAAB9AAAAAAEAgAAAAAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAcwAAAAAAAIEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAACBAAAAAAAAfQAAAAAAAIEAAAAAAABzAAAAAAAAcwAAAAAAAHMAAAAAAACBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAIEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAgQAAAAAAAH0AAAAAAACBAAAAAAAAeAAAAAAAAHgAAAAAAABzAAAAAAAAgQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAACBAAAAAAAAQQAAAAAAACkAAAAAAAApAAAAAAIAgQAAAAAAAIEAAAAAAAApAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABPAAAAAAAATwAAAAAAAGAAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAACBAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAAANgAAAAAAAAYAAAAAAAA2AAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAIEAAAAAAAA2AAAAAAAATwAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAAApAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAACQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAA2AAAAAAAABgAAAAAAAAYAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAAA2AAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAANgAAAAAAAAYAAAAAAAAGAAAAAAAAKQAAAAAAACkAAAAAAAAkAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAABwAAAAAAAAMAAAAAAABOAAAAAAAATwAAAAAAAH4AAAAAAAAaAAAAAAAAGgAAAAAAADYAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAAMAAAAAAAADAAAAAAAATwAAAAAAAH4AAAAAAAB9AAAAAAAAGgAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAIAAAAAAAAB9AAAAAAEAgAAAAAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 -2,0: ind: -2,0 - tiles: fgAAAAAAAE8AAAAAAACBAAAAAAAAcAAAAAAAAGAAAAAAAgCBAAAAAAAAcAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAAIEAAAAAAACBAAAAAAAAdwAAAAACAHwAAAAAAQB8AAAAAAAAgQAAAAAAAH4AAAAAAABPAAAAAAAATwAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAAUAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAACBAAAAAAAAagAAAAACAGAAAAAAAwCBAAAAAAAAaAAAAAADAIEAAAAAAAB+AAAAAAAATwAAAAAAAE4AAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAAFAAAAAAIAOAAAAAAAADgAAAAAAAA4AAAAAAAAYAAAAAAAAHAAAAAAAABgAAAAAAAAaAAAAAACAGAAAAAAAgCBAAAAAAAAfgAAAAAAAE8AAAAAAABOAAAAAAAANgAAAAAAADYAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAABgAAAAAAIAagAAAAACAIEAAAAAAABrAAAAAAMAagAAAAABAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAwCBAAAAAAAAgQAAAAAAAGcAAAAAAABwAAAAAAAAagAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAaAAAAAACAIEAAAAAAABPAAAAAAAATgAAAAAAAAYAAAAAAgAGAAAAAAAAgQAAAAAAAGoAAAAAAgBwAAAAAAAAgQAAAAAAAGcAAAAAAABnAAAAAAAAgQAAAAAAAH0AAAAAAgCBAAAAAAAAHAAAAAAAABwAAAAAAACBAAAAAAAATwAAAAAAAE4AAAAAAAAGAAAAAAAABgAAAAAAAIEAAAAAAABgAAAAAAIAZwAAAAAAAHAAAAAAAABnAAAAAAAAgQAAAAAAAIEAAAAAAAB9AAAAAAIAgQAAAAAAABwAAAAAAAAcAAAAAAAAgQAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAIAKQAAAAABAHAAAAAAAABqAAAAAAMAYAAAAAADAGoAAAAAAQCBAAAAAAAAfQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABOAAAAAAAATwAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAgApAAAAAAAAYAAAAAABAIEAAAAAAABnAAAAAAAAgQAAAAAAAH0AAAAAAAB9AAAAAAIAgQAAAAAAAH4AAAAAAgCBAAAAAAAATwAAAAAAAE8AAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgAGAAAAAAEABgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAB9AAAAAAIAfQAAAAAAAIEAAAAAAAB9AAAAAAMAgQAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAABAAYAAAAAAQAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAAABgAAAAADAIEAAAAAAAB9AAAAAAMAFgAAAAABAH0AAAAAAgB9AAAAAAMAfQAAAAADAIEAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAAAGAAAAAAEABgAAAAADAAYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAwCBAAAAAAAAfQAAAAABAH0AAAAAAwB9AAAAAAAAfQAAAAACAH0AAAAAAQCBAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAfQAAAAADAH0AAAAAAwAVAAAAAAAAgQAAAAAAAIEAAAAAAAAWAAAAAAMAgQAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAACkAAAAAAwApAAAAAAEAgQAAAAAAAIEAAAAAAAAWAAAAAAAAfQAAAAADAH0AAAAAAwBOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAIAfgAAAAADAH4AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAAfgAAAAACAH4AAAAAAgApAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== + tiles: fgAAAAAAAE8AAAAAAACBAAAAAAAAcAAAAAAAAGAAAAAAAgCBAAAAAAAAcAAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAIEAAAAAAACBAAAAAAAAdwAAAAACAHwAAAAAAQB8AAAAAAAAgQAAAAAAAH4AAAAAAABPAAAAAAAATwAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAACBAAAAAAAAagAAAAACAGAAAAAAAwCBAAAAAAAAaAAAAAADAIEAAAAAAAB+AAAAAAAATwAAAAAAAE4AAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAAAYAAAAAAAAHAAAAAAAABgAAAAAAAAaAAAAAACAGAAAAAAAgCBAAAAAAAAfgAAAAAAAE8AAAAAAABOAAAAAAAANgAAAAAAADYAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAABgAAAAAAIAagAAAAACAIEAAAAAAABrAAAAAAMAagAAAAABAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAwCBAAAAAAAAgQAAAAAAAGcAAAAAAABwAAAAAAAAagAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAaAAAAAACAIEAAAAAAABPAAAAAAAATgAAAAAAAAYAAAAAAgAGAAAAAAAAgQAAAAAAAGoAAAAAAgBwAAAAAAAAgQAAAAAAAGcAAAAAAABnAAAAAAAAgQAAAAAAAH0AAAAAAgCBAAAAAAAAHAAAAAAAABwAAAAAAACBAAAAAAAATwAAAAAAAE4AAAAAAAAGAAAAAAAABgAAAAAAAIEAAAAAAABgAAAAAAIAZwAAAAAAAHAAAAAAAABnAAAAAAAAgQAAAAAAAIEAAAAAAAB9AAAAAAIAgQAAAAAAABwAAAAAAAAcAAAAAAAAgQAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAIAKQAAAAABAHAAAAAAAABqAAAAAAMAYAAAAAADAGoAAAAAAQCBAAAAAAAAfQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABOAAAAAAAATwAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAgApAAAAAAAAYAAAAAABAIEAAAAAAABnAAAAAAAAgQAAAAAAAH0AAAAAAAB9AAAAAAIAgQAAAAAAAH4AAAAAAgCBAAAAAAAATwAAAAAAAE8AAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgAGAAAAAAEABgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAB9AAAAAAIAfQAAAAAAAIEAAAAAAAB9AAAAAAMAgQAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAABAAYAAAAAAQAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAAABgAAAAADAIEAAAAAAAB9AAAAAAMAFgAAAAABAH0AAAAAAgB9AAAAAAMAfQAAAAADAIEAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAAAGAAAAAAEABgAAAAADAAYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAwCBAAAAAAAAfQAAAAABAH0AAAAAAwB9AAAAAAAAfQAAAAACAH0AAAAAAQCBAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAfQAAAAADAH0AAAAAAwAVAAAAAAAAgQAAAAAAAIEAAAAAAAAWAAAAAAMAgQAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAACkAAAAAAwApAAAAAAEAgQAAAAAAAIEAAAAAAAAWAAAAAAAAfQAAAAADAH0AAAAAAwBOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAIAfgAAAAADAH4AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAAfgAAAAACAH4AAAAAAgApAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== version: 7 -2,-1: ind: -2,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAB9AAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAfgAAAAAAAIAAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAYAAAAAAAB+AAAAAAAAfQAAAAAAAH4AAAAAAABwAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAgQAAAAAAAH0AAAAAAAB+AAAAAAAABgAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAagAAAAAAAGAAAAAAAAB+AAAAAAAAfQAAAAAAAAYAAAAAAAAFAAAAAAAABgAAAAAAAAUAAAAAAAAGAAAAAAAABQAAAAAAAAYAAAAAAAAGAAAAAAAAgAAAAAAAAHAAAAAAAABwAAAAAAAAagAAAAAAAGsAAAAAAACBAAAAAAAAfgAAAAAAAH0AAAAAAAAGAAAAAAAABQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAH4AAAAAAABwAAAAAAAAcAAAAAAAAGAAAAAAAABgAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAB+AAAAAAAAYAAAAAAAAGAAAAAAAAAmAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAEABQAAAAAAAAUAAAAAAgAFAAAAAAAABQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAFQAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAfQAAAAADAH0AAAAAAwCBAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAQAGAAAAAAEATgAAAAAAAE4AAAAAAACBAAAAAAAAfQAAAAABAH0AAAAAAgB9AAAAAAMAgQAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAVAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAE4AAAAAAABOAAAAAAAAgQAAAAAAAH0AAAAAAwB9AAAAAAMAfQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAIEAAAAAAABOAAAAAAAATgAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAANwAAAAACADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAAwAAAAAAAAMAAAAAAABwAAAAAAAAZwAAAAAAAHAAAAAAAABwAAAAAAAAgQAAAAAAAIEAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAAAMAAAAAAAADAAAAAAAAcAAAAAAAAGoAAAAAAgBnAAAAAAAAagAAAAADAHAAAAAAAABwAAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAADAAAAAAAATgAAAAAAAHAAAAAAAABnAAAAAAAAcAAAAAAAAGoAAAAAAQBwAAAAAAAAcAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAfAAAAAADAHwAAAAAAACBAAAAAAAATgAAAAAAAE4AAAAAAABwAAAAAAAAagAAAAACAGcAAAAAAABnAAAAAAAAcAAAAAAAAHAAAAAAAAA4AAAAAAAAOAAAAAAAADYAAAAAAAA2AAAAAAAAfAAAAAADAHcAAAAAAwB8AAAAAAAAgQAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAB9AAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAAfgAAAAAAAIAAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAAYAAAAAAAB+AAAAAAAAfQAAAAAAAH4AAAAAAABwAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAgQAAAAAAAH0AAAAAAAB+AAAAAAAABgAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAagAAAAAAAGAAAAAAAAB+AAAAAAAAfQAAAAAAAAYAAAAAAAA2AAAAAAAABgAAAAAAADYAAAAAAAAGAAAAAAAANgAAAAAAAAYAAAAAAAAGAAAAAAAAgAAAAAAAAHAAAAAAAABwAAAAAAAAagAAAAAAAGsAAAAAAACBAAAAAAAAfgAAAAAAAH0AAAAAAAAGAAAAAAAANgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAH4AAAAAAABwAAAAAAAAcAAAAAAAAGAAAAAAAABgAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAB+AAAAAAAAYAAAAAAAAGAAAAAAAAAmAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAEANgAAAAAAADYAAAAAAgA2AAAAAAAANgAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAFQAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAfQAAAAADAH0AAAAAAwCBAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAQAGAAAAAAEATgAAAAAAAE4AAAAAAACBAAAAAAAAfQAAAAABAH0AAAAAAgB9AAAAAAMAgQAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAVAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAE4AAAAAAABOAAAAAAAAgQAAAAAAAH0AAAAAAwB9AAAAAAMAfQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAIEAAAAAAABOAAAAAAAATgAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAANwAAAAACADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAAwAAAAAAAAMAAAAAAABwAAAAAAAAZwAAAAAAAHAAAAAAAABwAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAMAAAAAAAADAAAAAAAAcAAAAAAAAGoAAAAAAgBnAAAAAAAAagAAAAADAHAAAAAAAABwAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAADAAAAAAAATgAAAAAAAHAAAAAAAABnAAAAAAAAcAAAAAAAAGoAAAAAAQBwAAAAAAAAcAAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAfAAAAAADAHwAAAAAAACBAAAAAAAATgAAAAAAAE4AAAAAAABwAAAAAAAAagAAAAACAGcAAAAAAABnAAAAAAAAcAAAAAAAAHAAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAA2AAAAAAAAfAAAAAADAHcAAAAAAwB8AAAAAAAAgQAAAAAAAA== version: 7 1,1: ind: 1,1 - tiles: fQAAAAAAAH0AAAAAAAB9AAAAAAAAKQAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAADAAYAAAAAAAAGAAAAAAEABQAAAAADAH0AAAAAAAB9AAAAAAAAfQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAQAGAAAAAAAABQAAAAAAAAUAAAAAAAApAAAAAAAAKQAAAAAAAAUAAAAAAQAGAAAAAAIABgAAAAACAAYAAAAAAQAGAAAAAAMABgAAAAACAAYAAAAAAAAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAIABgAAAAAAAAUAAAAAAAADAAAAAAAATwAAAAAAAE8AAAAAAAAFAAAAAAMABgAAAAABAAYAAAAAAQAGAAAAAAEABgAAAAACAAYAAAAAAQAGAAAAAAIABgAAAAAAAAYAAAAAAwAGAAAAAAEABgAAAAADAAYAAAAAAgAFAAAAAAMAAwAAAAAAAE8AAAAAAABPAAAAAAAABQAAAAACAAYAAAAAAgAGAAAAAAIABgAAAAACAAYAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAACAAYAAAAAAAAGAAAAAAIABQAAAAACAAAAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAGAAAAAAEABgAAAAAAAAYAAAAAAQAFAAAAAAMABQAAAAAAAAUAAAAAAgAFAAAAAAAABQAAAAACAAUAAAAAAwAFAAAAAAEABQAAAAABAAUAAAAAAgAFAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAAAAAYAAAAAAwApAAAAAAAABQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAJAAAAAAAACkAAAAAAAAlAAAAAAAAKQAAAAAAAAYAAAAAAAAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAAAGAAAAAAMABQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAADAAUAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAKQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAQAFAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKQAAAAAAACkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: fQAAAAAAAH0AAAAAAAB9AAAAAAAAKQAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAADAAYAAAAAAAAGAAAAAAEANgAAAAADAH0AAAAAAAB9AAAAAAAAfQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAQAGAAAAAAAANgAAAAAAADYAAAAAAAApAAAAAAAAKQAAAAAAADYAAAAAAQAGAAAAAAIABgAAAAACAAYAAAAAAQAGAAAAAAMABgAAAAACAAYAAAAAAAAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAIABgAAAAAAADYAAAAAAAADAAAAAAAATwAAAAAAAE8AAAAAAAA2AAAAAAMABgAAAAABAAYAAAAAAQAGAAAAAAEABgAAAAACAAYAAAAAAQAGAAAAAAIABgAAAAAAAAYAAAAAAwAGAAAAAAEABgAAAAADAAYAAAAAAgA2AAAAAAMAAwAAAAAAAE8AAAAAAABPAAAAAAAANgAAAAACAAYAAAAAAgAGAAAAAAIABgAAAAACAAYAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAACAAYAAAAAAAAGAAAAAAIANgAAAAACAAAAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAGAAAAAAEABgAAAAAAAAYAAAAAAQA2AAAAAAMANgAAAAAAADYAAAAAAgA2AAAAAAAANgAAAAACADYAAAAAAwA2AAAAAAEANgAAAAABADYAAAAAAgA2AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAAAAAYAAAAAAwApAAAAAAAANgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAJAAAAAAAACkAAAAAAAAlAAAAAAAAKQAAAAAAAAYAAAAAAAA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAAAGAAAAAAMANgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAADADYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAKQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAQA2AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKQAAAAAAACkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 0,-2: ind: 0,-2 - tiles: AwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAAHAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAcAAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAAbAAAAAAAAfgAAAAAAAA0AAAAAAAB9AAAAAAAABwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAGwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAB+AAAAAAAADQAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH4AAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH4AAAAAAAB+AAAAAAAABwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAYAAAAAAAAGAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAAABQAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAABwAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAMABwAAAAAAAAcAAAAAAAAHAAAAAAAABgAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAAAFAAAAAAMABQAAAAAAAAUAAAAAAAAFAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAGAAAAAAMABQAAAAACAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAADAAUAAAAAAABPAAAAAAAATwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAHAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAABOAAAAAAAATgAAAAAAAAYAAAAAAAAFAAAAAAMABQAAAAACAAUAAAAAAQAkAAAAAAMAJAAAAAADACQAAAAAAAAkAAAAAAAAgQAAAAAAAGAAAAAAAgB9AAAAAAEAfQAAAAAAAH0AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAAGAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAEAIAAAAAAAACAAAAAAAQAgAAAAAAAAJAAAAAADAIEAAAAAAABgAAAAAAIAYAAAAAAAAGAAAAAAAQBgAAAAAAMAYAAAAAADAE8AAAAAAABPAAAAAAAABgAAAAACAAUAAAAAAAAFAAAAAAEABQAAAAACACQAAAAAAQAkAAAAAAMAJAAAAAADACQAAAAAAwCBAAAAAAAAYAAAAAACAGAAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAABPAAAAAAAATwAAAAAAAA== + tiles: AwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAAHAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAcAAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAAbAAAAAAAAfgAAAAAAAA0AAAAAAAB9AAAAAAAABwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAGwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAB+AAAAAAAADQAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH4AAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH4AAAAAAAB+AAAAAAAABwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAYAAAAAAAAGAAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAAANgAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAABwAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAMABwAAAAAAAAcAAAAAAAAHAAAAAAAABgAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAAA2AAAAAAMANgAAAAAAADYAAAAAAAA2AAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAGAAAAAAMANgAAAAACAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAADADYAAAAAAABPAAAAAAAATwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAHAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAABOAAAAAAAATgAAAAAAAAYAAAAAAAA2AAAAAAMANgAAAAACADYAAAAAAQAkAAAAAAMAJAAAAAADACQAAAAAAAAkAAAAAAAAgQAAAAAAAGAAAAAAAgB9AAAAAAEAfQAAAAAAAH0AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAAGAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAEAIAAAAAAAACAAAAAAAQAgAAAAAAAAJAAAAAADAIEAAAAAAABgAAAAAAIAYAAAAAAAAGAAAAAAAQBgAAAAAAMAYAAAAAADAE8AAAAAAABPAAAAAAAABgAAAAACADYAAAAAAAA2AAAAAAEANgAAAAACACQAAAAAAQAkAAAAAAMAJAAAAAADACQAAAAAAwCBAAAAAAAAYAAAAAACAGAAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAABPAAAAAAAATwAAAAAAAA== version: 7 1,-2: ind: 1,-2 @@ -203,23 +200,23 @@ entities: version: 7 -2,1: ind: -2,1 - tiles: TgAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAACkAAAAAAAApAAAAAAMAgQAAAAAAAIEAAAAAAAApAAAAAAAAKQAAAAADAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAfgAAAAAAAH4AAAAAAAB9AAAAAAAAfgAAAAAAAH4AAAAAAAApAAAAAAIAKQAAAAABACkAAAAAAAApAAAAAAMAKQAAAAAAACkAAAAAAgBPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAH4AAAAAAAB9AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAKQAAAAAAACkAAAAAAQCBAAAAAAAAgQAAAAAAACkAAAAAAgApAAAAAAIATgAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAE4AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAABOAAAAAAAAfgAAAAAAAH4AAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAfgAAAAADAH4AAAAAAQAWAAAAAAQAfgAAAAADAH0AAAAAAwB+AAAAAAMATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAH4AAAAAAQB+AAAAAAMAfQAAAAACAH4AAAAAAwB+AAAAAAIAfQAAAAADABkAAAAAAAAZAAAAAAAAGQAAAAAAABkAAAAAAAAZAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAFgAAAAABAH4AAAAAAgAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAJwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAAB+AAAAAAEAfgAAAAABAH8AAAAAAQB/AAAAAAAAfwAAAAACAH4AAAAAAQB+AAAAAAMAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABkAAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAACBAAAAAAAAfgAAAAAAAH0AAAAAAAB9AAAAAAIAfgAAAAADAH4AAAAAAgB+AAAAAAMATgAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAZAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAAfgAAAAADAH8AAAAAAQB9AAAAAAIAfwAAAAABAH4AAAAAAAAHAAAAAAAATgAAAAAAAE4AAAAAAAAZAAAAAAAAKAAAAAAAABkAAAAAAAAoAAAAAAAAKAAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAH4AAAAAAwB+AAAAAAIAfgAAAAADAH4AAAAAAgB+AAAAAAEATgAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAABIAAAAAAAASAAAAAAIATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAAcAAAAAAgAHAAAAAAMABwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAABAH4AAAAAAABPAAAAAAAATwAAAAAAAAcAAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAEAAwAAAAAAAAMAAAAAAAB+AAAAAAAAfgAAAAABAH4AAAAAAQB+AAAAAAEATgAAAAAAAE8AAAAAAABPAAAAAAAABwAAAAADAAcAAAAABgAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAfgAAAAAAAH0AAAAAAAB+AAAAAAIAfgAAAAABAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAkACAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== + tiles: TgAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAACkAAAAAAAApAAAAAAMAgQAAAAAAAIEAAAAAAAApAAAAAAAAKQAAAAADAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAfgAAAAAAAH4AAAAAAAB9AAAAAAAAfgAAAAAAAH4AAAAAAAApAAAAAAIAKQAAAAABACkAAAAAAAApAAAAAAMAKQAAAAAAACkAAAAAAgBPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAH4AAAAAAAB9AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAKQAAAAAAACkAAAAAAQCBAAAAAAAAgQAAAAAAACkAAAAAAgApAAAAAAIATgAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAE4AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAABOAAAAAAAAfgAAAAAAAH4AAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAfgAAAAADAH4AAAAAAQAWAAAAAAQAfgAAAAADAH0AAAAAAwB+AAAAAAMATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAH4AAAAAAQB+AAAAAAMAfQAAAAACAH4AAAAAAwB+AAAAAAIAfQAAAAADABkAAAAAAAAZAAAAAAAAGQAAAAAAABkAAAAAAAAZAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAFgAAAAABAH4AAAAAAgAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAJwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAAB+AAAAAAEAfgAAAAABAH8AAAAAAQB/AAAAAAAAfwAAAAACAH4AAAAAAQB+AAAAAAMAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABkAAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAACBAAAAAAAAfgAAAAAAAH0AAAAAAAB9AAAAAAIAfgAAAAADAH4AAAAAAgB+AAAAAAMATgAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAZAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAAfgAAAAADAH8AAAAAAQB9AAAAAAIAfwAAAAABAH4AAAAAAAAHAAAAAAAATgAAAAAAAE4AAAAAAAAZAAAAAAAAKAAAAAAAABkAAAAAAAAoAAAAAAAAKAAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAH4AAAAAAwB+AAAAAAIAfgAAAAADAH4AAAAAAgB+AAAAAAEATgAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAB0AAAAAAAAdAAAAAAIATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAAcAAAAAAgAHAAAAAAMABwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAABAH4AAAAAAABPAAAAAAAATwAAAAAAAAcAAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAEAAwAAAAAAAAMAAAAAAAB+AAAAAAAAfgAAAAABAH4AAAAAAQB+AAAAAAEATgAAAAAAAE8AAAAAAABPAAAAAAAABwAAAAADAAcAAAAABgAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAfgAAAAAAAH0AAAAAAAB+AAAAAAIAfgAAAAABAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAkACAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== version: 7 -1,-2: ind: -1,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAABQAAAAAAAAAAAAAAAAAFAAAAAAIABQAAAAAAAAUAAAAAAAAFAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAADAAYAAAAAAAAAAAAAAAAABQAAAAADAAYAAAAAAQAGAAAAAAEABQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAQAGAAAAAAIAAAAAAAAAAAUAAAAAAgAGAAAAAAMABgAAAAAAAAUAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAMABgAAAAAAAAAAAAAAAAAFAAAAAAEABgAAAAAAAAYAAAAAAAAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAYAAAAAAQAAAAAAAAAABQAAAAABAAYAAAAAAQAGAAAAAAEABQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAgAGAAAAAAEAAAAAAAAAAAUAAAAAAQAGAAAAAAMABgAAAAABAAUAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAEABgAAAAADAAUAAAAAAAAFAAAAAAEABgAAAAAAAAYAAAAAAwAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAYAAAAAAQAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAIABQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAgAGAAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAABAAUAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAMABgAAAAABAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAAAAAAAAAAAA2AAAAAAIANgAAAAAAADYAAAAAAAA2AAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAADAAYAAAAAAAAAAAAAAAAANgAAAAADAAYAAAAAAQAGAAAAAAEANgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAQAGAAAAAAIAAAAAAAAAADYAAAAAAgAGAAAAAAMABgAAAAAAADYAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAMABgAAAAAAAAAAAAAAAAA2AAAAAAEABgAAAAAAAAYAAAAAAAA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACAAYAAAAAAQAAAAAAAAAANgAAAAABAAYAAAAAAQAGAAAAAAEANgAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAgAGAAAAAAEAAAAAAAAAADYAAAAAAQAGAAAAAAMABgAAAAABADYAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAEABgAAAAADADYAAAAAAAA2AAAAAAEABgAAAAAAAAYAAAAAAwA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACAAYAAAAAAQAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAIANgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAgAGAAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAABADYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAMABgAAAAABAA== version: 7 2,0: ind: 2,0 - tiles: BgAAAAAAAAYAAAAAAwAFAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABgAAAAACAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAwAGAAAAAAIABgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAMABQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAUAAAAAAwADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAgAFAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAMABQAAAAADAAUAAAAAAwADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAADAAYAAAAAAgAFAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAQAGAAAAAAMABgAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABgAAAAACAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAYAAAAAAwAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAwAGAAAAAAAABQAAAAAAAAUAAAAAAgAFAAAAAAAABQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABQAAAAADAAUAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAUAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: BgAAAAAAAAYAAAAAAwA2AAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABgAAAAACAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAwAGAAAAAAIABgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAMANgAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABADYAAAAAAwADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAgA2AAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAMANgAAAAADADYAAAAAAwADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAADAAYAAAAAAgA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAQAGAAAAAAMABgAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABgAAAAACAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAYAAAAAAwAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAwAGAAAAAAAANgAAAAAAADYAAAAAAgA2AAAAAAAANgAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIANgAAAAADADYAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACADYAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 2,1: ind: 2,1 - tiles: BQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: NgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 -3,-1: ind: -3,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAawAAAAAAAHAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAGAAAAAAAAAmAAAAAAAAawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAABgAAAAAAAAYAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAYAAAAAAAAGsAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAIEAAAAAAABqAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAgQAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAAAAAE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAawAAAAAAAHAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAGAAAAAAAAAmAAAAAAAAawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAABgAAAAAAAAYAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAYAAAAAAAAGsAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAAAAIEAAAAAAABqAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAAgQAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAAAAAE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== version: 7 -3,0: ind: -3,0 @@ -255,7 +252,7 @@ entities: version: 7 2,-1: ind: 2,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4AAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAUAAAAAAAAFAAAAAAIAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4AAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACADYAAAAAAAA2AAAAAAIAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 2,-2: ind: 2,-2 @@ -271,7 +268,7 @@ entities: version: 7 -2,-2: ind: -2,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAYAAAAAAAAGAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB+AAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAH0AAAAAAAB9AAAAAAAAfgAAAAAAAIAAAAAAAAB+AAAAAAAAfQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAAAAAYAAAAAAAAGAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB+AAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAH0AAAAAAAB9AAAAAAAAfgAAAAAAAIAAAAAAAAB+AAAAAAAAfQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAA== version: 7 - type: Broadphase - type: Physics @@ -4499,6 +4496,8 @@ entities: - type: FTLDestination - type: GravityShake shakeTimes: 10 + - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 5 components: - type: MetaData @@ -4590,7 +4589,7 @@ entities: chunks: 0,0: ind: 0,0 - tiles: LgAAAAAAAC4AAAAAAgAuAAAAAAIALgAAAAACAC4AAAAAAAAuAAAAAAMALgAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAC4AAAAAAwAuAAAAAAEALgAAAAABAC4AAAAAAQAuAAAAAAAAbwAAAAAAAC4AAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAIAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAAAuAAAAAAAALgAAAAAAAC4AAAAAAgAuAAAAAAEALgAAAAACAC4AAAAAAAAuAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAANgAAAAACAIQAAAAAAACEAAAAAAAANgAAAAAAADYAAAAAAgA2AAAAAAAALgAAAAABAC4AAAAAAQAuAAAAAAMALgAAAAACAC4AAAAAAwAuAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAwA2AAAAAAEAhAAAAAAAADgAAAAAAwA2AAAAAAEANgAAAAABAIEAAAAAAACBAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAA2AAAAAAEAgQAAAAAAADYAAAAAAQA2AAAAAAEANgAAAAAAADgAAAAAAACBAAAAAAAAgQAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAOAAAAAAAAIEAAAAAAACEAAAAAAAANgAAAAACADYAAAAAAgA2AAAAAAMAgQAAAAAAAIEAAAAAAACEAAAAAAAANgAAAAACADgAAAAAAQA2AAAAAAIANgAAAAADADYAAAAAAgCBAAAAAAAAgQAAAAAAADYAAAAAAgCEAAAAAAAAhAAAAAAAADYAAAAAAwA2AAAAAAAANgAAAAADAIEAAAAAAACBAAAAAAAAhAAAAAAAADYAAAAAAQA2AAAAAAEANgAAAAADADYAAAAAAAA2AAAAAAIAgQAAAAAAAIEAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAAuAAAAAAMANgAAAAACADYAAAAAAAA4AAAAAAAANgAAAAACAIEAAAAAAAA2AAAAAAIANgAAAAABAIQAAAAAAAA4AAAAAAAANgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACEAAAAAAAALgAAAAADAC4AAAAAAQA2AAAAAAMANgAAAAABAIEAAAAAAACBAAAAAAAALgAAAAABADYAAAAAAACEAAAAAAAANgAAAAABADYAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAANgAAAAABADYAAAAAAAA4AAAAAAIANgAAAAACADYAAAAAAgCBAAAAAAAAgQAAAAAAAC4AAAAAAgAuAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAMANgAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAMAAAAAAAADAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAE4AAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAATgAAAAAAAAwAAAAAAAAMAAAAAAAAAwAAAAAAAA== + tiles: LgAAAAAAAC4AAAAAAgAuAAAAAAIALgAAAAACAC4AAAAAAAAuAAAAAAMALgAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAC4AAAAAAwAuAAAAAAEALgAAAAABAC4AAAAAAQAuAAAAAAAAbwAAAAAAAC4AAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAIAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAAAuAAAAAAAALgAAAAAAAC4AAAAAAgAuAAAAAAEALgAAAAACAC4AAAAAAAAuAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAANgAAAAACAIQAAAAAAACEAAAAAAAANgAAAAAAADYAAAAAAgA2AAAAAAAALgAAAAABAC4AAAAAAQAuAAAAAAMALgAAAAACAC4AAAAAAwAuAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAwA2AAAAAAEAhAAAAAAAAAYAAAAAAwA2AAAAAAEANgAAAAABAIEAAAAAAACBAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAA2AAAAAAEAgQAAAAAAADYAAAAAAQA2AAAAAAEANgAAAAAAAAYAAAAAAACBAAAAAAAAgQAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAABgAAAAAAAIEAAAAAAACEAAAAAAAANgAAAAACADYAAAAAAgA2AAAAAAMAgQAAAAAAAIEAAAAAAACEAAAAAAAANgAAAAACAAYAAAAAAQA2AAAAAAIANgAAAAADADYAAAAAAgCBAAAAAAAAgQAAAAAAADYAAAAAAgCEAAAAAAAAhAAAAAAAADYAAAAAAwA2AAAAAAAANgAAAAADAIEAAAAAAACBAAAAAAAAhAAAAAAAADYAAAAAAQA2AAAAAAEANgAAAAADADYAAAAAAAA2AAAAAAIAgQAAAAAAAIEAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAAuAAAAAAMANgAAAAACADYAAAAAAAAGAAAAAAAANgAAAAACAIEAAAAAAAA2AAAAAAIANgAAAAABAIQAAAAAAAAGAAAAAAAANgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACEAAAAAAAALgAAAAADAC4AAAAAAQA2AAAAAAMANgAAAAABAIEAAAAAAACBAAAAAAAALgAAAAABADYAAAAAAACEAAAAAAAANgAAAAABADYAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAANgAAAAABADYAAAAAAAAGAAAAAAIANgAAAAACADYAAAAAAgCBAAAAAAAAgQAAAAAAAC4AAAAAAgAuAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAMANgAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAMAAAAAAAADAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAE4AAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAATgAAAAAAAAwAAAAAAAAMAAAAAAAAAwAAAAAAAA== version: 7 -1,0: ind: -1,0 @@ -6389,6 +6388,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 7784 components: - type: MetaData @@ -6500,6 +6500,7 @@ entities: - type: IFF flags: Hide - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8115 components: - type: MetaData @@ -6681,6 +6682,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8173 components: - type: MetaData @@ -6763,6 +6765,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8185 components: - type: MetaData @@ -6855,6 +6858,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8200 components: - type: MetaData @@ -7177,6 +7181,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8246 components: - type: MetaData @@ -7252,6 +7257,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8264 components: - type: MetaData @@ -7329,6 +7335,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8280 components: - type: MetaData @@ -7406,6 +7413,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8320 components: - type: MetaData @@ -7525,6 +7533,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8389 components: - type: MetaData @@ -7655,6 +7664,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8406 components: - type: MetaData @@ -8006,6 +8016,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - proto: AcousticGuitarInstrument entities: - uid: 21 @@ -8205,8 +8216,8 @@ entities: - 3523 - 3529 - 3570 - - 3522 - - 3569 + - 3406 + - 3407 - 3571 - 3527 - 3528 @@ -8224,6 +8235,10 @@ entities: - 2789 - 2787 - 2786 + - 8633 + - 114 + - 8634 + - 3522 - type: Fixtures fixtures: {} - proto: Airlock @@ -8954,11 +8969,6 @@ entities: - type: Transform pos: -1.5,10.5 parent: 2 - - uid: 114 - components: - - type: Transform - pos: 6.5,11.5 - parent: 2 - uid: 115 components: - type: Transform @@ -9019,6 +9029,15 @@ entities: - type: DeviceNetwork deviceLists: - 2760 + - uid: 8634 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 4.5,12.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 - proto: AirTankFilled entities: - uid: 125 @@ -27690,18 +27709,6 @@ entities: parent: 6776 - type: Physics canCollide: False -- proto: DrinkKiraSpecial - entities: - - uid: 2663 - components: - - type: Transform - pos: -19.440605,-5.1425204 - parent: 2 - - uid: 2664 - components: - - type: Transform - pos: -18.596855,-5.382104 - parent: 2 - proto: DrinkMugHeart entities: - uid: 2665 @@ -27735,6 +27742,18 @@ entities: parent: 6776 - type: Physics canCollide: False +- proto: DrinkOrangeLimeSodaGlass + entities: + - uid: 2663 + components: + - type: Transform + pos: -19.440605,-5.1425204 + parent: 2 + - uid: 2664 + components: + - type: Transform + pos: -18.596855,-5.382104 + parent: 2 - proto: DrinkShakeMeat entities: - uid: 6790 @@ -28393,9 +28412,6 @@ entities: rot: -1.5707963267948966 rad pos: 9.5,13.5 parent: 2 - - type: DeviceList - devices: - - 114 - type: Fixtures fixtures: {} - uid: 2754 @@ -31164,20 +31180,8 @@ entities: - type: Transform pos: 7.5,16.5 parent: 2 - - uid: 3027 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,10.5 - parent: 2 - - uid: 3028 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,10.5 - parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' + color: '#0000FFFF' - uid: 3029 components: - type: Transform @@ -31218,14 +31222,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 3034 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 3.5,11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 3035 components: - type: Transform @@ -31375,14 +31371,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3054 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,9.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 3055 components: - type: Transform @@ -31405,6 +31393,21 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' + - uid: 3509 + components: + - type: Transform + pos: -2.5,11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3569 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 0.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPipeFourway entities: - uid: 3058 @@ -31463,13 +31466,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3066 - components: - - type: Transform - pos: -4.5,9.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 3067 components: - type: Transform @@ -31486,26 +31482,58 @@ entities: color: '#FF0000FF' - proto: GasPipeStraight entities: + - uid: 3027 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3028 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 4.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3054 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 1.5,15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 3069 components: - type: Transform pos: 3.5,16.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3070 components: - type: Transform pos: 3.5,15.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3071 components: - type: Transform pos: 3.5,14.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3072 components: - type: Transform pos: 8.5,15.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 3073 components: - type: Transform @@ -31722,11 +31750,11 @@ entities: - uid: 3101 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 6.5,11.5 + rot: 3.141592653589793 rad + pos: 1.5,13.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' + color: '#FF0000FF' - uid: 3102 components: - type: Transform @@ -31848,7 +31876,7 @@ entities: - uid: 3117 components: - type: Transform - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad pos: 5.5,11.5 parent: 2 - type: AtmosPipeColor @@ -31918,8 +31946,7 @@ entities: - uid: 3126 components: - type: Transform - rot: 3.141592653589793 rad - pos: 3.5,13.5 + pos: 3.5,12.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' @@ -33753,29 +33780,14 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3359 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 7.5,12.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 3360 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 6.5,12.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 3361 components: - type: Transform - pos: 5.5,11.5 + rot: 1.5707963267948966 rad + pos: 2.5,11.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' + color: '#0000FFFF' - uid: 3362 components: - type: Transform @@ -34105,35 +34117,19 @@ entities: - uid: 3404 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -3.5,9.5 + rot: 3.141592653589793 rad + pos: 1.5,17.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - uid: 3405 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,9.5 + rot: 3.141592653589793 rad + pos: 1.5,16.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3406 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 3407 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 3408 components: - type: Transform @@ -34296,8 +34292,7 @@ entities: - uid: 3428 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -0.5,10.5 + pos: 5.5,11.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -34396,8 +34391,8 @@ entities: - uid: 3441 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,10.5 + rot: 1.5707963267948966 rad + pos: 3.5,12.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -34425,8 +34420,63 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 3464 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 7.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3505 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 6.5,11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3526 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 1.5,14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPipeTJunction entities: + - uid: 3034 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3066 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,9.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3359 + components: + - type: Transform + pos: 4.5,11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3360 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 3.5,11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3445 components: - type: Transform @@ -34562,7 +34612,8 @@ entities: - uid: 3462 components: - type: Transform - pos: 4.5,11.5 + rot: -1.5707963267948966 rad + pos: 3.5,13.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' @@ -34574,14 +34625,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 3464 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 3.5,12.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 3465 components: - type: Transform @@ -34888,14 +34931,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3505 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 5.5,12.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 3506 components: - type: Transform @@ -34920,13 +34955,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3509 - components: - - type: Transform - pos: -2.5,11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 3510 components: - type: Transform @@ -34965,6 +34993,21 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' + - uid: 3568 + components: + - type: Transform + pos: 5.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8632 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 1.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPort entities: - uid: 3515 @@ -35019,20 +35062,24 @@ entities: parent: 2 - proto: GasVentPump entities: - - uid: 3522 + - uid: 3407 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,11.5 + rot: 1.5707963267948966 rad + pos: 1.5,11.5 parent: 2 - type: DeviceNetwork deviceLists: - 32 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3523 components: - type: Transform pos: 3.5,17.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35042,6 +35089,8 @@ entities: rot: -1.5707963267948966 rad pos: 12.5,14.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35051,18 +35100,16 @@ entities: rot: 3.141592653589793 rad pos: 7.5,8.5 parent: 2 - - uid: 3526 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 4.5,12.5 - parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3527 components: - type: Transform rot: 1.5707963267948966 rad pos: 6.5,16.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35071,6 +35118,8 @@ entities: - type: Transform pos: 11.5,16.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35080,6 +35129,8 @@ entities: rot: 3.141592653589793 rad pos: 4.5,9.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35379,26 +35430,57 @@ entities: - 24 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 8633 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 2.5,13.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#0000FFFF' - proto: GasVentScrubber entities: + - uid: 114 + components: + - type: Transform + pos: 6.5,13.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3406 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,11.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3522 + components: + - type: Transform + pos: 1.5,18.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 3567 components: - type: Transform pos: 12.5,13.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 32 - - uid: 3568 - components: - - type: Transform - pos: 5.5,13.5 - parent: 2 - - uid: 3569 - components: - - type: Transform - pos: 1.5,11.5 - parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - type: DeviceNetwork deviceLists: - 32 @@ -35408,6 +35490,8 @@ entities: rot: 3.141592653589793 rad pos: 5.5,9.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - type: DeviceNetwork deviceLists: - 32 @@ -35416,6 +35500,8 @@ entities: - type: Transform pos: 8.5,16.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - type: DeviceNetwork deviceLists: - 32 @@ -35425,6 +35511,8 @@ entities: rot: 1.5707963267948966 rad pos: 7.5,7.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - type: DeviceNetwork deviceLists: - 32 @@ -38049,13 +38137,13 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage + removedMasks: 20 air: volume: 200 immutable: False temperature: 293.1496 moles: {} open: True - removedMasks: 20 - type: PlaceableSurface isPlaceable: True - proto: LockerWallMedicalFilled @@ -38700,43 +38788,31 @@ entities: - type: Transform pos: 5.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6938 components: - type: Transform pos: 3.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6939 components: - type: Transform pos: 4.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6940 components: - type: Transform pos: 4.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6941 components: - type: Transform pos: 3.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6942 components: - type: Transform pos: 5.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - proto: Mirror entities: - uid: 3959 @@ -43196,6 +43272,8 @@ entities: - type: RandomSpawner prototypes: - MobBreadDog + rarePrototypes: [] + gameRules: [] - uid: 4516 components: - type: MetaData @@ -43206,6 +43284,8 @@ entities: - type: RandomSpawner prototypes: - MobCatCake + rarePrototypes: [] + gameRules: [] - proto: RandomFloraTree entities: - uid: 4517 @@ -43503,99 +43583,73 @@ entities: - type: Transform pos: 30.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4564 components: - type: Transform rot: 1.5707963267948966 rad pos: 29.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4565 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4566 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4567 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4568 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,-2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4569 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,-3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4570 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,-4.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4571 components: - type: Transform pos: 23.5,5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4572 components: - type: Transform pos: 23.5,4.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4573 components: - type: Transform pos: 22.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4574 components: - type: Transform pos: 23.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4575 components: - type: Transform pos: 24.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: ReinforcedWindow entities: - uid: 4576 @@ -43603,705 +43657,517 @@ entities: - type: Transform pos: 9.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4577 components: - type: Transform rot: 3.141592653589793 rad pos: -16.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4578 components: - type: Transform rot: 3.141592653589793 rad pos: -15.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4579 components: - type: Transform rot: 3.141592653589793 rad pos: -18.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4580 components: - type: Transform rot: 3.141592653589793 rad pos: -19.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4581 components: - type: Transform rot: 3.141592653589793 rad pos: -17.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4582 components: - type: Transform pos: -3.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4583 components: - type: Transform rot: 1.5707963267948966 rad pos: 11.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4584 components: - type: Transform pos: -3.5,21.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4585 components: - type: Transform pos: -4.5,21.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4586 components: - type: Transform pos: 5.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4587 components: - type: Transform rot: -1.5707963267948966 rad pos: 15.5,29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4588 components: - type: Transform rot: -1.5707963267948966 rad pos: 14.5,29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4589 components: - type: Transform rot: -1.5707963267948966 rad pos: 16.5,29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4590 components: - type: Transform rot: -1.5707963267948966 rad pos: 17.5,28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4591 components: - type: Transform rot: -1.5707963267948966 rad pos: -4.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4592 components: - type: Transform rot: -1.5707963267948966 rad pos: 13.5,25.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4593 components: - type: Transform rot: 3.141592653589793 rad pos: 20.5,22.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4594 components: - type: Transform pos: 8.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4595 components: - type: Transform rot: 1.5707963267948966 rad pos: 14.5,25.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4596 components: - type: Transform pos: 6.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4597 components: - type: Transform pos: 9.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4598 components: - type: Transform pos: 7.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4599 components: - type: Transform rot: 3.141592653589793 rad pos: -26.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4600 components: - type: Transform rot: -1.5707963267948966 rad pos: 11.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4601 components: - type: Transform rot: 1.5707963267948966 rad pos: 17.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4602 components: - type: Transform rot: 3.141592653589793 rad pos: -25.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4603 components: - type: Transform rot: 1.5707963267948966 rad pos: 16.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4604 components: - type: Transform rot: 1.5707963267948966 rad pos: 15.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4605 components: - type: Transform rot: 3.141592653589793 rad pos: 23.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4606 components: - type: Transform rot: 3.141592653589793 rad pos: 24.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4607 components: - type: Transform rot: -1.5707963267948966 rad pos: 10.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4608 components: - type: Transform pos: 2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4609 components: - type: Transform pos: 1.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4610 components: - type: Transform pos: 0.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4611 components: - type: Transform pos: -0.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4612 components: - type: Transform pos: 5.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4613 components: - type: Transform pos: 3.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4614 components: - type: Transform pos: 12.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4615 components: - type: Transform pos: -9.5,5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4616 components: - type: Transform pos: -7.5,5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4617 components: - type: Transform pos: 26.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4618 components: - type: Transform pos: 19.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4619 components: - type: Transform pos: 3.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4620 components: - type: Transform pos: 5.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4621 components: - type: Transform pos: 12.5,-3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4622 components: - type: Transform pos: 8.5,10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4623 components: - type: Transform pos: 26.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4624 components: - type: Transform pos: 12.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4625 components: - type: Transform pos: 7.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4626 components: - type: Transform pos: 10.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4627 components: - type: Transform pos: 8.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4628 components: - type: Transform pos: 18.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4629 components: - type: Transform pos: 5.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4630 components: - type: Transform pos: 12.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4631 components: - type: Transform pos: 7.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4632 components: - type: Transform rot: 3.141592653589793 rad pos: 20.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4633 components: - type: Transform rot: 1.5707963267948966 rad pos: 25.5,-14.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4634 components: - type: Transform pos: 12.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4635 components: - type: Transform pos: 9.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4636 components: - type: Transform pos: -29.5,-2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4637 components: - type: Transform pos: -29.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4638 components: - type: Transform pos: -7.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4639 components: - type: Transform pos: 26.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4640 components: - type: Transform pos: -29.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4641 components: - type: Transform pos: 26.5,11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4642 components: - type: Transform pos: 26.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4643 components: - type: Transform rot: 3.141592653589793 rad pos: 10.5,12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4644 components: - type: Transform rot: 3.141592653589793 rad pos: 25.5,-12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4645 components: - type: Transform rot: 3.141592653589793 rad pos: 26.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4646 components: - type: Transform rot: 3.141592653589793 rad pos: 27.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4647 components: - type: Transform pos: 9.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4648 components: - type: Transform rot: -1.5707963267948966 rad pos: 21.5,14.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4649 components: - type: Transform rot: 3.141592653589793 rad pos: 22.5,14.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4650 components: - type: Transform rot: 3.141592653589793 rad pos: 23.5,14.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4651 components: - type: Transform rot: 3.141592653589793 rad pos: 17.5,27.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4652 components: - type: Transform rot: 3.141592653589793 rad pos: 18.5,26.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4653 components: - type: Transform rot: 1.5707963267948966 rad pos: 15.5,24.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4654 components: - type: Transform rot: -1.5707963267948966 rad pos: 19.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4655 components: - type: Transform rot: 1.5707963267948966 rad pos: 0.5,-30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4656 components: - type: Transform rot: 1.5707963267948966 rad pos: -0.5,-29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4657 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,-28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4658 components: - type: Transform rot: 1.5707963267948966 rad pos: 6.5,-27.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4659 components: - type: Transform rot: 1.5707963267948966 rad pos: 6.5,-28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4660 components: - type: Transform rot: 1.5707963267948966 rad pos: 6.5,-29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4661 components: - type: Transform pos: -29.5,-13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4662 components: - type: Transform rot: 3.141592653589793 rad pos: -26.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4663 components: - type: Transform rot: 3.141592653589793 rad pos: -25.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4664 components: - type: Transform rot: 3.141592653589793 rad pos: -24.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4665 components: - type: Transform rot: 3.141592653589793 rad pos: -23.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4666 components: - type: Transform pos: -28.5,-13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4667 components: - type: Transform pos: -33.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4668 components: - type: Transform pos: -34.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4669 components: - type: Transform pos: -35.5,-10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: ReinforcedWindowDiagonal entities: - uid: 4670 @@ -44310,131 +44176,97 @@ entities: rot: -1.5707963267948966 rad pos: 18.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4671 components: - type: Transform rot: 1.5707963267948966 rad pos: 18.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4672 components: - type: Transform rot: -1.5707963267948966 rad pos: 19.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4673 components: - type: Transform pos: -27.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4674 components: - type: Transform rot: 3.141592653589793 rad pos: -25.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4675 components: - type: Transform rot: 3.141592653589793 rad pos: -24.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4676 components: - type: Transform pos: -26.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4677 components: - type: Transform rot: 3.141592653589793 rad pos: -26.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4678 components: - type: Transform pos: -25.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4679 components: - type: Transform rot: 3.141592653589793 rad pos: 13.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4680 components: - type: Transform rot: 1.5707963267948966 rad pos: 14.5,24.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4681 components: - type: Transform rot: -1.5707963267948966 rad pos: 17.5,29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4682 components: - type: Transform rot: -1.5707963267948966 rad pos: 18.5,27.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4683 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,-29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4684 components: - type: Transform rot: 1.5707963267948966 rad pos: -0.5,-30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4685 components: - type: Transform pos: -34.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4686 components: - type: Transform pos: -35.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: ResearchAndDevelopmentServer entities: - uid: 4687 @@ -44981,8 +44813,6 @@ entities: - type: Transform pos: 28.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: SignalButton entities: - uid: 4739 @@ -59335,8 +59165,6 @@ entities: - type: Transform pos: 4.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorBarKitchenLocked entities: - uid: 6119 @@ -59345,8 +59173,6 @@ entities: rot: 1.5707963267948966 rad pos: 5.5,-12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorChapelLocked entities: - uid: 6120 @@ -59355,8 +59181,6 @@ entities: rot: 3.141592653589793 rad pos: -51.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorKitchenHydroponicsLocked entities: - uid: 6121 @@ -59365,8 +59189,6 @@ entities: rot: 3.141592653589793 rad pos: 9.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecure entities: - uid: 6122 @@ -59374,75 +59196,55 @@ entities: - type: Transform pos: -8.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6123 components: - type: Transform pos: -15.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6124 components: - type: Transform pos: -14.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6125 components: - type: Transform pos: 26.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6126 components: - type: Transform pos: -5.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6127 components: - type: Transform rot: 1.5707963267948966 rad pos: -7.5,-4.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6128 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6129 components: - type: Transform pos: -6.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6130 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7743 components: - type: Transform rot: 3.141592653589793 rad pos: 4.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - type: AccessReader access: - - Engineering @@ -59457,71 +59259,53 @@ entities: rot: 1.5707963267948966 rad pos: -11.5,20.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6132 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6133 components: - type: Transform rot: -1.5707963267948966 rad pos: -12.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6134 components: - type: Transform rot: 3.141592653589793 rad pos: -8.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6135 components: - type: Transform rot: -1.5707963267948966 rad pos: -12.5,20.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6136 components: - type: Transform pos: -8.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7744 components: - type: Transform rot: 3.141592653589793 rad pos: 15.5,2.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7745 components: - type: Transform rot: 3.141592653589793 rad pos: 13.5,2.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7746 components: - type: Transform rot: 3.141592653589793 rad pos: 14.5,2.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - proto: WindoorSecureBrigLocked entities: - uid: 6137 @@ -59530,23 +59314,17 @@ entities: rot: -1.5707963267948966 rad pos: -16.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6138 components: - type: Transform rot: 3.141592653589793 rad pos: -1.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6139 components: - type: Transform pos: -1.5,11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureCargoLocked entities: - uid: 6140 @@ -59555,16 +59333,12 @@ entities: rot: 3.141592653589793 rad pos: 13.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6141 components: - type: Transform rot: 1.5707963267948966 rad pos: 12.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureChemistryLocked entities: - uid: 6142 @@ -59573,15 +59347,11 @@ entities: rot: 3.141592653589793 rad pos: 4.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6143 components: - type: Transform pos: 5.5,10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureEngineeringLocked entities: - uid: 6144 @@ -59590,8 +59360,6 @@ entities: rot: 1.5707963267948966 rad pos: 12.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureHeadOfPersonnelLocked entities: - uid: 6145 @@ -59600,8 +59368,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureMedicalLocked entities: - uid: 6146 @@ -59610,8 +59376,6 @@ entities: rot: -1.5707963267948966 rad pos: 2.5,12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureSalvageLocked entities: - uid: 6147 @@ -59620,8 +59384,6 @@ entities: rot: 3.141592653589793 rad pos: 19.5,22.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureScienceLocked entities: - uid: 6148 @@ -59630,16 +59392,12 @@ entities: rot: 1.5707963267948966 rad pos: 12.5,-2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7747 components: - type: Transform rot: 3.141592653589793 rad pos: 7.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - type: AccessReader access: - - Engineering @@ -59654,8 +59412,6 @@ entities: rot: 3.141592653589793 rad pos: -8.5,5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorServiceLocked entities: - uid: 6150 @@ -59663,8 +59419,6 @@ entities: - type: Transform pos: -20.5,24.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: Window entities: - uid: 6151 @@ -59673,240 +59427,174 @@ entities: rot: 3.141592653589793 rad pos: -35.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6152 components: - type: Transform rot: 3.141592653589793 rad pos: -34.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6153 components: - type: Transform rot: 1.5707963267948966 rad pos: -25.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6154 components: - type: Transform pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6155 components: - type: Transform pos: -24.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6156 components: - type: Transform pos: -22.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6157 components: - type: Transform pos: -21.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6158 components: - type: Transform pos: -23.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6159 components: - type: Transform rot: -1.5707963267948966 rad pos: -7.5,1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6160 components: - type: Transform pos: -29.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6161 components: - type: Transform pos: -30.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6162 components: - type: Transform pos: -31.5,31.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6163 components: - type: Transform pos: -31.5,30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6164 components: - type: Transform pos: 12.5,15.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6165 components: - type: Transform pos: 10.5,15.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6166 components: - type: Transform pos: -37.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6167 components: - type: Transform pos: -37.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6168 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6169 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6170 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6171 components: - type: Transform pos: -35.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6172 components: - type: Transform pos: -32.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6173 components: - type: Transform pos: -33.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 8373 components: - type: Transform pos: -3.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8374 components: - type: Transform pos: -2.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8375 components: - type: Transform pos: -1.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8376 components: - type: Transform pos: -0.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8585 components: - type: Transform pos: -11.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8586 components: - type: Transform pos: -9.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8587 components: - type: Transform pos: -8.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8588 components: - type: Transform pos: -10.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8589 components: - type: Transform rot: -1.5707963267948966 rad pos: -9.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8590 components: - type: Transform rot: -1.5707963267948966 rad pos: -8.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - proto: WindowClockworkDirectional entities: - uid: 6174 @@ -59915,8 +59603,6 @@ entities: rot: -1.5707963267948966 rad pos: -19.5,24.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindowDirectional entities: - uid: 6175 @@ -59924,558 +59610,414 @@ entities: - type: Transform pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6176 components: - type: Transform rot: -1.5707963267948966 rad pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6177 components: - type: Transform rot: 1.5707963267948966 rad pos: -31.5,28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6178 components: - type: Transform rot: -1.5707963267948966 rad pos: -28.5,28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6179 components: - type: Transform rot: -1.5707963267948966 rad pos: 7.5,-16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6180 components: - type: Transform pos: -31.5,30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6181 components: - type: Transform rot: 3.141592653589793 rad pos: -31.5,31.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6182 components: - type: Transform rot: 1.5707963267948966 rad pos: -31.5,31.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6183 components: - type: Transform rot: 1.5707963267948966 rad pos: -31.5,30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6184 components: - type: Transform rot: -1.5707963267948966 rad pos: -31.5,30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6185 components: - type: Transform rot: -1.5707963267948966 rad pos: -31.5,31.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6186 components: - type: Transform rot: 3.141592653589793 rad pos: -30.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6187 components: - type: Transform rot: 3.141592653589793 rad pos: -29.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6188 components: - type: Transform rot: 1.5707963267948966 rad pos: -29.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6189 components: - type: Transform pos: -30.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6190 components: - type: Transform rot: -1.5707963267948966 rad pos: -30.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6191 components: - type: Transform pos: -29.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6192 components: - type: Transform rot: -1.5707963267948966 rad pos: -37.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6193 components: - type: Transform pos: -35.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6194 components: - type: Transform pos: -34.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6195 components: - type: Transform rot: -1.5707963267948966 rad pos: -35.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6196 components: - type: Transform rot: 1.5707963267948966 rad pos: -34.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6197 components: - type: Transform rot: 3.141592653589793 rad pos: -34.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6198 components: - type: Transform rot: 3.141592653589793 rad pos: -35.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6199 components: - type: Transform rot: 1.5707963267948966 rad pos: -37.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6200 components: - type: Transform rot: 1.5707963267948966 rad pos: -37.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6201 components: - type: Transform rot: -1.5707963267948966 rad pos: -37.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6202 components: - type: Transform pos: -37.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6203 components: - type: Transform rot: 3.141592653589793 rad pos: -37.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6204 components: - type: Transform rot: 3.141592653589793 rad pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6205 components: - type: Transform rot: 1.5707963267948966 rad pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6206 components: - type: Transform rot: -1.5707963267948966 rad pos: -26.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6207 components: - type: Transform rot: -1.5707963267948966 rad pos: -26.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6208 components: - type: Transform rot: -1.5707963267948966 rad pos: -26.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6209 components: - type: Transform rot: 3.141592653589793 rad pos: -26.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6210 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6211 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6212 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6213 components: - type: Transform pos: -26.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6214 components: - type: Transform rot: 3.141592653589793 rad pos: -35.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6215 components: - type: Transform rot: 1.5707963267948966 rad pos: -35.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6216 components: - type: Transform rot: -1.5707963267948966 rad pos: -35.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6217 components: - type: Transform rot: 1.5707963267948966 rad pos: -32.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6218 components: - type: Transform rot: 3.141592653589793 rad pos: -32.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6219 components: - type: Transform rot: 3.141592653589793 rad pos: -33.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6220 components: - type: Transform pos: -32.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 8377 components: - type: Transform pos: -3.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8378 components: - type: Transform pos: -2.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8379 components: - type: Transform pos: -1.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8380 components: - type: Transform pos: -0.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8381 components: - type: Transform rot: 3.141592653589793 rad pos: -3.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8382 components: - type: Transform rot: 3.141592653589793 rad pos: -2.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8383 components: - type: Transform rot: 3.141592653589793 rad pos: -1.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8384 components: - type: Transform rot: 3.141592653589793 rad pos: -0.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8385 components: - type: Transform rot: 1.5707963267948966 rad pos: -0.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8386 components: - type: Transform rot: -1.5707963267948966 rad pos: -3.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8591 components: - type: Transform pos: -11.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8592 components: - type: Transform pos: -10.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8593 components: - type: Transform pos: -9.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8594 components: - type: Transform pos: -8.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8595 components: - type: Transform rot: 3.141592653589793 rad pos: -11.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8596 components: - type: Transform rot: 3.141592653589793 rad pos: -10.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8597 components: - type: Transform rot: 3.141592653589793 rad pos: -9.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8598 components: - type: Transform rot: 3.141592653589793 rad pos: -8.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8599 components: - type: Transform rot: 1.5707963267948966 rad pos: -8.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8600 components: - type: Transform rot: -1.5707963267948966 rad pos: -11.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8601 components: - type: Transform rot: -1.5707963267948966 rad pos: -9.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8602 components: - type: Transform rot: 1.5707963267948966 rad pos: -8.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8603 components: - type: Transform pos: -8.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8604 components: - type: Transform pos: -9.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8605 components: - type: Transform rot: 3.141592653589793 rad pos: -9.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8606 components: - type: Transform rot: 3.141592653589793 rad pos: -8.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - proto: WindowFrostedDirectional entities: - uid: 6221 @@ -60484,233 +60026,173 @@ entities: rot: 3.141592653589793 rad pos: -52.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7748 components: - type: Transform rot: 1.5707963267948966 rad pos: 0.5,-6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7749 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7750 components: - type: Transform rot: 1.5707963267948966 rad pos: -8.5,0.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7751 components: - type: Transform rot: -1.5707963267948966 rad pos: 2.5,10.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7752 components: - type: Transform rot: 3.141592653589793 rad pos: 3.5,11.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7753 components: - type: Transform rot: 3.141592653589793 rad pos: 4.5,11.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7754 components: - type: Transform rot: 1.5707963267948966 rad pos: 4.5,11.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7755 components: - type: Transform rot: -1.5707963267948966 rad pos: 3.5,11.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7756 components: - type: Transform rot: 3.141592653589793 rad pos: 2.5,10.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7757 components: - type: Transform pos: 2.5,10.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7758 components: - type: Transform rot: -1.5707963267948966 rad pos: -1.5,-6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7759 components: - type: Transform rot: -1.5707963267948966 rad pos: 9.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7760 components: - type: Transform rot: 3.141592653589793 rad pos: 9.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7761 components: - type: Transform pos: 9.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7762 components: - type: Transform rot: 3.141592653589793 rad pos: 5.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7763 components: - type: Transform pos: 3.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7764 components: - type: Transform rot: 3.141592653589793 rad pos: 4.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7765 components: - type: Transform pos: 4.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7766 components: - type: Transform rot: 3.141592653589793 rad pos: 3.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7767 components: - type: Transform pos: 5.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7768 components: - type: Transform rot: 1.5707963267948966 rad pos: 17.5,0.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7769 components: - type: Transform pos: -1.5,9.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7770 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,9.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7771 components: - type: Transform rot: -1.5707963267948966 rad pos: 15.5,8.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7772 components: - type: Transform pos: 15.5,8.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7773 components: - type: Transform rot: 3.141592653589793 rad pos: 15.5,8.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7774 components: - type: Transform rot: 1.5707963267948966 rad pos: 15.5,8.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7775 components: - type: Transform rot: -1.5707963267948966 rad pos: -1.5,9.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7776 components: - type: Transform rot: 3.141592653589793 rad pos: -1.5,9.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - proto: WindowReinforcedDirectional entities: - uid: 6222 @@ -60718,202 +60200,150 @@ entities: - type: Transform pos: -13.5,20.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6223 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6224 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.5,21.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6225 components: - type: Transform pos: -17.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6226 components: - type: Transform pos: 12.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6227 components: - type: Transform rot: 1.5707963267948966 rad pos: -7.5,-3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6228 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6229 components: - type: Transform rot: -1.5707963267948966 rad pos: 6.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6230 components: - type: Transform rot: 1.5707963267948966 rad pos: 5.5,-13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6231 components: - type: Transform rot: 1.5707963267948966 rad pos: 4.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6232 components: - type: Transform rot: -1.5707963267948966 rad pos: 2.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6233 components: - type: Transform rot: 3.141592653589793 rad pos: -17.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6234 components: - type: Transform rot: 3.141592653589793 rad pos: 1.5,-12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6235 components: - type: Transform rot: 3.141592653589793 rad pos: 1.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6236 components: - type: Transform rot: 1.5707963267948966 rad pos: 1.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6237 components: - type: Transform pos: 1.5,-13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6238 components: - type: Transform pos: 1.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6239 components: - type: Transform pos: -9.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6240 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6241 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6242 components: - type: Transform rot: 1.5707963267948966 rad pos: -3.5,12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6243 components: - type: Transform rot: 1.5707963267948966 rad pos: -3.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6244 components: - type: Transform rot: 3.141592653589793 rad pos: -2.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6245 components: - type: Transform rot: 1.5707963267948966 rad pos: -3.5,11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6246 components: - type: Transform pos: -2.5,11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6247 components: - type: Transform rot: 3.141592653589793 rad pos: -10.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - type: Occluder boundingBox: -0.5,-0.5,0.5,-0.3 - uid: 6248 @@ -60922,8 +60352,6 @@ entities: rot: 3.141592653589793 rad pos: -8.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - type: Occluder boundingBox: -0.5,-0.5,0.5,-0.3 - uid: 6249 @@ -60932,8 +60360,6 @@ entities: rot: 3.141592653589793 rad pos: -9.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - type: Occluder boundingBox: -0.5,-0.5,0.5,-0.3 - uid: 6250 @@ -60942,442 +60368,330 @@ entities: rot: -1.5707963267948966 rad pos: 11.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6251 components: - type: Transform rot: 3.141592653589793 rad pos: 11.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6252 components: - type: Transform rot: 1.5707963267948966 rad pos: 11.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6253 components: - type: Transform pos: 11.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6254 components: - type: Transform pos: 7.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6255 components: - type: Transform rot: -1.5707963267948966 rad pos: 7.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6256 components: - type: Transform rot: 3.141592653589793 rad pos: 7.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6257 components: - type: Transform rot: 1.5707963267948966 rad pos: 7.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6258 components: - type: Transform rot: 1.5707963267948966 rad pos: -2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6259 components: - type: Transform pos: -2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6260 components: - type: Transform rot: -1.5707963267948966 rad pos: -2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6261 components: - type: Transform rot: 3.141592653589793 rad pos: -2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6262 components: - type: Transform rot: 3.141592653589793 rad pos: 21.5,-3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6263 components: - type: Transform rot: 3.141592653589793 rad pos: 12.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6264 components: - type: Transform rot: 1.5707963267948966 rad pos: 12.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6265 components: - type: Transform rot: 1.5707963267948966 rad pos: 12.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6266 components: - type: Transform rot: 1.5707963267948966 rad pos: 18.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6267 components: - type: Transform rot: 1.5707963267948966 rad pos: 18.5,12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6268 components: - type: Transform rot: 1.5707963267948966 rad pos: 21.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6269 components: - type: Transform rot: 1.5707963267948966 rad pos: 21.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6270 components: - type: Transform pos: 7.5,-10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6271 components: - type: Transform rot: 3.141592653589793 rad pos: 7.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6272 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6273 components: - type: Transform rot: 3.141592653589793 rad pos: 8.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6274 components: - type: Transform rot: 3.141592653589793 rad pos: 9.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6275 components: - type: Transform rot: 1.5707963267948966 rad pos: 33.5,-7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6276 components: - type: Transform pos: 34.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7777 components: - type: Transform rot: -1.5707963267948966 rad pos: 6.5,6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7778 components: - type: Transform rot: -1.5707963267948966 rad pos: 6.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7779 components: - type: Transform rot: 3.141592653589793 rad pos: 6.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7780 components: - type: Transform rot: 3.141592653589793 rad pos: 3.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7781 components: - type: Transform rot: 3.141592653589793 rad pos: 5.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7782 components: - type: Transform rot: 1.5707963267948966 rad pos: 16.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7783 components: - type: Transform rot: 3.141592653589793 rad pos: -0.5,-6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 8093 components: - type: Transform rot: -1.5707963267948966 rad pos: 3.5,7.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8094 components: - type: Transform rot: -1.5707963267948966 rad pos: 2.5,10.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8095 components: - type: Transform rot: -1.5707963267948966 rad pos: -1.5,10.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8096 components: - type: Transform rot: -1.5707963267948966 rad pos: -2.5,7.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8097 components: - type: Transform rot: -1.5707963267948966 rad pos: -3.5,4.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8098 components: - type: Transform rot: 1.5707963267948966 rad pos: -3.5,4.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8099 components: - type: Transform rot: 1.5707963267948966 rad pos: -2.5,7.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8100 components: - type: Transform rot: 1.5707963267948966 rad pos: 4.5,4.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8101 components: - type: Transform rot: -1.5707963267948966 rad pos: 4.5,4.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8102 components: - type: Transform rot: 1.5707963267948966 rad pos: 3.5,7.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8103 components: - type: Transform rot: 1.5707963267948966 rad pos: 2.5,10.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8104 components: - type: Transform rot: 1.5707963267948966 rad pos: 1.5,13.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8105 components: - type: Transform rot: 1.5707963267948966 rad pos: 1.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8106 components: - type: Transform pos: 0.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8107 components: - type: Transform rot: -1.5707963267948966 rad pos: -0.5,13.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8108 components: - type: Transform rot: -1.5707963267948966 rad pos: -0.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8109 components: - type: Transform rot: 3.141592653589793 rad pos: -0.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8110 components: - type: Transform rot: 3.141592653589793 rad pos: 0.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8111 components: - type: Transform rot: 3.141592653589793 rad pos: 1.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8112 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,13.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8113 components: - type: Transform rot: 1.5707963267948966 rad pos: -0.5,13.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8114 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,10.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - proto: Wirecutter entities: - uid: 6277 diff --git a/Resources/Maps/Shuttles/emergency_delta.yml b/Resources/Maps/Shuttles/emergency_delta.yml index c2d2628fb9..1797ce0ecb 100644 --- a/Resources/Maps/Shuttles/emergency_delta.yml +++ b/Resources/Maps/Shuttles/emergency_delta.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Grid - engineVersion: 266.0.0 + engineVersion: 275.2.0 forkId: "" forkVersion: "" - time: 09/17/2025 04:14:52 - entityCount: 957 + time: 04/05/2026 13:24:37 + entityCount: 959 maps: [] grids: - 1 @@ -516,6 +516,9 @@ entities: - type: GasTileOverlay - type: RadiationGridResistance - type: ImplicitRoof + - type: TileHistory + chunkHistory: {} + - type: ExplosionAirtightGrid - proto: AirCanister entities: - uid: 326 @@ -523,10 +526,10 @@ entities: - type: Transform pos: -13.5,-3.5 parent: 1 - - uid: 445 + - uid: 643 components: - type: Transform - pos: -1.5,-18.5 + pos: 0.5,-20.5 parent: 1 - proto: AirlockBrigGlassLocked entities: @@ -735,6 +738,14 @@ entities: parent: 1 - type: Fixtures fixtures: {} + - uid: 795 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,0.5 + parent: 1 + - type: Fixtures + fixtures: {} - proto: AtmosDeviceFanDirectional entities: - uid: 71 @@ -1714,6 +1725,11 @@ entities: - type: Transform pos: -2.5,-22.5 parent: 1 + - uid: 802 + components: + - type: Transform + pos: -9.5,0.5 + parent: 1 - proto: CableHV entities: - uid: 33 @@ -2223,6 +2239,16 @@ entities: - type: Transform pos: -4.5,-17.5 parent: 1 + - uid: 958 + components: + - type: Transform + pos: -9.5,0.5 + parent: 1 + - uid: 959 + components: + - type: Transform + pos: -8.5,0.5 + parent: 1 - proto: CableTerminal entities: - uid: 277 @@ -3134,11 +3160,11 @@ entities: parent: 1 - proto: GasOutletInjector entities: - - uid: 801 + - uid: 687 components: - type: Transform rot: -1.5707963267948966 rad - pos: 1.5,-18.5 + pos: 1.5,-20.5 parent: 1 - proto: GasPassiveVent entities: @@ -3146,10 +3172,16 @@ entities: components: - type: Transform rot: -1.5707963267948966 rad - pos: 1.5,-20.5 + pos: 1.5,-19.5 parent: 1 - proto: GasPipeBend entities: + - uid: 680 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-20.5 + parent: 1 - uid: 856 components: - type: Transform @@ -3181,7 +3213,7 @@ entities: components: - type: Transform rot: -1.5707963267948966 rad - pos: 0.5,-20.5 + pos: -0.5,-19.5 parent: 1 - uid: 514 components: @@ -3189,45 +3221,28 @@ entities: rot: 3.141592653589793 rad pos: -2.5,-16.5 parent: 1 - - uid: 643 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-20.5 - parent: 1 - uid: 644 components: - type: Transform rot: -1.5707963267948966 rad - pos: -0.5,-20.5 + pos: -1.5,-19.5 parent: 1 - uid: 675 components: - type: Transform pos: -2.5,-17.5 parent: 1 - - uid: 680 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-18.5 - parent: 1 - uid: 699 components: - type: Transform - pos: -2.5,-19.5 + rot: -1.5707963267948966 rad + pos: 0.5,-19.5 parent: 1 - uid: 707 components: - type: Transform pos: -2.5,-18.5 parent: 1 - - uid: 802 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-18.5 - parent: 1 - uid: 840 components: - type: Transform @@ -3627,11 +3642,11 @@ entities: parent: 1 - proto: GasPipeTJunction entities: - - uid: 795 + - uid: 445 components: - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-20.5 + rot: 1.5707963267948966 rad + pos: -2.5,-19.5 parent: 1 - uid: 841 components: @@ -3741,11 +3756,11 @@ entities: parent: 1 - proto: GasPort entities: - - uid: 687 + - uid: 460 components: - type: Transform rot: 1.5707963267948966 rad - pos: -1.5,-18.5 + pos: 0.5,-20.5 parent: 1 - proto: GasVentPump entities: @@ -4270,32 +4285,24 @@ entities: rot: -1.5707963267948966 rad pos: 1.5,-18.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 119 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-20.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 130 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-19.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 293 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-21.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - proto: PosterLegitEnlist entities: - uid: 788 @@ -4657,393 +4664,281 @@ entities: - type: Transform pos: 2.5,1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 7 components: - type: Transform pos: 2.5,2.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 15 components: - type: Transform pos: -3.5,1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 18 components: - type: Transform pos: 0.5,3.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 19 components: - type: Transform pos: -0.5,3.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 81 components: - type: Transform pos: -0.5,-1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 82 components: - type: Transform pos: -1.5,-2.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 101 components: - type: Transform pos: -12.5,3.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 142 components: - type: Transform pos: -4.5,-1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 170 components: - type: Transform pos: -1.5,6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 171 components: - type: Transform pos: -10.5,6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 173 components: - type: Transform pos: -10.5,7.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 174 components: - type: Transform pos: -1.5,7.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 182 components: - type: Transform pos: -8.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 183 components: - type: Transform pos: -3.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 184 components: - type: Transform pos: -4.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 185 components: - type: Transform pos: -5.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 186 components: - type: Transform pos: -6.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 187 components: - type: Transform pos: -7.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 261 components: - type: Transform pos: -14.5,-3.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 262 components: - type: Transform pos: -14.5,-4.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 263 components: - type: Transform pos: -14.5,-5.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 264 components: - type: Transform pos: -14.5,-6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 265 components: - type: Transform pos: -14.5,-7.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 267 components: - type: Transform pos: -1.5,-22.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 274 components: - type: Transform pos: -6.5,-1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 327 components: - type: Transform pos: -2.5,-22.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 330 components: - type: Transform pos: -9.5,-11.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 331 components: - type: Transform pos: -9.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 338 components: - type: Transform pos: -11.5,-17.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 339 components: - type: Transform pos: -12.5,-17.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 342 components: - type: Transform pos: -14.5,-12.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 343 components: - type: Transform pos: -14.5,-13.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 344 components: - type: Transform pos: -14.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 345 components: - type: Transform pos: -14.5,-15.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 430 components: - type: Transform pos: -8.5,-17.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 432 components: - type: Transform pos: -6.5,-17.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 469 components: - type: Transform pos: 2.5,-4.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 470 components: - type: Transform pos: 2.5,-5.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 471 components: - type: Transform pos: 2.5,-6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 478 components: - type: Transform pos: 2.5,-13.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 479 components: - type: Transform pos: 2.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 513 components: - type: Transform pos: -1.5,-8.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 515 components: - type: Transform pos: -1.5,-4.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 530 components: - type: Transform pos: -5.5,-10.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 533 components: - type: Transform pos: -1.5,-10.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 535 components: - type: Transform pos: -1.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 557 components: - type: Transform pos: -5.5,-4.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 712 components: - type: Transform pos: -8.5,-1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 760 components: - type: Transform pos: -9.5,-7.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 764 components: - type: Transform pos: -9.5,-6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 817 components: - type: Transform pos: -5.5,-8.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 819 components: - type: Transform pos: -5.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 820 components: - type: Transform pos: -5.5,-16.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 821 components: - type: Transform pos: -1.5,-16.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 837 components: - type: Transform pos: -5.5,-2.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - proto: SignBridge entities: - uid: 11 @@ -5064,10 +4959,10 @@ entities: fixtures: {} - proto: SignEVA entities: - - uid: 460 + - uid: 801 components: - type: Transform - pos: -9.5,0.5 + pos: -9.5,2.5 parent: 1 - type: Fixtures fixtures: {} @@ -5952,8 +5847,6 @@ entities: rot: 1.5707963267948966 rad pos: -1.5,-20.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - proto: WindoorSecureSecurityLocked entities: - uid: 37 @@ -5962,8 +5855,6 @@ entities: rot: 3.141592653589793 rad pos: -2.5,1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - proto: WindowReinforcedDirectional entities: - uid: 660 @@ -5972,30 +5863,22 @@ entities: rot: 1.5707963267948966 rad pos: -1.5,-19.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 682 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,-18.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 746 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,-21.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 755 components: - type: Transform rot: -1.5707963267948966 rad pos: -1.5,2.5 parent: 1 - - type: DeltaPressure - gridUid: 1 ... diff --git a/Resources/Prototypes/Actions/station_ai.yml b/Resources/Prototypes/Actions/station_ai.yml index 4dbaf07aab..9e9bc9f650 100644 --- a/Resources/Prototypes/Actions/station_ai.yml +++ b/Resources/Prototypes/Actions/station_ai.yml @@ -14,34 +14,6 @@ - type: InstantAction event: !type:JumpToCoreEvent -- type: entity - parent: BaseAction - id: ActionSurvCameraLights - name: Toggle camera lights - description: Enable surveillance camera lights near wherever you're viewing. - components: - - type: Action - priority: -5 - itemIconStyle: BigAction - icon: - sprite: Interface/Actions/actions_ai.rsi - state: camera_light - - type: InstantAction - event: !type:RelayedActionComponentChangeEvent - components: - - type: LightOnCollideCollider - - type: FixturesChange - fixtures: - lightTrigger: - shape: - !type:PhysShapeCircle - radius: 0.35 - density: 80 - hard: false - layer: - - GhostImpassable - - - type: entity parent: BaseMentalAction id: ActionAIViewLaws diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index 3ded7c11bf..1b8d3994fe 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -439,6 +439,17 @@ - type: InstantAction event: !type:GravityJumpEvent {} +- type: entity + parent: ActionGravityJump + id: ActionJumpBoost + name: Jump boost + components: + - type: Action + useDelay: 16 + icon: + sprite: Interface/Actions/actions_borg.rsi + state: xenoborg-jump-module + - type: entity parent: BaseAction id: ActionVulpkaninGravityJump diff --git a/Resources/Prototypes/Alerts/changeling.yml b/Resources/Prototypes/Alerts/changeling.yml new file mode 100644 index 0000000000..b6702fb4b4 --- /dev/null +++ b/Resources/Prototypes/Alerts/changeling.yml @@ -0,0 +1,13 @@ +# for ChangelingFleshClothingAbilityComponent +- type: alert + id: ChangelingFleshClothing + clickEvent: !type:ToggleFleshClothingEvent + icons: + - sprite: /Textures/Interface/Alerts/changeling.rsi + state: flesh-clothing-off + - sprite: /Textures/Interface/Alerts/changeling.rsi + state: flesh-clothing-on + name: changeling-flesh-clothing-alert-name + description: changeling-flesh-clothing-alert-desc + minSeverity: 0 + maxSeverity: 1 diff --git a/Resources/Prototypes/Atmospherics/gases.yml b/Resources/Prototypes/Atmospherics/gases.yml index f27bab9074..b217074ca8 100644 --- a/Resources/Prototypes/Atmospherics/gases.yml +++ b/Resources/Prototypes/Atmospherics/gases.yml @@ -1,105 +1,119 @@ - type: gas id: Oxygen - name: gases-oxygen - specificHeat: 20 + name: gas-oxygen + abbreviation: gas-oxygen-abbreviation + molarHeatCapacity: 20 heatCapacityRatio: 1.4 molarMass: 32 - color: 2887E8 + color: '#2887E8' reagent: Oxygen pricePerMole: 0 isOxidizer: true - type: gas id: Nitrogen - name: gases-nitrogen - specificHeat: 30 + name: gas-nitrogen + abbreviation: gas-nitrogen-abbreviation + molarHeatCapacity: 30 heatCapacityRatio: 1.4 molarMass: 28 - color: DA1010 + color: '#DA1010' reagent: Nitrogen pricePerMole: 0 - type: gas id: CarbonDioxide - name: gases-co2 - specificHeat: 30 + name: gas-carbon-dioxide + abbreviation: gas-carbon-dioxide-abbreviation + molarHeatCapacity: 30 heatCapacityRatio: 1.3 molarMass: 44 - color: 4e4e4e + color: '#4e4e4e' reagent: CarbonDioxide pricePerMole: 0 - type: gas id: Plasma - name: gases-plasma - specificHeat: 200 + name: gas-plasma + abbreviation: gas-plasma-abbreviation + molarHeatCapacity: 200 heatCapacityRatio: 1.7 molarMass: 120 - gasOverlaySprite: /Textures/Effects/atmospherics.rsi - gasOverlayState: plasma - color: FF3300 + gasOverlaySprite: + sprite: /Textures/Effects/atmospherics.rsi + state: plasma + color: '#FF3300' reagent: Plasma pricePerMole: 0 isFuel: true - type: gas id: Tritium - name: gases-tritium - specificHeat: 10 + name: gas-tritium + abbreviation: gas-tritium-abbreviation + molarHeatCapacity: 10 heatCapacityRatio: 1.3 - molarMass: 6 - gasOverlaySprite: /Textures/Effects/atmospherics.rsi - gasOverlayState: tritium - color: 13FF4B + molarMass: 3 + gasOverlaySprite: + sprite: /Textures/Effects/atmospherics.rsi + state: tritium + color: '#13FF4B' reagent: Tritium pricePerMole: 2.5 isFuel: true - type: gas id: WaterVapor - name: gases-water-vapor - specificHeat: 40 + name: gas-water-vapor + abbreviation: gas-water-vapor-abbreviation + molarHeatCapacity: 40 heatCapacityRatio: 1.33 molarMass: 18 - gasOverlaySprite: /Textures/Effects/atmospherics.rsi - gasOverlayState: water_vapor - color: bffffd + gasOverlaySprite: + sprite: /Textures/Effects/atmospherics.rsi + state: water_vapor + color: '#bffffd' reagent: Water pricePerMole: 0 - type: gas id: Ammonia - name: gases-ammonia - specificHeat: 20 + name: gas-ammonia + abbreviation: gas-ammonia-abbreviation + molarHeatCapacity: 20 heatCapacityRatio: 1.4 molarMass: 44 - gasOverlaySprite: /Textures/Effects/atmospherics.rsi - gasOverlayState: miasma + gasOverlaySprite: + sprite: /Textures/Effects/atmospherics.rsi + state: miasma gasMolesVisible: 2 - gasVisbilityFactor: 3.5 - color: 56941E + gasVisibilityFactor: 3.5 + color: '#56941E' reagent: Ammonia pricePerMole: 0.15 - type: gas id: NitrousOxide - name: gases-n2o - specificHeat: 40 + name: gas-nitrous-oxide + abbreviation: gas-nitrous-oxide-abbreviation + molarHeatCapacity: 40 heatCapacityRatio: 1.3 molarMass: 44 - color: 8F00FF + color: '#8F00FF' reagent: NitrousOxide pricePerMole: 0.1 - type: gas id: Frezon - name: gases-frezon - specificHeat: 600 # Strongest by far + name: gas-frezon + abbreviation: gas-frezon-abbreviation + molarHeatCapacity: 600 # Strongest by far heatCapacityRatio: 1.33 molarMass: 50 - gasOverlaySprite: /Textures/Effects/atmospherics.rsi - gasOverlayState: frezon + gasOverlaySprite: + sprite: /Textures/Effects/atmospherics.rsi + state: frezon gasMolesVisible: 0.6 - color: 3a758c + color: '#3a758c' reagent: Frezon pricePerMole: 1 diff --git a/Resources/Prototypes/Body/Species/arachnid.yml b/Resources/Prototypes/Body/Species/arachnid.yml index 734c230af3..c46338be2a 100644 --- a/Resources/Prototypes/Body/Species/arachnid.yml +++ b/Resources/Prototypes/Body/Species/arachnid.yml @@ -36,6 +36,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceArachnid + categories: [ HideSpawnMenu ] name: arachnid appearance components: - type: Inventory @@ -137,6 +138,10 @@ Unsexed: UnisexArachnid - type: TypingIndicator proto: spider + - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_arachnid #WL-Changes: Languages start - type: Languages speaking: diff --git a/Resources/Prototypes/Body/Species/diona.yml b/Resources/Prototypes/Body/Species/diona.yml index deeb923c26..24ee4afc44 100644 --- a/Resources/Prototypes/Body/Species/diona.yml +++ b/Resources/Prototypes/Body/Species/diona.yml @@ -35,6 +35,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceDiona + categories: [ HideSpawnMenu ] name: diona appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/dwarf.yml b/Resources/Prototypes/Body/Species/dwarf.yml index 1d64142ca9..5b053f0735 100644 --- a/Resources/Prototypes/Body/Species/dwarf.yml +++ b/Resources/Prototypes/Body/Species/dwarf.yml @@ -1,6 +1,7 @@ - type: entity parent: AppearanceHuman id: AppearanceDwarf + categories: [ HideSpawnMenu ] name: dwarf appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/gingerbread.yml b/Resources/Prototypes/Body/Species/gingerbread.yml index b32cbac91d..5c429af01c 100644 --- a/Resources/Prototypes/Body/Species/gingerbread.yml +++ b/Resources/Prototypes/Body/Species/gingerbread.yml @@ -1,6 +1,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceGingerbread + categories: [ HideSpawnMenu ] name: gingerbread appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/human.yml b/Resources/Prototypes/Body/Species/human.yml index fbd4f59516..a0fe57b9c9 100644 --- a/Resources/Prototypes/Body/Species/human.yml +++ b/Resources/Prototypes/Body/Species/human.yml @@ -2,6 +2,9 @@ parent: Undergarments id: Human limits: + enum.HumanoidVisualLayers.HeadTop: + limit: 1 + required: false enum.HumanoidVisualLayers.Hair: limit: 1 required: false @@ -48,6 +51,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceHuman + categories: [ HideSpawnMenu ] name: human appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/moth.yml b/Resources/Prototypes/Body/Species/moth.yml index f772859c41..6c71987fbc 100644 --- a/Resources/Prototypes/Body/Species/moth.yml +++ b/Resources/Prototypes/Body/Species/moth.yml @@ -51,6 +51,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceMoth + categories: [ HideSpawnMenu ] name: moth appearance components: - type: Inventory @@ -148,6 +149,10 @@ allowedEmotes: ['Chitter', 'Squeak', 'Flap'] - type: TypingIndicator proto: moth + - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_moth - type: Bloodstream bloodReferenceSolution: reagents: diff --git a/Resources/Prototypes/Body/Species/reptilian.yml b/Resources/Prototypes/Body/Species/reptilian.yml index 8fcff8133b..03d32c4062 100644 --- a/Resources/Prototypes/Body/Species/reptilian.yml +++ b/Resources/Prototypes/Body/Species/reptilian.yml @@ -54,6 +54,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceReptilian + categories: [ HideSpawnMenu ] name: reptilian appearance components: - type: Inventory @@ -150,6 +151,10 @@ allowedEmotes: ['Thump'] - type: TypingIndicator proto: lizard + - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_lizard - type: Vocal sounds: Male: MaleReptilian diff --git a/Resources/Prototypes/Body/Species/skeleton.yml b/Resources/Prototypes/Body/Species/skeleton.yml index 0288e2a1f0..b49b08b30a 100644 --- a/Resources/Prototypes/Body/Species/skeleton.yml +++ b/Resources/Prototypes/Body/Species/skeleton.yml @@ -42,6 +42,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceSkeletonPerson + categories: [ HideSpawnMenu ] name: skeletonperson appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/slime.yml b/Resources/Prototypes/Body/Species/slime.yml index 0c7e1dbcb7..259304ed85 100644 --- a/Resources/Prototypes/Body/Species/slime.yml +++ b/Resources/Prototypes/Body/Species/slime.yml @@ -54,6 +54,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceSlimePerson + categories: [ HideSpawnMenu ] name: SlimePerson appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/vox.yml b/Resources/Prototypes/Body/Species/vox.yml index c812581b10..d5676fdbe5 100644 --- a/Resources/Prototypes/Body/Species/vox.yml +++ b/Resources/Prototypes/Body/Species/vox.yml @@ -74,6 +74,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceVox + categories: [ HideSpawnMenu ] name: vox appearance components: - type: Inventory @@ -170,6 +171,10 @@ allowedEmotes: ['Click', 'Chitter'] - type: TypingIndicator proto: vox + - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_vox - type: Vocal sounds: Male: UnisexVox @@ -317,6 +322,12 @@ dependentHidingLayers: enum.HumanoidVisualLayers.Snout: - enum.HumanoidVisualLayers.SnoutCover + markingsDisplacement: + Hair: + sizeMaps: + 32: + sprite: Mobs/Species/Vox/displacement.rsi + state: hair - type: entity parent: [ OrganBaseArmLeft, OrganVoxExternal ] diff --git a/Resources/Prototypes/Body/Species/vulpkanin.yml b/Resources/Prototypes/Body/Species/vulpkanin.yml index 57060d7f55..688e94e190 100644 --- a/Resources/Prototypes/Body/Species/vulpkanin.yml +++ b/Resources/Prototypes/Body/Species/vulpkanin.yml @@ -63,6 +63,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceVulpkanin + categories: [ HideSpawnMenu ] name: vulpkanin appearance components: - type: InitialBody @@ -233,6 +234,10 @@ reagents: - ReagentId: SulfurBlood Quantity: 300 + - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_vulpkanin # Corvax-Vulp_Port start - type: GrowlingAccent - type: Respirator @@ -310,6 +315,12 @@ dependentHidingLayers: enum.HumanoidVisualLayers.Snout: - enum.HumanoidVisualLayers.SnoutCover + markingsDisplacement: + Hair: + sizeMaps: + 32: + sprite: Mobs/Species/Vulpkanin/displacement.rsi + state: hair - type: entity parent: [ OrganBaseArmLeft, OrganVulpkaninExternal ] diff --git a/Resources/Prototypes/Body/species_appearance.yml b/Resources/Prototypes/Body/species_appearance.yml index 43815b08f5..674b75f4c2 100644 --- a/Resources/Prototypes/Body/species_appearance.yml +++ b/Resources/Prototypes/Body/species_appearance.yml @@ -69,11 +69,6 @@ state: body-overlay-2 visible: false - - map: [ "clownedon" ] # Dynamically generated - sprite: "Effects/creampie.rsi" - state: "creampie_human" - visible: false - - type: entity id: BaseSpeciesAppearance parent: diff --git a/Resources/Prototypes/Body/species_base.yml b/Resources/Prototypes/Body/species_base.yml index febf438b64..cc8407998e 100644 --- a/Resources/Prototypes/Body/species_base.yml +++ b/Resources/Prototypes/Body/species_base.yml @@ -26,12 +26,6 @@ color: "#FF0000" Burn: sprite: Mobs/Effects/burn_damage.rsi - - type: GenericVisualizer - visuals: - enum.CreamPiedVisuals.Creamed: - clownedon: - True: { visible: true } - False: { visible: false } - type: StatusIcon bounds: -0.5,-0.5,0.5,0.5 - type: JobStatus @@ -85,7 +79,6 @@ - TemporaryBlindness - Pacified - Flashed - - RadiationProtection - Adrenaline - type: Identity - type: IdExaminable @@ -127,6 +120,9 @@ factions: - NanoTrasen - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_human - type: ParcelWrapOverride parcelPrototype: WrappedParcelHumanoid wrapDelay: 5 @@ -159,6 +155,8 @@ - type: MobPrice price: 1500 # Kidnapping a living person and selling them for cred is a good move. deathPenalty: 0.01 # However they really ought to be living and intact, otherwise they're worth 100x less. + - type: RandomPrice + maxRandomPrice: 20000 - type: Tag tags: - CanPilot diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml index 516765e268..8b4df9f89f 100644 --- a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml +++ b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml @@ -307,6 +307,7 @@ whitelist: tags: - TrashBag + - TrashBagBlue - type: Sprite layers: - state: box diff --git a/Resources/Prototypes/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/Catalog/Fills/Crates/service.yml index 9d53c1e2b4..1f1432e709 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/service.yml @@ -24,7 +24,6 @@ amount: 2 - id: BoxCleanerGrenades - id: WireBrush - amount: 2 - type: entity parent: CratePlastic diff --git a/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml b/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml index 2bbcc3b13d..f377d96db9 100644 --- a/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml +++ b/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml @@ -26,7 +26,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # 31 minutes volume: 5 @@ -40,7 +40,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # 4 minutes volume: 0.66 @@ -54,7 +54,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # 4 minutes volume: 0.66 @@ -69,7 +69,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # 9 minutes volume: 1.5 @@ -83,7 +83,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # 9 minutes volume: 1.5 @@ -98,7 +98,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # 15 minutes volume: 2.5 @@ -112,7 +112,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # 15 minutes volume: 2.5 @@ -126,7 +126,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 22.4 + releasePressure: 22.4 air: # 4 minutes volume: 0.66 @@ -142,7 +142,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 101.3 + releasePressure: 101.3 air: # 6 minutes due to output pressure volume: 5 @@ -178,7 +178,7 @@ # * 101.325 | one atmosphere # __________ # 30.3975 optimal output pressure - outputPressure: 30.4 + releasePressure: 30.4 air: # only 22 minutes due to pressure volume: 5 @@ -195,10 +195,95 @@ suffix: Filled components: - type: GasTank - outputPressure: 101.3 + releasePressure: 101.3 air: # 6 minutes of agony volume: 5 moles: Plasma: 2.051379050 temperature: 293.15 + +- type: entity + parent: AirTank + id: MaxCap # As of currently writing, tanks have an explosion intensity of about 125, which is about as powerful as an explosive grenade. + suffix: DEBUG, Max Cap + components: + - type: GasTank + air: + volume: 5 + moles: + Oxygen: 1.0 # If trit fire burn ratio is ever changed, this value will need to be adjusted. + Tritium: 0.5 + temperature: 373.149 + +- type: entity + parent: EmergencyOxygenTank + id: MaxCapSmall # As of currently writing, mini tanks have an explosion intensity of about 40. + suffix: DEBUG, Max Cap + components: + - type: GasTank + air: + volume: 0.66 + moles: + Oxygen: 0.1 # If trit fire burn ratio is ever changed, this value will need to be adjusted. + Tritium: 0.05 + Plasma: 0.06 # High mass to slow flow rate + temperature: 373.149 + +- type: entity + parent: ExtendedEmergencyOxygenTank + id: MaxCapSmallEx # As of currently writing, extended tanks currently have an explosive intensity of about 65 + suffix: DEBUG, Max Cap + components: + - type: GasTank + air: + volume: 1.5 + moles: + Oxygen: 0.41027581 + Tritium: 0.205137905 + temperature: 373.149 + +- type: entity + parent: DoubleEmergencyOxygenTank + id: MaxCapSmallDouble # As of currently writing, extended tanks currently have an explosive intensity of about 85 + suffix: DEBUG, Max Cap + components: + - type: GasTank + air: + volume: 2.5 + moles: + Oxygen: 0.68379301666 + Tritium: 0.34189650833 + temperature: 373.149 + +- type: entity + parent: AirTank + id: MaxCapSilly + name: max cap + suffix: DEBUG, Max Cap, Impossible + components: + - type: GasTank + air: + volume: 5 + moles: + Oxygen: 6 + Tritium: 3 + temperature: 373.149 + +- type: entity + parent: AirTank + id: MaxCapBluespace + name: max cap + suffix: DEBUG, Max Cap, Canister + components: + - type: Explosive + maxIntensity: 200 + - type: GasTank + safetyPressure: 5066.25 + overpressure: 15198.75 + air: + volume: 1500 + moles: + Oxygen: 1400 + Tritium: 700 + temperature: 373.149 diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml index 31f4d1d12c..6228c1a229 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml @@ -92,6 +92,7 @@ - id: LightReplacer - id: BoxLightMixed - id: Holoprojector + - id: WireBrush - !type:AllSelector rolls: 2 children: @@ -101,7 +102,6 @@ - id: SoapNT - id: FlashlightLantern - id: Plunger - - id: WireBrush - id: PlushieLizardJobJanitor prob: 0.02 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml index 7e7bb37162..4ceaeef80c 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml @@ -40,6 +40,7 @@ ClothingUniformJumpsuitDameDane: 2 ClothingShoesDameDane: 2 ClothingOuterDameDane: 2 + ClothingHeadHatMitreClown: 1 ClothingOuterClownPriest: 1 ClothingMaskSadMime: 1 ClothingMaskScaredMime: 1 diff --git a/Resources/Prototypes/Catalog/changeling_catalog.yml b/Resources/Prototypes/Catalog/changeling_catalog.yml index dea0f908eb..57b0addaa6 100644 --- a/Resources/Prototypes/Catalog/changeling_catalog.yml +++ b/Resources/Prototypes/Catalog/changeling_catalog.yml @@ -2,9 +2,8 @@ - type: listing id: ChangelingArmBlade - name: changeling-arm-blade-name - description: changeling-arm-blade-desc - productAction: ActionRetractableItemArmBlade + name: changeling-catalog-arm-blade-name + description: changeling-catalog-arm-blade-desc applyToMob: true cost: ChangelingDNA: 25 @@ -13,3 +12,18 @@ conditions: - !type:ListingLimitedStockCondition stock: 1 + productAction: ActionRetractableItemArmBlade + +- type: listing + id: ChangelingFleshClothing + name: changeling-catalog-flesh-clothing-name + description: changeling-catalog-flesh-clothing-desc + icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: flesh_clothing } + cost: + ChangelingDNA: 10 + categories: + - ChangelingAbilities + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + productComponents: ChangelingFleshClothingAbilityStoreDummy diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index f90e85cc0a..84e5977213 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -1092,6 +1092,16 @@ categories: - UplinkDisruption +- type: listing + id: UplinkTravelCamera + name: uplink-travel-camera-name + description: uplink-travel-camera-desc + productEntity: TravelCamera + cost: + Telecrystal: 1 + categories: + - UplinkDeception + # Note: Removed for the time being until surgery/newmed is added. Considered bloat until then. # - type: listing # id: UplinkDuffelSurgery @@ -2269,3 +2279,20 @@ - !type:BuyerJobCondition whitelist: - Lawyer + +#Objective items + +- type: listing + id: uplinkHijackBeacon + name: uplink-hijack-beacon-name + description: uplink-hijack-beacon-desc + productEntity: HijackBeacon + categories: + - UplinkObjective + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + - !type:BuyerObjectiveWhitelistCondition + whitelist: + components: + - HijackTradeStationCondition diff --git a/Resources/Prototypes/Chat/notifications.yml b/Resources/Prototypes/Chat/notifications.yml index cea67fa0ee..f155b99433 100644 --- a/Resources/Prototypes/Chat/notifications.yml +++ b/Resources/Prototypes/Chat/notifications.yml @@ -32,4 +32,11 @@ message: station-ai-core-critical-power sound: /Audio/Effects/alert.ogg color: Red - nextDelay: 120 \ No newline at end of file + nextDelay: 120 + +- type: chatNotification + id: AiTakingDamage + message: station-ai-core-taking-damage + sound: /Audio/Misc/notice2.ogg + color: Orange + nextDelay: 30 diff --git a/Resources/Prototypes/Corvax/Guidebook/SOP.yml b/Resources/Prototypes/Corvax/Guidebook/SOP.yml index ba4cc9d75b..9a5306b500 100644 --- a/Resources/Prototypes/Corvax/Guidebook/SOP.yml +++ b/Resources/Prototypes/Corvax/Guidebook/SOP.yml @@ -10,6 +10,7 @@ - SOPEngineering - SOPCommand - SOPCentcomm + - SOPDSO - SOPLegal - SOPSecurity - SOPGeneral @@ -55,6 +56,11 @@ name: guide-entry-sop-centcomm text: "/ServerInfo/Corvax/Guidebook/SOP/Centcomm.xml" +- type: guideEntry + id: SOPDSO + name: guide-entry-sop-dso + text: "/ServerInfo/Corvax/Guidebook/SOP/DSO.xml" + - type: guideEntry id: SOPLegal name: guide-entry-sop-legal diff --git a/Resources/Prototypes/Corvax/SoundCollections/growl.yml b/Resources/Prototypes/Corvax/SoundCollections/growl.yml deleted file mode 100644 index 3c72c91a08..0000000000 --- a/Resources/Prototypes/Corvax/SoundCollections/growl.yml +++ /dev/null @@ -1,14 +0,0 @@ -- type: soundCollection - id: CorvaxVulpkaninGrowl - files: - - /Audio/Corvax/Effects/Growl/growl1.ogg - - /Audio/Corvax/Effects/Growl/growl2.ogg - - /Audio/Corvax/Effects/Growl/growl3.ogg -#WL-Changes-Start - - /Audio/_WL/Voice/Vulpkanin/dog_growl1.ogg - - /Audio/_WL/Voice/Vulpkanin/dog_growl2.ogg - - /Audio/_WL/Voice/Vulpkanin/dog_growl3.ogg - - /Audio/_WL/Voice/Vulpkanin/dog_snarl1.ogg - - /Audio/_WL/Voice/Vulpkanin/dog_snarl2.ogg - - /Audio/_WL/Voice/Vulpkanin/dog_snarl3.ogg -#WL-Changes-End diff --git a/Resources/Prototypes/Corvax/Voice/speech_emote_sounds.yml b/Resources/Prototypes/Corvax/Voice/speech_emote_sounds.yml index df3d07c357..def73de064 100644 --- a/Resources/Prototypes/Corvax/Voice/speech_emote_sounds.yml +++ b/Resources/Prototypes/Corvax/Voice/speech_emote_sounds.yml @@ -1,84 +1,3 @@ -# species -- type: emoteSounds - id: MaleCorvaxVulpkanin - params: - variation: 0.125 - sounds: - Scream: - collection: MaleScreams - Laugh: - collection: MaleLaugh - Growl: - collection: CorvaxVulpkaninGrowl - Howl: - path: /Audio/Corvax/Effects/howl.ogg - #WL-Changes-Start - Bark: - collection: Bark - Whine: - path: /Audio/_WL/Voice/Vulpkanin/dog_whine.ogg - #WL-Changes-End - Sneeze: - collection: MaleSneezes - Cough: - collection: MaleCoughs - Yawn: - collection: MaleYawn - Snore: - collection: Snores - Sigh: - collection: MaleSigh - Crying: - collection: MaleCry - Whistle: - collection: Whistles - Weh: - collection: Weh - Gasp: - collection: MaleGasp - DefaultDeathgasp: - collection: MaleDeathGasp - -- type: emoteSounds - id: FemaleCorvaxVulpkanin - params: - variation: 0.125 - sounds: - Scream: - collection: FemaleScreams - Laugh: - collection: FemaleLaugh - Growl: - collection: CorvaxVulpkaninGrowl - Howl: - path: /Audio/Corvax/Effects/howl.ogg - #WL-Changes-Start - Bark: - collection: Bark - Whine: - path: /Audio/_WL/Voice/Vulpkanin/dog_whine.ogg - #WL-Changes-End - Sneeze: - collection: FemaleSneezes - Cough: - collection: FemaleCoughs - Yawn: - collection: FemaleYawn - Snore: - collection: Snores - Sigh: - collection: FemaleSigh - Crying: - collection: FemaleCry - Whistle: - collection: Whistles - Weh: - collection: Weh - Gasp: - collection: FemaleGasp - DefaultDeathgasp: - collection: FemaleDeathGasp - # species - type: emoteSounds id: MaleTajaran diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml index fd0ef4f2d1..0aac5c96d5 100644 --- a/Resources/Prototypes/Damage/modifier_sets.yml +++ b/Resources/Prototypes/Damage/modifier_sets.yml @@ -322,12 +322,6 @@ Heat: 2.5 Caustic: 0.0 -# protects against radiation -- type: damageModifierSet - id: PotassiumIodide - coefficients: - Radiation: 0.1 - - type: damageModifierSet id: ManifestedSpirit coefficients: diff --git a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml index 449ecedbca..47f619bbdf 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml @@ -237,7 +237,7 @@ sprite: Clothing/Belt/militarywebbingmed.rsi - type: entity - parent: ClothingBeltMilitaryWebbing + parent: [ BaseCentcommContraband, ClothingBeltMilitaryWebbing ] id: ClothingBeltMilitaryWebbingERT name: ERT chest rig description: A set of tactical webbing worn by Emergency Response Team operatives. diff --git a/Resources/Prototypes/Entities/Clothing/Belt/job.yml b/Resources/Prototypes/Entities/Clothing/Belt/job.yml index a40740f46a..a551ce4418 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/job.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/job.yml @@ -135,11 +135,11 @@ - WetFloorSign - HolosignProjector - Plunger + - TrashBagBlue - GoldenPlunger - WireBrush - components: - LightReplacer - - SmokeOnTrigger + - CleanerGrenade - type: ItemMapper sprite: *BeltOverlay mapLayers: @@ -151,10 +151,46 @@ whitelist: tags: - Spray - wrench: + light_replacer: whitelist: tags: - - Wrench + - LightReplacer + trashbag: + whitelist: + tags: + - TrashBag + trashbag_blue: + whitelist: + tags: + - TrashBagBlue + wetfloorsign: + whitelist: + tags: + - WetFloorSign + soap: + whitelist: + tags: + - Soap + wire_brush: + whitelist: + tags: + - WireBrush + holosign: + whitelist: + tags: + - HolosignProjector + plunger: + whitelist: + tags: + - Plunger + golden_plunger: + whitelist: + tags: + - GoldenPlunger + cleaner_grenade: + whitelist: + tags: + - CleanerGrenade - type: Appearance - type: entity diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index d04fa298bc..fd90fe126a 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -1538,3 +1538,14 @@ tags: - PetWearable - CorgiWearable + +- type: entity + parent: ClothingHeadBase + id: ClothingHeadHatMitreClown + name: honkmother mitre + description: It's hard for parishoners to see a banana peel on the floor when they're looking up at your glorious chapeau. + components: + - type: Sprite + sprite: Clothing/Head/Hats/mitre_clown.rsi + - type: Clothing + sprite: Clothing/Head/Hats/mitre_clown.rsi diff --git a/Resources/Prototypes/Entities/Clothing/Misc/changeling.yml b/Resources/Prototypes/Entities/Clothing/Misc/changeling.yml new file mode 100644 index 0000000000..ca9cc5db59 --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Misc/changeling.yml @@ -0,0 +1,269 @@ +# this file contains chameleon clothing for the changelings transformation ability +# one for each slot + +- type: entity + abstract: true + parent: Clothing + id : ChangelingFleshClothingBase + suffix: Changeling + components: + - type: Clothing + quickEquip: false + - type: FleetingClothing + removedSound: + path: /Audio/Voice/Slime/slime_squish.ogg # TODO: replace placeholder + playSoundOnSelfUnequip: false # silent if you remove it yourself + selfUnquipPopupWearer: null + selfUnquipPopupOthers: null + removedPopup: changeling-flesh-clothing-removed-popop + examineWearer: changeling-flesh-clothing-examine-wearer + examineOthers: null # stealthy unless stripped + - type: ChangelingFleshClothing + - type: ChameleonClothing # has the component, but not the BUI, so that it cannot be set manually + affectedByEmp: false + canBeSetByController: false + showVerb: false + - type: FlavorProfile + flavors: + - meaty + - type: Tag + tags: [] # ignore "WhitelistChameleon" tag + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingBack + name: changeling flesh backpack + components: + - type: Sprite + sprite: Clothing/Back/Backpacks/backpack.rsi + state: icon + - type: Item + size: Huge + - type: Clothing + slots: + - back + - type: ChameleonClothing + default: ChangelingFleshClothingBack + slot: + - back + # does not have storage, it's just a visual replacement for stealth purposes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingBelt + name: changeling flesh belt + components: + - type: Sprite + sprite: Clothing/Belt/utility.rsi + state: icon + - type: Item + size: Normal + - type: Clothing + slots: + - belt + - type: ChameleonClothing + default: ChangelingFleshClothingBelt + slot: + - belt + - type: StaticPrice # same as ClothingBeltBase + price: 20 + # does not have storage, it's just a visual replacement for stealth purposes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingEars + name: changeling flesh headset + components: + - type: Sprite + sprite: Clothing/Ears/Headsets/base.rsi + state: icon + - type: Item + size: Small + - type: Clothing + slots: + - ears + - type: ChameleonClothing + default: ChangelingFleshClothingEars + slot: + - ears + # not functional, it's just a visual replacement for stealth purposes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingEyes + name: changeling flesh glasses + components: + - type: Sprite + sprite: Clothing/Eyes/Glasses/glasses.rsi + state: icon + - type: Item + size: Small + storedRotation: -90 + - type: Clothing + slots: + - eyes + - type: ChameleonClothing + default: ChangelingFleshClothingEyes + slot: + - eyes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingGloves + name: changeling flesh gloves + components: + - type: Sprite + sprite: Clothing/Hands/Gloves/Color/color.rsi + layers: + - state: icon + color: "#999999" + - type: Item + size: Small + storedRotation: -90 + - type: Clothing + slots: + - gloves + - type: ChameleonClothing + default: ChangelingFleshClothingGloves + slot: + - gloves + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingHead + name: changeling flesh hat + components: + - type: Sprite + sprite: Clothing/Head/Soft/greysoft.rsi + state: icon + - type: Item + size: Small + storedRotation: -90 + - type: Clothing + slots: + - head + - type: ChameleonClothing + default: ChangelingFleshClothingHead + slot: + - head + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingMask + name: changeling flesh mask + components: + - type: Sprite + sprite: Clothing/Mask/gas.rsi + state: icon + - type: Item + size: Small + - type: Clothing + slots: + - mask + - type: ChameleonClothing + default: ChangelingFleshClothingMask + slot: + - mask + - type: StaticPrice # same as ClothingMaskBase + price: 25 + # identity blockers are dynamically added by ChameleonClothingSystem + # TODO: check if we need to do the same for HideLayerClothingComponent + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingNeck + name: changeling flesh tie + components: + - type: Sprite + sprite: Clothing/Neck/Ties/redtie.rsi + state: icon + - type: Item + size: Small + - type: Clothing + slots: + - neck + - type: ChameleonClothing + default: ChangelingFleshClothingNeck + slot: + - neck + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingInner + name: changeling flesh jumpsuit + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color.rsi + layers: + - state: icon + color: "#b3b3b3" + - state: trinkets-icon + - type: Item + size: Normal + - type: Clothing + slots: + - innerclothing + - type: ChameleonClothing + default: ChangelingFleshClothingInner + slot: + - innerclothing + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingOuter + name: changeling flesh vest + components: + - type: Sprite + sprite: Clothing/OuterClothing/Vests/vest.rsi + state: icon + - type: Item + size: Huge + - type: Clothing + slots: + - outerClothing + - type: ChameleonClothing + default: ChangelingFleshClothingOuter + slot: + - outerClothing + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingFeet + name: changeling flesh shoes + components: + - type: Sprite + sprite: Clothing/Shoes/color.rsi + layers: + - state: icon + color: "#EAE8E8" #Deliberately NOT pure white + - type: Item + size: Normal + - type: Clothing + slots: + - feet + - type: ChameleonClothing + default: ChangelingFleshClothingFeet + slot: + - feet + # not functional, it's just a visual replacement for stealth purposes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingSuitStorage + name: changeling flesh gas tank + components: + - type: Sprite + sprite: Objects/Tanks/generic.rsi + state: icon + - type: Item + size: Normal + - type: Clothing + quickEquip: false + sprite: Objects/Tanks/generic.rsi + slots: + - suitStorage + - type: ChameleonClothing + default: ChangelingFleshClothingSuitStorage + slot: + - suitStorage + # not functional, it's just a visual replacement for stealth purposes diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index e1cdba065e..b7a1e3cd30 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -5,7 +5,7 @@ #Basic armor vest for inheritance - type: entity abstract: true - parent: [ClothingOuterBaseMedium, AllowSuitStorageClothing, BaseSecurityContraband] + parent: [ ClothingOuterBaseMedium, AllowSuitStorageClothing ] id: ClothingOuterArmorBase name: armor vest description: A standard Type I armored vest that provides decent protection against most types of damage. @@ -24,18 +24,18 @@ - type: ExplosionResistance damageCoefficient: 0.90 -#Standard armor vest, allowed for security and bartenders +#Standard armor vest, allowed for security only - type: entity - parent: [ BaseSecurityBartenderContraband, ClothingOuterArmorBase] + parent: [ BaseSecurityContraband, ClothingOuterArmorBase ] id: ClothingOuterArmorBasic components: - type: Tag tags: - WhitelistChameleon -#Alternate / slim basic armor vest +#Alternate / slim basic armor vest, allowed for security and bartenders - type: entity - parent: ClothingOuterArmorBasic + parent: [ BaseSecurityBartenderContraband, ClothingOuterArmorBasic ] id: ClothingOuterArmorBasicSlim name: armor vest suffix: slim @@ -47,7 +47,7 @@ sprite: Clothing/OuterClothing/Armor/security_slim.rsi - type: entity - parent: ClothingOuterArmorBase + parent: ClothingOuterArmorBasic id: ClothingOuterArmorBulletproof name: bulletproof vest description: A Type III heavy bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent. @@ -67,7 +67,7 @@ damageCoefficient: 0.80 - type: entity - parent: ClothingOuterArmorBase + parent: ClothingOuterArmorBasic id: ClothingOuterArmorReflective name: reflective vest description: An armored vest with advanced shielding to protect against energy weapons. @@ -91,7 +91,7 @@ #Detective's vest - type: entity - parent: [ClothingOuterArmorBase, BaseSecurityContraband] + parent: [ClothingOuterArmorBasic, BaseSecurityContraband] id: ClothingOuterVestDetective name: detective's vest description: A hard-boiled private investigator's armored vest. @@ -166,7 +166,7 @@ #Elite web vest - type: entity - parent: [ClothingOuterArmorBase, AllowSuitStorageClothing, BaseSyndicateContraband] + parent: [ClothingOuterArmorBase, ClothingOuterStorageBase, BaseSyndicateContraband] id: ClothingOuterVestWebElite name: elite web vest description: A synthetic armor vest. This one has added webbing and heat resistant fibers. diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index bb2008b017..d5a61f42c7 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -353,7 +353,7 @@ - type: entity parent: ClothingOuterStorageBase id: ClothingOuterClownPriest - name: robes of the honkmother + name: honkmother coat description: Meant for a clown of the cloth. components: - type: Sprite diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index d10c37392e..708cd59ce1 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -102,7 +102,7 @@ walkModifier: 0.9 sprintModifier: 0.9 - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 2 minutes of thrust volume: 0.75 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/donkpocketbox.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/donkpocketbox.yml index 05ad0ba725..b921582ff7 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/donkpocketbox.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/donkpocketbox.yml @@ -1,23 +1,24 @@ - type: entity - name: Donkpocket Box Spawner - id: DonkpocketBoxSpawner parent: MarkerBase + id: DonkpocketBoxSpawner + name: Donkpocket Box Spawner components: - - type: Sprite - layers: - - state: red - - sprite: Objects/Consumable/Food/Baked/donkpocket.rsi - state: box - - type: RandomSpawner - prototypes: - - FoodBoxDonkpocket - - FoodBoxDonkpocketSpicy - - FoodBoxDonkpocketTeriyaki - - FoodBoxDonkpocketPizza - - FoodBoxDonkpocketStonk - - FoodBoxDonkpocketBerry - - FoodBoxDonkpocketHonk - - FoodBoxDonkpocketDink - - FoodBoxDonkpocketMoth - chance: 0.5 - offset: 0.0 + - type: Sprite + layers: + - state: red + - sprite: Objects/Consumable/Food/Baked/donkpocket.rsi + state: box + - type: EntityTableSpawner + table: !type:GroupSelector + prob: 0.5 + children: + - id: FoodBoxDonkpocket + - id: FoodBoxDonkpocketSpicy + - id: FoodBoxDonkpocketTeriyaki + - id: FoodBoxDonkpocketPizza + - id: FoodBoxDonkpocketStonk + - id: FoodBoxDonkpocketBerry + - id: FoodBoxDonkpocketHonk + - id: FoodBoxDonkpocketDink + - id: FoodBoxDonkpocketMoth + offset: 0.0 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml index d049250b55..ac845e55d4 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml @@ -1,140 +1,143 @@ - type: entity + parent: MarkerBase id: RandomDrinkGlass name: random drink spawner suffix: Glass - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Drinks/beerglass.rsi - state: icon - - type: RandomSpawner - #small item - prototypes: - - DrinkAbsintheGlass - - DrinkAleGlass - - DrinkAlienBrainHemorrhage - - DrinkAloe - - DrinkAndalusia - - DrinkAntifreeze - - DrinkArnoldPalmer - - DrinkB52Glass - - DrinkBahamaMama - - DrinkBananaHonkGlass - - DrinkBarefootGlass - - DrinkBeerglass - - DrinkBerryJuice - - DrinkBlackRussianGlass - - DrinkBlueCuracaoGlass - - DrinkBlueHawaiianGlass - - DrinkBloodyMaryGlass - - DrinkBooger - - DrinkBraveBullGlass - - DrinkBronxGlass - - BudgetInsulsDrinkGlass - - DrinkCarrotJuice - - DrinkCoconutRum - - DrinkChocolateGlass - - DrinkCognacGlass - - DrinkCosmopolitan - - DrinkCrushDepthGlass - - DrinkCubaLibreGlass - - DrinkDarkandStormyGlass - - DrinkDeadRumGlass - - DrinkDevilsKiss - - DrinkDriestMartiniGlass - - DrinkDrGibbGlass - - DrinkElectricSharkGlass - - DrinkErikaSurprise - - DrinkFourteenLokoGlass - - DrinkGargleBlasterGlass - - DrinkGinFizzGlass - - DrinkGinGlass - - DrinkGinTonicglass - - DrinkGildlagerGlass - - DrinkGrapeJuice - - DrinkGreenTeaGlass - - DrinkGrogGlass - - DrinkHippiesDelightGlass - - DrinkIcedCoffeeGlass - - DrinkIcedGreenTeaGlass - - DrinkIcedBeerGlass - - DrinkIceCreamGlass - - IrishBoolGlass - - DrinkIrishSlammer - - DrinkIrishCoffeeGlass - - DrinkLemonadeGlass - - DrinkJackRoseGlass - - DrinkJungleBirdGlass - - DrinkKalimotxoGlass - - DrinkOrangeLimeSodaGlass - - DrinkLongIslandIcedTeaGlass - - DrinkManhattanGlass - - DrinkManlyDorfGlass - - DrinkMargaritaGlass - - DrinkMartiniGlass - - DrinkMeadGlass - - DrinkMilkshake - - DrinkMojito - - DrinkMonkeyBusinessGlass - - DrinkNTCahors - - DrinkPainkillerGlass - - DrinkPatronGlass - - DrinkPinaColadaGlass - - DrinkPoscaGlass - - DrinkRadlerGlass - - DrinkRedMeadGlass - - DrinkRewriter - - DrinkRoyRogersGlass - - DrinkRootBeerFloatGlass - - RubberneckGlass - - DrinkRumGlass - - DrinkSakeGlass - - DrinkSbitenGlass - - DrinkScrewdriverCocktailGlass - - DrinkShirleyTempleGlass - - DrinkSuiDreamGlass - - DrinkSingulo - - DrinkSoyLatte - - DrinkSyndicatebomb - - DrinkTequilaSunriseGlass - - DrinkThreeMileIslandGlass - - DrinkTortugaGlass - - DrinkToxinsSpecialGlass - - DrinkVampiroGlass - - DrinkVodkaMartiniGlass - - DrinkVodkaRedBool - - DrinkVodkaTonicGlass - - DrinkWatermelonJuice - - DrinkWatermelonWakeup - - DrinkWhiskeyColaGlass - - DrinkWhiskeySodaGlass - - DrinkWhiteRussianGlass - - DrinkWineGlass - - XenoBasherGlass - - DrinkShakeBlue - - DrinkShakeWhite - - DrinkTheMartinez - - DrinkMoonshineGlass - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Drinks/beerglass.rsi + state: icon + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector #small item + weight: 0.95 + prob: 0.8 + children: + - id: DrinkAbsintheGlass + - id: DrinkAleGlass + - id: DrinkAlienBrainHemorrhage + - id: DrinkAloe + - id: DrinkAndalusia + - id: DrinkAntifreeze + - id: DrinkArnoldPalmer + - id: DrinkB52Glass + - id: DrinkBahamaMama + - id: DrinkBananaHonkGlass + - id: DrinkBarefootGlass + - id: DrinkBeerglass + - id: DrinkBerryJuice + - id: DrinkBlackRussianGlass + - id: DrinkBlueCuracaoGlass + - id: DrinkBlueHawaiianGlass + - id: DrinkBloodyMaryGlass + - id: DrinkBooger + - id: DrinkBraveBullGlass + - id: DrinkBronxGlass + - id: BudgetInsulsDrinkGlass + - id: DrinkCarrotJuice + - id: DrinkCoconutRum + - id: DrinkChocolateGlass + - id: DrinkCognacGlass + - id: DrinkCosmopolitan + - id: DrinkCrushDepthGlass + - id: DrinkCubaLibreGlass + - id: DrinkDarkandStormyGlass + - id: DrinkDeadRumGlass + - id: DrinkDevilsKiss + - id: DrinkDriestMartiniGlass + - id: DrinkDrGibbGlass + - id: DrinkElectricSharkGlass + - id: DrinkErikaSurprise + - id: DrinkFourteenLokoGlass + - id: DrinkGargleBlasterGlass + - id: DrinkGinFizzGlass + - id: DrinkGinGlass + - id: DrinkGinTonicglass + - id: DrinkGildlagerGlass + - id: DrinkGrapeJuice + - id: DrinkGreenTeaGlass + - id: DrinkGrogGlass + - id: DrinkHippiesDelightGlass + - id: DrinkIcedCoffeeGlass + - id: DrinkIcedGreenTeaGlass + - id: DrinkIcedBeerGlass + - id: DrinkIceCreamGlass + - id: IrishBoolGlass + - id: DrinkIrishSlammer + - id: DrinkIrishCoffeeGlass + - id: DrinkLemonadeGlass + - id: DrinkJackRoseGlass + - id: DrinkJungleBirdGlass + - id: DrinkKalimotxoGlass + - id: DrinkOrangeLimeSodaGlass + - id: DrinkLongIslandIcedTeaGlass + - id: DrinkManhattanGlass + - id: DrinkManlyDorfGlass + - id: DrinkMargaritaGlass + - id: DrinkMartiniGlass + - id: DrinkMeadGlass + - id: DrinkMilkshake + - id: DrinkMojito + - id: DrinkMonkeyBusinessGlass + - id: DrinkNTCahors + - id: DrinkPainkillerGlass + - id: DrinkPatronGlass + - id: DrinkPinaColadaGlass + - id: DrinkPoscaGlass + - id: DrinkRadlerGlass + - id: DrinkRedMeadGlass + - id: DrinkRewriter + - id: DrinkRoyRogersGlass + - id: DrinkRootBeerFloatGlass + - id: RubberneckGlass + - id: DrinkRumGlass + - id: DrinkSakeGlass + - id: DrinkSbitenGlass + - id: DrinkScrewdriverCocktailGlass + - id: DrinkShirleyTempleGlass + - id: DrinkSuiDreamGlass + - id: DrinkSingulo + - id: DrinkSoyLatte + - id: DrinkSyndicatebomb + - id: DrinkTequilaSunriseGlass + - id: DrinkThreeMileIslandGlass + - id: DrinkTortugaGlass + - id: DrinkToxinsSpecialGlass + - id: DrinkVampiroGlass + - id: DrinkVodkaMartiniGlass + - id: DrinkVodkaRedBool + - id: DrinkVodkaTonicGlass + - id: DrinkWatermelonJuice + - id: DrinkWatermelonWakeup + - id: DrinkWhiskeyColaGlass + - id: DrinkWhiskeySodaGlass + - id: DrinkWhiteRussianGlass + - id: DrinkWineGlass + - id: XenoBasherGlass + - id: DrinkShakeBlue + - id: DrinkShakeWhite + - id: DrinkTheMartinez + - id: DrinkMoonshineGlass + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: DrinkAcidSpitGlass + - id: DrinkAlliesCocktail + - id: DrinkAmasecGlass + - id: DrinkAtomicBombGlass + - id: DrinkDemonsBlood + - id: DrinkDoctorsDelightGlass + - id: DrinkNeurotoxinGlass + - id: DrinkNuclearColaGlass + - id: DrinkSilencerGlass + - id: DrinkShakeMeat + - id: DrinkShakeRobo + - id: DrinkHoochGlass + - id: DrinkBeepskySmashGlass + - id: DrinkBacchusBlessing offset: 0.0 - #rare - rarePrototypes: - - DrinkAcidSpitGlass - - DrinkAlliesCocktail - - DrinkAmasecGlass - - DrinkAtomicBombGlass - - DrinkDemonsBlood - - DrinkDoctorsDelightGlass - - DrinkNeurotoxinGlass - - DrinkNuclearColaGlass - - DrinkSilencerGlass - - DrinkShakeMeat - - DrinkShakeRobo - - DrinkHoochGlass - - DrinkBeepskySmashGlass - - DrinkBacchusBlessing - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_single.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_single.yml index 7d0f734acc..1a4d8defb8 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_single.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_single.yml @@ -1,96 +1,100 @@ - type: entity + parent: MarkerBase id: RandomFoodBakedSingle name: random baked food spawner suffix: Single Serving - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Food/Baked/pie.rsi - state: plain-slice - - type: RandomSpawner - prototypes: - - FoodBreadPlainSlice - - FoodBreadMeatSlice - - FoodBreadBananaSlice - - FoodBreadCreamcheeseSlice - - FoodBreadMoldySlice - - FoodBreadGarlicSlice - - FoodBreadCornSlice - - FoodBreadSausageSlice - - FoodBreadButteredToast - - FoodBreadJellySlice - - FoodBreadFrenchToast - - FoodBreadTwoSlice - - FoodBreadVolcanicSlice - - FoodBreadTofuSlice - - FoodBreadBaguetteSlice - - FoodCakeBlueberrySlice - - FoodCakePlainSlice - - FoodCakeCarrotSlice - - FoodCakeCheeseSlice - - FoodCakeOrangeSlice - - FoodCakeLimeSlice - - FoodCakeLemonSlice - - FoodCakeChocolateSlice - - FoodCakeAppleSlice - - FoodCakeSlimeSlice - - FoodCakePumpkinSlice - - FoodCakeChristmasSlice - - FoodCakeVanillaSlice - - FoodCakeBirthdaySlice - - FoodCakeBerryDelightSlice - - FoodCakeCottonSlice - - FoodBakedMuffin - - FoodBakedMuffinBerry - - FoodBakedMuffinCherry - - FoodBakedMuffinBluecherry - - FoodBakedMuffinChocolate - - FoodBakedMuffinBanana - - FoodBakedBunHoney - - FoodBakedBunHotX - - FoodBakedBunMeat - - FoodBakedCookie - - FoodBakedCookieOatmeal - - FoodBakedCookieRaisin - - FoodBakedCookieSugar - - FoodBakedGrilledCheeseSandwich - - FoodBakedGrilledCheeseSandwichCotton - - FoodBakedNugget - - FoodBakedPancake - - FoodBakedPancakeBb - - FoodBakedPancakeCc - - FoodBakedWaffle - - FoodBakedWaffleSoy - - FoodBakedWaffleSoylent - - FoodBakedWaffleRoffle - - FoodBakedPretzel - - FoodBakedCannoli - - FoodPieAppleSlice - - FoodPieBaklavaSlice - - FoodPieClafoutisSlice - - FoodPieMeatSlice - - FoodPieCherrySlice - - FoodPieFrostySlice - - FoodTartGrape - - FoodTartCoco - - FoodBakedBrownie - - FoodPieBananaCreamSlice - - FoodTartMimeSlice - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Food/Baked/pie.rsi + state: plain-slice + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector + weight: 0.95 + prob: 0.8 + children: + - id: FoodBreadPlainSlice + - id: FoodBreadMeatSlice + - id: FoodBreadBananaSlice + - id: FoodBreadCreamcheeseSlice + - id: FoodBreadMoldySlice + - id: FoodBreadGarlicSlice + - id: FoodBreadCornSlice + - id: FoodBreadSausageSlice + - id: FoodBreadButteredToast + - id: FoodBreadJellySlice + - id: FoodBreadFrenchToast + - id: FoodBreadTwoSlice + - id: FoodBreadVolcanicSlice + - id: FoodBreadTofuSlice + - id: FoodBreadBaguetteSlice + - id: FoodCakeBlueberrySlice + - id: FoodCakePlainSlice + - id: FoodCakeCarrotSlice + - id: FoodCakeCheeseSlice + - id: FoodCakeOrangeSlice + - id: FoodCakeLimeSlice + - id: FoodCakeLemonSlice + - id: FoodCakeChocolateSlice + - id: FoodCakeAppleSlice + - id: FoodCakeSlimeSlice + - id: FoodCakePumpkinSlice + - id: FoodCakeChristmasSlice + - id: FoodCakeVanillaSlice + - id: FoodCakeBirthdaySlice + - id: FoodCakeBerryDelightSlice + - id: FoodCakeCottonSlice + - id: FoodBakedMuffin + - id: FoodBakedMuffinBerry + - id: FoodBakedMuffinCherry + - id: FoodBakedMuffinBluecherry + - id: FoodBakedMuffinChocolate + - id: FoodBakedMuffinBanana + - id: FoodBakedBunHoney + - id: FoodBakedBunHotX + - id: FoodBakedBunMeat + - id: FoodBakedCookie + - id: FoodBakedCookieOatmeal + - id: FoodBakedCookieRaisin + - id: FoodBakedCookieSugar + - id: FoodBakedGrilledCheeseSandwich + - id: FoodBakedGrilledCheeseSandwichCotton + - id: FoodBakedNugget + - id: FoodBakedPancake + - id: FoodBakedPancakeBb + - id: FoodBakedPancakeCc + - id: FoodBakedWaffle + - id: FoodBakedWaffleSoy + - id: FoodBakedWaffleSoylent + - id: FoodBakedWaffleRoffle + - id: FoodBakedPretzel + - id: FoodBakedCannoli + - id: FoodPieAppleSlice + - id: FoodPieBaklavaSlice + - id: FoodPieClafoutisSlice + - id: FoodPieMeatSlice + - id: FoodPieCherrySlice + - id: FoodPieFrostySlice + - id: FoodTartGrape + - id: FoodTartCoco + - id: FoodBakedBrownie + - id: FoodPieBananaCreamSlice + - id: FoodTartMimeSlice + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: FoodBreadMeatSpiderSlice + - id: FoodBreadMimanaSlice + - id: FoodCakeBrainSlice + - id: FoodCakeClownSlice + - id: FoodCakeSpacemanSlice + - id: FoodTartGapple + - id: FoodBreadMeatXenoSlice + - id: FoodPieXenoSlice + - id: FoodBakedCannabisBrownie offset: 0.0 - #rare - rarePrototypes: - - FoodBreadMeatSpiderSlice - - FoodBreadMimanaSlice - - FoodCakeBrainSlice - - FoodCakeClownSlice - - FoodCakeSpacemanSlice - - FoodTartGapple - - FoodBreadMeatXenoSlice - - FoodPieXenoSlice - - FoodBakedCannabisBrownie - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_whole.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_whole.yml index 7683f19884..f7c553b022 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_whole.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_whole.yml @@ -1,65 +1,68 @@ - type: entity + parent: MarkerBase id: RandomFoodBakedWhole name: random baked food spawner suffix: Whole - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Food/Baked/pie.rsi - state: plain - - type: RandomSpawner - #small item - prototypes: - - FoodBreadVolcanic - - FoodBreadPlain - - FoodBreadMeat - - FoodBreadCorn - - FoodBreadSausage - - FoodBreadBanana - - FoodBreadTofu - - FoodBreadCreamcheese - - FoodBreadBaguette - - FoodCakeBlueberry - - FoodCakePlain - - FoodCakeCheese - - FoodCakeCarrot - - FoodCakeOrange - - FoodCakeLime - - FoodCakeLemon - - FoodCakeChocolate - - FoodCakeApple - - FoodCakeSlime - - FoodCakePumpkin - - FoodCakeChristmas - - FoodCakeBirthday - - FoodCakeVanilla - - FoodCakeBerryDelight - - FoodCakeCotton - - FoodPieApple - - FoodPieBaklava - - FoodPieBananaCream - - FoodPieClafoutis - - FoodPieCherry - - FoodPieMeat - - FoodPieFrosty - - FoodBakedBrownieBatch - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Food/Baked/pie.rsi + state: plain + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector #small item + weight: 0.95 + prob: 0.8 + children: + - id: FoodBreadVolcanic + - id: FoodBreadPlain + - id: FoodBreadMeat + - id: FoodBreadCorn + - id: FoodBreadSausage + - id: FoodBreadBanana + - id: FoodBreadTofu + - id: FoodBreadCreamcheese + - id: FoodBreadBaguette + - id: FoodCakeBlueberry + - id: FoodCakePlain + - id: FoodCakeCheese + - id: FoodCakeCarrot + - id: FoodCakeOrange + - id: FoodCakeLime + - id: FoodCakeLemon + - id: FoodCakeChocolate + - id: FoodCakeApple + - id: FoodCakeSlime + - id: FoodCakePumpkin + - id: FoodCakeChristmas + - id: FoodCakeBirthday + - id: FoodCakeVanilla + - id: FoodCakeBerryDelight + - id: FoodCakeCotton + - id: FoodPieApple + - id: FoodPieBaklava + - id: FoodPieBananaCream + - id: FoodPieClafoutis + - id: FoodPieCherry + - id: FoodPieMeat + - id: FoodPieFrosty + - id: FoodBakedBrownieBatch + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: FoodBreadMeatSpider + - id: FoodBreadMimana + - id: FoodCakeBrain + - id: FoodCakeClown + - id: FoodCakeSpaceman + - id: FoodPieXeno + - id: FoodPieAmanita + - id: FoodPiePlump + - id: FoodBreadMeatXeno + - id: FoodPieXeno + - id: FoodBakedCannabisBrownieBatch offset: 0.0 - #rare - rarePrototypes: - - FoodBreadMeatSpider - - FoodBreadMimana - - FoodCakeBrain - - FoodCakeClown - - FoodCakeSpaceman - - FoodPieXeno - - FoodPieAmanita - - FoodPiePlump - - FoodBreadMeatXeno - - FoodPieXeno - - FoodBakedCannabisBrownieBatch - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_breakfast.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_breakfast.yml index e4213ad31f..27256c673e 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_breakfast.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_breakfast.yml @@ -1,18 +1,19 @@ - type: entity + parent: MarkerBase id: RandomFoodBreakfast name: random food spawner - suffix: Meal - parent: MarkerBase + suffix: Breakfast placement: mode: AlignTileAny components: - type: Sprite layers: - - sprite: Objects/Consumable/Food/breakfast.rsi - state: fullamerican - - type: RandomSpawner - prototypes: - - FoodBreakfastAmerican - - FoodBreakfastEnglish - chance: 0.8 + - sprite: Objects/Consumable/Food/breakfast.rsi + state: fullamerican + - type: EntityTableSpawner + table: !type:GroupSelector + prob: 0.8 + children: + - id: FoodBreakfastAmerican + - id: FoodBreakfastEnglish offset: 0.0 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_meal.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_meal.yml index 4c64bdd786..14aea5b79c 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_meal.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_meal.yml @@ -1,97 +1,101 @@ - type: entity + parent: MarkerBase id: RandomFoodMeal name: random food spawner suffix: Meal - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Food/meals.rsi - state: cornedbeef - - type: RandomSpawner - prototypes: - - FoodMealPotatoLoaded - - FoodMealFries - - FoodMealFriesCheesy - - FoodMealFriesCarrot - - FoodMealNachos - - FoodMealNachosCheesy - - FoodMealNachosCuban - - FoodMealEggplantParm - - FoodMealPotatoYaki - - FoodMealCubancarp - - FoodMealCornedbeef - - FoodMealPigblanket - - FoodMealRibs - - FoodMealEggsbenedict - - FoodMealOmelette - - FoodMealFriedegg - - FoodMealQueso - - FoodMealSashimi - - FoodMealEnchiladas - - FoodNoodlesChowmein - - FoodNoodlesSpesslaw - - FoodNoodlesMeatball - - FoodNoodlesBoiled - - FoodNoodles - - FoodNoodlesButter - - FoodMealHappyHonkClown - - FoodSoupPea - - FoodSaladHerb - - FoodSaladValid - - FoodSaladFruit - - FoodSaladJungle - - FoodSaladCitrus - - FoodSaladCaesar - - FoodSaladColeslaw - - FoodSaladKimchi - - FoodSaladWatermelonFruitBowl - - FoodRiceBoiled - - FoodRiceEgg - - FoodRicePork - - FoodRicePudding - - FoodRiceGumbo - - FoodOatmeal - - FoodSoupMeatball - - FoodSoupVegetable - - FoodSoupNettle - - FoodSoupChiliHot - - FoodSoupChiliCold - - FoodSoupTomato - - FoodSoupMiso - - FoodSoupMushroom - - FoodSoupBeet - - FoodSoupBeetRed - - FoodSoupPotato - - FoodSoupOnion - - FoodSoupBisque - - FoodSoupBungo - - FoodMealCornInButter - - FoodSoupStew - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Food/meals.rsi + state: cornedbeef + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector + weight: 0.95 + prob: 0.8 + children: + - id: FoodMealPotatoLoaded + - id: FoodMealFries + - id: FoodMealFriesCheesy + - id: FoodMealFriesCarrot + - id: FoodMealNachos + - id: FoodMealNachosCheesy + - id: FoodMealNachosCuban + - id: FoodMealEggplantParm + - id: FoodMealPotatoYaki + - id: FoodMealCubancarp + - id: FoodMealCornedbeef + - id: FoodMealPigblanket + - id: FoodMealRibs + - id: FoodMealEggsbenedict + - id: FoodMealOmelette + - id: FoodMealFriedegg + - id: FoodMealQueso + - id: FoodMealSashimi + - id: FoodMealEnchiladas + - id: FoodNoodlesChowmein + - id: FoodNoodlesSpesslaw + - id: FoodNoodlesMeatball + - id: FoodNoodlesBoiled + - id: FoodNoodles + - id: FoodNoodlesButter + - id: FoodMealHappyHonkClown + - id: FoodSoupPea + - id: FoodSaladHerb + - id: FoodSaladValid + - id: FoodSaladFruit + - id: FoodSaladJungle + - id: FoodSaladCitrus + - id: FoodSaladCaesar + - id: FoodSaladColeslaw + - id: FoodSaladKimchi + - id: FoodSaladWatermelonFruitBowl + - id: FoodRiceBoiled + - id: FoodRiceEgg + - id: FoodRicePork + - id: FoodRicePudding + - id: FoodRiceGumbo + - id: FoodOatmeal + - id: FoodSoupMeatball + - id: FoodSoupVegetable + - id: FoodSoupNettle + - id: FoodSoupChiliHot + - id: FoodSoupChiliCold + - id: FoodSoupTomato + - id: FoodSoupMiso + - id: FoodSoupMushroom + - id: FoodSoupBeet + - id: FoodSoupBeetRed + - id: FoodSoupPotato + - id: FoodSoupOnion + - id: FoodSoupBisque + - id: FoodSoupBungo + - id: FoodMealCornInButter + - id: FoodSoupStew + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: FoodMealMint + - id: FoodMealBearsteak + - id: FoodMealMilkape + - id: DisgustingSweptSoup + - id: FoodMealMemoryleek + - id: FoodSaladAesir + - id: FoodSaladEden + - id: FoodJellyDuff + - id: FoodJellyAmanita + - id: FoodSoupSlime + - id: FoodSoupTomatoBlood + - id: FoodSoupWingFangChu + - id: FoodSoupClown + - id: FoodSoupMystery + - id: FoodSoupChiliClown + - id: FoodSoupMonkey + - id: FoodSoupEyeball + - id: FoodSoupElectron + - id: FoodNoodlesCopy offset: 0.0 - #rare - rarePrototypes: - - FoodMealMint - - FoodMealBearsteak - - FoodMealMilkape - - DisgustingSweptSoup - - FoodMealMemoryleek - - FoodSaladAesir - - FoodSaladEden - - FoodJellyDuff - - FoodJellyAmanita - - FoodSoupSlime - - FoodSoupTomatoBlood - - FoodSoupWingFangChu - - FoodSoupClown - - FoodSoupMystery - - FoodSoupChiliClown - - FoodSoupMonkey - - FoodSoupEyeball - - FoodSoupElectron - - FoodNoodlesCopy - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml index fda7b85b75..ea44f18599 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml @@ -1,71 +1,75 @@ - type: entity + parent: MarkerBase id: RandomFoodSingle name: random food spawner suffix: Single Serving - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Food/burger.rsi - state: plain - - type: RandomSpawner - prototypes: - - FoodBagel - - FoodBagelPoppy - - FoodBurgerJelly - - FoodBurgerCarp - - FoodBurgerTofu - - FoodBurgerXeno - - FoodBurgerBig - - FoodBurgerFive - - FoodBurgerRat - - FoodBurgerBacon - - FoodBurgerEmpowered - - FoodBurgerCrab - - FoodBurgerHuman - - FoodBurgerSoy - - FoodBurgerMcrib - - FoodBurgerMcguffin - - FoodBurgerChicken - - FoodBurgerDuck - - FoodBurgerCheese - - FoodNoodlesBoiled - - FoodNoodles - - FoodNoodlesCopy - - FoodNoodlesButter - - FoodPizzaMargheritaSlice - - FoodPizzaMeatSlice - - FoodPizzaMushroomSlice - - FoodPizzaVegetableSlice - - FoodPizzaDonkpocketSlice - - FoodPizzaDankSlice - - FoodPizzaSassysageSlice - - FoodPizzaPineappleSlice - - FoodPizzaMoldySlice - - FoodBakedDumplings - - FoodBakedChevreChaud - - FoodBakedNugget - - FoodTacoShell - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Food/burger.rsi + state: plain + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector + weight: 0.95 + prob: 0.8 + children: + - id: FoodBagel + - id: FoodBagelPoppy + - id: FoodBurgerJelly + - id: FoodBurgerCarp + - id: FoodBurgerTofu + - id: FoodBurgerXeno + - id: FoodBurgerBig + - id: FoodBurgerFive + - id: FoodBurgerRat + - id: FoodBurgerBacon + - id: FoodBurgerEmpowered + - id: FoodBurgerCrab + - id: FoodBurgerHuman + - id: FoodBurgerSoy + - id: FoodBurgerMcrib + - id: FoodBurgerMcguffin + - id: FoodBurgerChicken + - id: FoodBurgerDuck + - id: FoodBurgerCheese + - id: FoodNoodlesBoiled + - id: FoodNoodles + - id: FoodNoodlesCopy + - id: FoodNoodlesButter + - id: FoodPizzaMargheritaSlice + - id: FoodPizzaMeatSlice + - id: FoodPizzaMushroomSlice + - id: FoodPizzaVegetableSlice + - id: FoodPizzaDonkpocketSlice + - id: FoodPizzaDankSlice + - id: FoodPizzaSassysageSlice + - id: FoodPizzaPineappleSlice + - id: FoodPizzaMoldySlice + - id: FoodBakedDumplings + - id: FoodBakedChevreChaud + - id: FoodBakedNugget + - id: FoodTacoShell + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: FoodBurgerAppendix + - id: FoodBurgerRobot + - id: FoodBurgerBaseball + - id: FoodBurgerBear + - id: FoodBurgerCat + - id: FoodBurgerClown + - id: FoodBurgerMime + - id: FoodBurgerBrain + - id: FoodBurgerGhost + - id: FoodBurgerSpell + - id: FoodBurgerSuper + - id: FoodBurgerCrazy + - id: FoodPizzaArnoldSlice + - id: FoodPizzaUraniumSlice + - id: FoodPizzaWorldpeasSlice offset: 0.0 - #rare - rarePrototypes: - - FoodBurgerAppendix - - FoodBurgerRobot - - FoodBurgerBaseball - - FoodBurgerBear - - FoodBurgerCat - - FoodBurgerClown - - FoodBurgerMime - - FoodBurgerBrain - - FoodBurgerGhost - - FoodBurgerSpell - - FoodBurgerSuper - - FoodBurgerCrazy - - FoodPizzaArnoldSlice - - FoodPizzaUraniumSlice - - FoodPizzaWorldpeasSlice - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index e62fed9bb9..f58028baf7 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -95,6 +95,7 @@ - !type:GroupSelector weight: 23 children: + - id: TravelCamera - id: ClothingNeckCloakHerald - id: ClothingHeadHelmetTemplar # Cloaks diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/vending.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/vending.yml index 0b530c68b1..98dfc20c13 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/vending.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/vending.yml @@ -1,38 +1,38 @@ - type: entity + parent: MarkerBase id: RandomVending name: random vending machine spawner suffix: Any - parent: MarkerBase components: - type: Sprite layers: - state: red - sprite: Structures/Machines/VendingMachines/random.rsi state: any - - type: RandomSpawner - prototypes: - - VendingMachineChang - - VendingMachineCigs - - VendingMachineCoffee - - VendingMachineCola #Robust Sofdrinks - - VendingMachineColaBlack #Robust Sofdrinks [Black] - - VendingMachineColaRed #Space Cola - - VendingMachineDiscount - - VendingMachineDonut - - VendingMachineDrGibb - - VendingMachinePwrGame - - VendingMachineShamblersJuice - - VendingMachineSmite - - VendingMachineSnack - - VendingMachineSnackBlue - - VendingMachineSnackGreen - - VendingMachineSnackOrange - - VendingMachineSnackTeal - - VendingMachineSoda #Robust Sofdrinks [Soda] - - VendingMachineSovietSoda #Boda - - VendingMachineSpaceUp - - VendingMachineStarkist - chance: 1 + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - id: VendingMachineChang + - id: VendingMachineCigs + - id: VendingMachineCoffee + - id: VendingMachineCola #Robust Sofdrinks + - id: VendingMachineColaBlack #Robust Sofdrinks [Black] + - id: VendingMachineColaRed #Space Cola + - id: VendingMachineDiscount + - id: VendingMachineDonut + - id: VendingMachineDrGibb + - id: VendingMachinePwrGame + - id: VendingMachineShamblersJuice + - id: VendingMachineSmite + - id: VendingMachineSnack + - id: VendingMachineSnackBlue + - id: VendingMachineSnackGreen + - id: VendingMachineSnackOrange + - id: VendingMachineSnackTeal + - id: VendingMachineSoda #Robust Sofdrinks [Soda] + - id: VendingMachineSovietSoda #Boda + - id: VendingMachineSpaceUp + - id: VendingMachineStarkist - type: entityTable @@ -40,19 +40,17 @@ table: !type:GroupSelector children: - id: VendingMachineClothing - weight: 40 + weight: 4 - id: VendingMachineWinter - weight: 40 + weight: 4 - id: VendingMachinePride - weight: 10 - id: VendingMachineTheater - weight: 10 - type: entity + parent: MarkerBase id: RandomVendingClothing name: random vending machine spawner suffix: Clothing - parent: MarkerBase components: - type: Sprite layers: diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingdrinks.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingdrinks.yml index 1c90664b3d..09c4ce8761 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingdrinks.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingdrinks.yml @@ -1,26 +1,26 @@ - type: entity + parent: MarkerBase id: RandomVendingDrinks name: random vending machine spawner suffix: Drinks - parent: MarkerBase components: - type: Sprite layers: - - state: red - - sprite: Structures/Machines/VendingMachines/random.rsi - state: drink - - type: RandomSpawner - prototypes: - - VendingMachineCoffee - - VendingMachineCola #Robust Sofdrinks - - VendingMachineColaBlack #Robust Sofdrinks [Black] - - VendingMachineColaRed #Space Cola - - VendingMachineDrGibb - - VendingMachinePwrGame - - VendingMachineShamblersJuice - - VendingMachineSmite - - VendingMachineSoda #Robust Sofdrinks [Soda] - - VendingMachineSovietSoda #Boda - - VendingMachineSpaceUp - - VendingMachineStarkist - chance: 1 + - state: red + - sprite: Structures/Machines/VendingMachines/random.rsi + state: drink + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - id: VendingMachineCoffee + - id: VendingMachineCola #Robust Sofdrinks + - id: VendingMachineColaBlack #Robust Sofdrinks [Black] + - id: VendingMachineColaRed #Space Cola + - id: VendingMachineDrGibb + - id: VendingMachinePwrGame + - id: VendingMachineShamblersJuice + - id: VendingMachineSmite + - id: VendingMachineSoda #Robust Sofdrinks [Soda] + - id: VendingMachineSovietSoda #Boda + - id: VendingMachineSpaceUp + - id: VendingMachineStarkist diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingsnacks.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingsnacks.yml index b634d50cc6..e919cf192d 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingsnacks.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingsnacks.yml @@ -1,22 +1,22 @@ - type: entity + parent: MarkerBase id: RandomVendingSnacks name: random vending machine spawner suffix: Snacks - parent: MarkerBase components: - type: Sprite layers: - - state: red - - sprite: Structures/Machines/VendingMachines/random.rsi - state: snack - - type: RandomSpawner - prototypes: - - VendingMachineDiscount - - VendingMachineSnack - - VendingMachineSnackBlue - - VendingMachineSnackGreen - - VendingMachineSnackOrange - - VendingMachineSnackTeal - - VendingMachineChang - - VendingMachineDonut - chance: 1 + - state: red + - sprite: Structures/Machines/VendingMachines/random.rsi + state: snack + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - id: VendingMachineDiscount + - id: VendingMachineSnack + - id: VendingMachineSnackBlue + - id: VendingMachineSnackGreen + - id: VendingMachineSnackOrange + - id: VendingMachineSnackTeal + - id: VendingMachineChang + - id: VendingMachineDonut diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index f2e4bb002c..890d21b597 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml @@ -349,6 +349,7 @@ - type: GhostRole name: ghost-role-information-wizard-name description: ghost-role-information-wizard-desc + rules: ghost-role-information-antagonist-rules mindRoles: - MindRoleWizard raffle: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index ea38ad4f40..d26d68d28e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1392,23 +1392,16 @@ - map: [ "outerClothing" ] - map: [ "mask" ] - map: [ "head" ] - - map: [ "clownedon" ] - sprite: "Effects/creampie.rsi" - state: "creampie_human" - visible: false - type: Hands activeHandId: Hand hands: Hand: location: Left - type: ComplexInteraction - - type: GenericVisualizer - visuals: - enum.CreamPiedVisuals.Creamed: - clownedon: - True: {visible: true} - False: {visible: false} - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_monkey - type: FireVisuals sprite: Mobs/Effects/onfire.rsi normalState: monkey_small_fire # Corvax WL /tg/ resprite @@ -1447,7 +1440,10 @@ - Passive - type: HTN rootTask: - task: SimpleHostileCompound + task: MonkeyCompound + blackboard: + NavInteract: !type:Bool + true - type: IdExaminable - type: Tag tags: @@ -1506,7 +1502,7 @@ - type: entity name: monkey id: MobBaseSyndicateMonkey - parent: MobBaseAncestor + parent: MobBaseAncestor # TODO: fix copy paste from MobMonkey description: New church of neo-darwinists actually believe that EVERY animal evolved from a monkey. Tastes like pork, and killing them is both fun and relaxing. suffix: syndicate base components: @@ -1630,10 +1626,6 @@ - map: [ "id" ] - map: [ "mask" ] - map: [ "head" ] - - map: [ "clownedon" ] - sprite: "Effects/creampie.rsi" - state: "creampie_human" - visible: false - type: RandomSprite getAllGroups: true available: @@ -2988,6 +2980,11 @@ - type: MobPrice price: 200 - type: MessyDrinker + # TODO: Fix the sprite offset + #- type: CreamPied + # sprite: + # sprite: Effects/creampie.rsi + # state: creampie_corgi - type: entity parent: MobCorgiBase diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml b/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml index 67c34e4680..d1da439ed8 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml @@ -20,7 +20,6 @@ allowed: - Electrocution - TemporaryBlindness - - RadiationProtection - Adrenaline - type: StandingState - type: Tag diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index 93f1a84e48..56a7f6e8e9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -33,6 +33,8 @@ proto: regal - type: Physics bodyType: KinematicController + - type: Puller + needsHands: false - type: Fixtures fixtures: fix1: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index cfed1c71ec..9fdfe59103 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -22,7 +22,6 @@ - TemporaryBlindness - Pacified - Flashed - - RadiationProtection - Adrenaline - type: Buckle - type: StandingState @@ -96,7 +95,6 @@ - TemporaryBlindness - Pacified - Flashed - - RadiationProtection - Adrenaline - type: Bloodstream bloodReferenceSolution: diff --git a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml index 6ee9de8d01..dcddf77e1f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml @@ -13,8 +13,24 @@ - type: Store balance: ChangelingDNA: 50 - - type: UserInterface - interfaces: - enum.StoreUiKey.Key: - type: StoreBoundUserInterface - requireInputValidation: false + +- type: entity # dummy prototype for the store listing + id: ChangelingFleshClothingAbilityStoreDummy + categories: [ HideSpawnMenu ] + components: + - type: ChangelingFleshClothingAbility + clothingPrototypes: + shoes: ChangelingFleshClothingFeet + jumpsuit: ChangelingFleshClothingInner + outerClothing: ChangelingFleshClothingOuter + gloves: ChangelingFleshClothingGloves + neck: ChangelingFleshClothingNeck + mask: ChangelingFleshClothingMask + eyes: ChangelingFleshClothingEyes + ears: ChangelingFleshClothingEars + head: ChangelingFleshClothingHead + suitstorage: ChangelingFleshClothingSuitStorage + #id: TODO: chameleon PDA that is not a functional PDA + belt: ChangelingFleshClothingBelt + back: ChangelingFleshClothingBack + diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 94c2ae8064..436ec99c42 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -10,11 +10,16 @@ components: # general # - DetailExaminable //WL-changes + - CreamPied - Dna - Fingerprint - Grammar # Pronouns - HumanoidProfile # Age, Sex, Gender + - MeleeWeapon # Attack animation, damage and sound - NpcFactionMember + - RandomPrice + - TemperatureDamage + - TemperatureSpeed # traits - BlackAndWhiteOverlay - Clumsy @@ -64,6 +69,7 @@ - StutteringAccent - TTS # Corvax-TTS eventComponents: + - Puller - Vocal # voice sounds # for job-specific traits etc. @@ -85,16 +91,20 @@ - Revolutionary - NukeOperative -# a full clone with all traits and items, but no antag roles - type: cloningSettings - id: BaseClone - parent: [Body, Special] + abstract: true + id: BaseEquipmentBlacklist blacklist: components: - AttachedClothing # helmets, which are part of the suit - Implanter # they will spawn full again, but you already get the implant. And we can't do item slot copying yet - VirtualItem +# a full clone with all traits and items, but no antag roles +- type: cloningSettings + id: BaseClone + parent: [Body, Special, BaseEquipmentBlacklist] + # for cloning pods - type: cloningSettings id: CloningPod @@ -114,7 +124,7 @@ # changeling identity copying - type: cloningSettings id: ChangelingCloningSettings - parent: Body + parent: [Body, BaseEquipmentBlacklist] components: # These are already part of the base species prototype that is spawned for the clone, # that means we only need to copy them over when switching between species. @@ -135,9 +145,10 @@ - Sericulture # arachnids - MovementSpeedModifier # moths when weightless - JumpAbility # vulp leaping - copyEquipment: null copyInternalStorage: false copyImplants: false + raiseEntityRenamedEvent: false + # spawner - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index ae4a318859..be79392b9a 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -106,7 +106,6 @@ - Electrocution - TemporaryBlindness - Pacified - - RadiationProtection - Adrenaline - type: Temperature - type: TemperatureDamage @@ -300,3 +299,16 @@ range: 0 - type: WorldTargetAction event: !type:ActionGunShootEvent + +- type: entity + parent: Smoke + id: BloodSmoke + name: smoke + categories: [ HideSpawnMenu ] + components: + - type: Smoke + spreadAmount: 3 + duration: 2 + - type: Sprite + sprite: Effects/chemsmoke.rsi + state: chemsmoke_white diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index d6a9abc19b..2d44177026 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -34,7 +34,6 @@ - type: ActionGrant actions: - ActionJumpToCore - - ActionSurvCameraLights - ActionAIViewLaws # WL-Changes-start - ActionOpenRemoteDevicesMenu @@ -293,6 +292,16 @@ enum.StationAiVisualLayers.Base: False: { state: ai_empty } True: { state: ai_error } + - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_ai + - type: Reactive + reactions: + - reagents: [Water, SpaceCleaner] + methods: [Touch] + effects: + - !type:WashCreamPie - type: InteractionPopup interactSuccessString: petting-success-station-ai interactFailureString: petting-failure-station-ai @@ -493,6 +502,16 @@ description: The AI's viewer. categories: [ HideSpawnMenu, DoNotMap ] components: + - type: Fixtures + fixtures: + lightTrigger: + shape: + !type:PhysShapeCircle + radius: 8 + density: 80 + hard: false + layer: + - GhostImpassable - type: NoFTL - type: Tag tags: @@ -512,6 +531,7 @@ - state: ai_camera shader: unshaded map: ["base"] + - type: CameraActiveOnCollideCollider # The holographic representation of the AI that is projected from a holopad. - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index 2e52646dac..118c008291 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -883,11 +883,6 @@ heldPrefix: produce - type: Produce seedId: potato - - type: Extractable - juiceSolution: - reagents: - - ReagentId: JuicePotato - Quantity: 10 - type: Tag tags: - Potato diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 60f662d7e4..359b5ce709 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -1,6 +1,6 @@ - type: entity abstract: true - parent: [ BaseItem, StorePresetUplink ] #PDA's have uplinks so they have to inherit the data. + parent: [ BaseItem ] id: BasePDA name: PDA description: Personal Data Assistant. @@ -107,6 +107,7 @@ type: InstrumentBoundUserInterface enum.HealthAnalyzerUiKey.Key: type: HealthAnalyzerBoundUserInterface + - type: RemoteStore # PDAs can access uplinks so they need this component. - type: Tag tags: - DoorBumpOpener diff --git a/Resources/Prototypes/Entities/Objects/Devices/travel_camera.yml b/Resources/Prototypes/Entities/Objects/Devices/travel_camera.yml new file mode 100644 index 0000000000..5ec8b87ae0 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/travel_camera.yml @@ -0,0 +1,116 @@ +- type: entity + parent: BaseItem + id: TravelCamera + name: travel camera + description: A picture says more than a thousand words. Comes with an ultrabright flash and internal recharging photo roll. + components: + # aptly named component for picture taking + - type: PictureTaker + photographs: !type:GroupSelector + children: + - !type:NestedSelector + tableId: TravelCameraPhotographs + - type: Sprite + sprite: Objects/Devices/travel_camera.rsi + state: icon + - type: Flash + flashOnRangedInteract: true + sound: !type:SoundPathSpecifier + path: /Audio/Misc/camera_snap.ogg + - type: MeleeWeapon # allows using the camera in melee + damage: + types: + Blunt: 0 + - type: LimitedCharges + maxCharges: 4 + - type: AutoRecharge + rechargeDuration: 60 + - type: UseDelay + - type: StaticPrice + price: 100 + - type: Clothing + slots: + - NECK + sprite: Objects/Devices/travel_camera.rsi + +# we have a base prototype so that if we need to add components later on its easier +- type: entity + abstract: true + parent: BasePaper + id: BasePhotograph + name: photograph + +- type: entity + parent: BasePhotograph + id: PhotographBlack + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: black + +- type: entity + parent: BasePhotograph + id: PhotographRed + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: red + +- type: entity + parent: BasePhotograph + id: PhotographBlue + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: blue + +- type: entity + parent: BasePhotograph + id: PhotographGreen + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: green + +- type: entity + parent: BasePhotograph + id: PhotographYellow + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: yellow + +- type: entity + parent: BasePhotograph + id: PhotographPurple + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: purple + +- type: entity + parent: BasePhotograph + id: PhotographRainbow + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: rainbow + +- type: entityTable + id: TravelCameraPhotographs + table: !type:GroupSelector + children: + - id: PhotographBlack + - id: PhotographRed + - id: PhotographBlue + - id: PhotographGreen + - id: PhotographYellow + - id: PhotographPurple + - id: PhotographRainbow diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml index 532d0b9cca..70c823b765 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml @@ -500,7 +500,7 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - type: Extractable - grindableSolutionName: brassglass + grindableSolutionName: cglass - type: SolutionContainerManager solutions: cglass: diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index 8e29fdd88f..f114ece546 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -158,7 +158,7 @@ - Syndicate - type: entity - parent: [ BaseSubdermalImplant, StorePresetUplink ] + parent: BaseSubdermalImplant id: UplinkImplant name: uplink implant description: This implant lets the user access a hidden Syndicate uplink at will. @@ -169,9 +169,7 @@ whitelist: components: - Hands # prevent mouse buying grenade penguin since its not telepathic - - type: Store - balance: - Telecrystal: 0 + - type: RemoteStore - type: UserInterface interfaces: enum.StoreUiKey.Key: diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 97865aef32..3e9a89c0d2 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -1658,7 +1658,7 @@ - Plating - FloorMowedAstroGrass - type: Stack - stackType: FloorTileAstroGrass + stackType: FloorTileMowedAstroGrass - type: entity id: FloorTileItemJungleAstroGrass @@ -1675,7 +1675,7 @@ - Plating - FloorJungleAstroGrass - type: Stack - stackType: FloorTileAstroGrass + stackType: FloorTileJungleAstroGrass - type: entity parent: FloorTileItemBase diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/trashbag.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/trashbag.yml index 95e8fcd372..43a9ac228f 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/trashbag.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/trashbag.yml @@ -34,6 +34,7 @@ - type: Clothing slots: [belt] sprite: Objects/Specific/Janitorial/trashbag.rsi + equippedState: equipped-BELT - type: Item size: Normal @@ -50,6 +51,13 @@ heldPrefix: blue - type: StorageFillVisualizer fillBaseName: blue-icon + - type: Clothing + slots: [belt] + sprite: Objects/Specific/Janitorial/trashbag.rsi + equippedState: blue-equipped-BELT + - type: Tag + tags: + - TrashBagBlue - type: entity name: spell of all-consuming cleanliness diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml index c09bef4a3a..7555e88861 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -834,40 +834,33 @@ storagebase: !type:GroupSelector children: - id: PillDexalin - amount: &Range1to6 !type:RangeNumberSelector - range: 1, 6 + amount: 1, 6 - id: PillDylovene - amount: *Range1to6 + amount: 1, 6 - id: PillHyronalin - amount: *Range1to6 + amount: 1, 6 - id: PillPotassiumIodide - amount: *Range1to6 + amount: 1, 6 - id: PillIron - amount: *Range1to6 + amount: 1, 6 - id: PillCopper - amount: *Range1to6 + amount: 1, 6 - id: PillKelotane - amount: *Range1to6 + amount: 1, 6 - id: PillDermaline - amount: *Range1to6 + amount: 1, 6 - id: PillTricordrazine - amount: *Range1to6 + amount: 1, 6 - id: PillBicaridine - amount: *Range1to6 + amount: 1, 6 - id: PillCharcoal - amount: *Range1to6 - - id: PillAmbuzol - weight: 0.75 - amount: *Range1to6 - - id: PillAmbuzolPlus - weight: 0.75 - amount: *Range1to6 + amount: 1, 6 - id: PillSpaceDrugs weight: 0.75 - amount: *Range1to6 + amount: 1, 6 - id: StrangePill weight: 0.75 - amount: *Range1to6 + amount: 1, 6 # Syringes - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index 0618e308d5..64311dcde6 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -1540,6 +1540,22 @@ - type: BorgModuleIcon icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-door-remote-module } +- type: entity + parent: [ BaseXenoborgModuleEngi, BaseProviderBorgModule, BaseXenoborgContraband ] + id: XenoborgModuleTileGun + name: tile gun xenoborg module + description: Module with a tile gun. wait, a what? + components: + - type: Sprite + layers: + - state: xenoborg_engi + - state: icon-xenoborg-tile + - type: ItemBorgModule + hands: + - item: WeaponTileGun + - type: BorgModuleIcon + icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-tile-module } + - type: entity parent: [ BaseXenoborgModuleHeavy, BaseProviderBorgModule, BaseXenoborgContraband ] id: XenoborgModuleJammer @@ -1609,6 +1625,23 @@ - type: BorgModuleIcon icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-space-movement-module } +- type: entity + parent: [ BaseXenoborgModuleScout, BaseXenoborgContraband ] + id: XenoborgModuleJump + name: xenoborg jump module + description: Module that allows a xenoborg to jump forward. + components: + - type: ComponentBorgModule + components: + - type: JumpAbility + action: ActionJumpBoost + jumpDistance: 4 + jumpSound: /Audio/Effects/stealthoff.ogg + - type: Sprite + layers: + - state: xenoborg_scout + - state: icon-xenoborg-jump + - type: entity parent: [ BaseXenoborgModuleScout, BaseProviderBorgModule, BaseXenoborgContraband ] id: XenoborgModuleSword diff --git a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml index 8ce9de0f3b..574e079fed 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml @@ -5,7 +5,17 @@ components: - type: Sprite sprite: Objects/Tanks/generic.rsi - state: icon + layers: + - state: icon + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: Appearance + - type: MaxPressureVisuals - type: Item size: Normal sprite: Objects/Tanks/generic.rsi @@ -25,16 +35,20 @@ interfaces: enum.SharedGasTankUiKey.Key: type: GasTankBoundUserInterface + - type: AtmosDevice + requireAnchored: false + joinSystem: true - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # If gas tank volume is changed, adjust MinimumTritiumOxyburnEnergy in Atmospherics.cs by the same proportions volume: 5 temperature: 293.15 tankLowPressure: 30.0 - type: Explosive - explosionType: MicroBomb + explosionType: Minibomb # Mostly a pressure wave explosive so primarily blunt damage maxIntensity: 20 + tileBreakScale: 0.2 - type: MeleeWeapon wideAnimationRotation: 45 attackRate: 0.8 @@ -98,6 +112,7 @@ state: storage sprite: Objects/Tanks/emergency.rsi - type: GasTank + overpressure: 1519.875 # Poorly manufactured, they also lose pressure quickly so this makes exploding them easier, at the cost of the explosion being smaller. air: volume: 0.66 temperature: 293.15 @@ -231,7 +246,7 @@ description: Mixed anyone? It can hold 5 L of gas. components: - type: GasTank - outputPressure: 101.3 + releasePressure: 101.3 - type: entity parent: GasTankRoundBase @@ -244,7 +259,7 @@ - type: Item sprite: Objects/Tanks/anesthetic.rsi - type: GasTank - outputPressure: 30.4 + releasePressure: 30.4 - type: Clothing sprite: Objects/Tanks/anesthetic.rsi @@ -260,7 +275,7 @@ - type: Item sprite: Objects/Tanks/plasma.rsi - type: GasTank - outputPressure: 101.3 + releasePressure: 101.3 - type: Clothing sprite: Objects/Tanks/plasma.rsi slots: diff --git a/Resources/Prototypes/Entities/Objects/Tools/hijack_beacon.yml b/Resources/Prototypes/Entities/Objects/Tools/hijack_beacon.yml new file mode 100644 index 0000000000..d15b845322 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Tools/hijack_beacon.yml @@ -0,0 +1,34 @@ +- type: entity + parent: [ BaseItem, BaseSyndicateContraband ] + id: HijackBeacon + name: hijack beacon + description: A device that bypasses the firewall on Nanotrasen-brand Automated Trade Stations. + components: + - type: Transform + anchored: false + noRot: true + - type: Physics + bodyType: Dynamic + - type: Anchorable + - type: Item + size: Normal + - type: HijackBeacon + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.3,-0.3,0.3,0.3" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Clickable + - type: InteractionOutline + - type: Appearance + - type: Sprite + sprite: Objects/Tools/hijack_beacon.rsi + noRot: true + layers: + - state: extraction_point diff --git a/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml b/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml index 983a71a9eb..5b25c3d7e0 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml @@ -47,7 +47,7 @@ slots: - Back - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: volume: 5 temperature: 293.15 @@ -102,7 +102,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 13 minutes of thrust volume: 5 @@ -137,7 +137,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 13 minutes of thrust volume: 5 @@ -177,7 +177,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 13 minutes of thrust volume: 5 @@ -205,7 +205,8 @@ - suitStorage - Belt - type: GasTank - outputPressure: 42.6 + overpressure: 1519.875 # Poorly manufactured, they also lose pressure quickly so this makes exploding them easier, at the cost of the explosion being tiny. + releasePressure: 42.6 air: volume: 1.5 @@ -217,7 +218,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 4 minutes of thrust volume: 1.5 @@ -251,7 +252,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 4 minutes of thrust volume: 1.5 @@ -286,7 +287,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 13 minutes of thrust volume: 5 @@ -303,7 +304,7 @@ suffix: Infinite components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: volume: 5 temperature: 293.15 diff --git a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml index 34dcd66b71..c7cd0efe7b 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml @@ -20,6 +20,9 @@ - type: ContainerContainer containers: light_replacer_storage: !type:Container + - type: Tag + tags: + - LightReplacer - type: entity parent: LightReplacer diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml new file mode 100644 index 0000000000..402ad51ce7 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml @@ -0,0 +1,58 @@ +- type: entity + parent: [BaseItem, BaseXenoborgContraband] + id: WeaponTileGun + name: tile gun + description: A strange gun that shoots tiles. Shoot them with the floor! + components: + - type: Appearance + - type: Sprite + sprite: Objects/Weapons/Guns/Basic/tilegun.rsi + layers: + - state: icon + - state: mag-1 + map: ["enum.GunVisualLayers.Mag"] + - type: MagazineVisuals + magState: mag + steps: 7 + zeroVisible: false + - type: Item + size: Large + - type: ToolTileCompatible + - type: Tool + qualities: + - Prying + - type: AmmoCounter + - type: Gun + fireRate: 4 + maxAngle: 15 + minAngle: 1 + selectedMode: FullAuto + availableModes: + - FullAuto + soundGunshot: + path: /Audio/Weapons/Guns/Gunshots/magnetic_shot.ogg + soundEmpty: + path: /Audio/Weapons/Guns/Empty/empty_beep.ogg + - type: BallisticAmmoInteractLoader + - type: BallisticAmmoProvider + whitelist: + components: + - FloorTile + capacity: 30 + proto: FloorTileItemXenoborg + soundInsert: + path: /Audio/Weapons/Guns/MagIn/tile_load.ogg + - type: ContainerContainer + containers: + ballistic-ammo: !type:Container + ents: [] + - type: StaticPrice + price: 5000 + +- type: entity + parent: WeaponTileGun + id: WeaponTileGunEmpty + suffix: empty + components: + - type: BallisticAmmoProvider + proto: null diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/canister_grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/canister_grenades.yml index f65869d9dd..3b4bbdf036 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/canister_grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/canister_grenades.yml @@ -41,6 +41,9 @@ reagents: - ReagentId: SpaceCleaner Quantity: 30 + - type: Tag + tags: + - CleanerGrenade - type: entity parent: SmokeGrenade diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index 2f6ac834ba..6d5830e161 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -145,6 +145,7 @@ visible: false shader: unshaded - type: Flash + - type: RevolutionaryConverter - type: LimitedCharges maxCharges: 5 - type: MeleeWeapon diff --git a/Resources/Prototypes/Entities/StatusEffects/body.yml b/Resources/Prototypes/Entities/StatusEffects/body.yml index 7c7d5fc68c..c85f975b93 100644 --- a/Resources/Prototypes/Entities/StatusEffects/body.yml +++ b/Resources/Prototypes/Entities/StatusEffects/body.yml @@ -24,8 +24,9 @@ - type: DrunkStatusEffect - type: entity + abstract: true parent: MobStatusEffectBase - id: PainNumbnessTraitStatusEffect + id: PainNumbnessStatusEffectBase components: - type: StatusEffect whitelist: @@ -34,12 +35,6 @@ - MobThresholds - type: PainNumbnessStatusEffect -- type: entity - parent: BloodstreamStatusEffectBase - id: StatusEffectHemophiliaTrait - components: - - type: HemophiliaStatusEffect - - type: entity parent: BloodstreamStatusEffectDebuff id: StatusEffectAnticoagulant # Decreases the amount of blood coagulation when bleeding. @@ -59,7 +54,7 @@ bleedAmountMultiplier: 1.33 - type: entity - parent: [ PainNumbnessTraitStatusEffect, MobStatusEffectDebuff ] + parent: [ PainNumbnessStatusEffectBase, MobStatusEffectDebuff ] id: StatusEffectPainNumbness name: pain numbness diff --git a/Resources/Prototypes/Entities/StatusEffects/damage.yml b/Resources/Prototypes/Entities/StatusEffects/damage.yml new file mode 100644 index 0000000000..2853eb19e2 --- /dev/null +++ b/Resources/Prototypes/Entities/StatusEffects/damage.yml @@ -0,0 +1,27 @@ +## Abstract + +- type: entity + abstract: true + parent: StatusEffectBase + id: StatusEffectDamageModifierBase + description: Status effect for modifying incoming sources of damage. You shouldn't be seeing this. + components: + - type: DamageModifierStatusEffect + - type: StatusEffect + whitelist: + components: + - Damageable + +## Buff + +- type: entity + parent: StatusEffectDamageModifierBase + id: StatusEffectRadiationProtection + name: radiation protection + components: + - type: DamageModifierStatusEffect + modifiers: + coefficients: + Radiation: 0.1 + +## Debuff diff --git a/Resources/Prototypes/Entities/StatusEffects/traits.yml b/Resources/Prototypes/Entities/StatusEffects/traits.yml new file mode 100644 index 0000000000..82200e80e3 --- /dev/null +++ b/Resources/Prototypes/Entities/StatusEffects/traits.yml @@ -0,0 +1,18 @@ +- type: entity + abstract: true + parent: StatusEffectBase + id: TraitStatusEffectBase + components: + - type: CloneableStatusEffect + +- type: entity + parent: [BloodstreamStatusEffectBase, TraitStatusEffectBase] + id: TraitStatusEffectHemophilia + components: + - type: HemophiliaStatusEffect + +- type: entity + parent: [ PainNumbnessStatusEffectBase, TraitStatusEffectBase ] + id: TraitStatusEffectPainNumbness + name: pain numbness + diff --git a/Resources/Prototypes/Entities/Structures/Decoration/cobwebs.yml b/Resources/Prototypes/Entities/Structures/Decoration/cobwebs.yml index 15897201b2..95aa115310 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/cobwebs.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/cobwebs.yml @@ -28,14 +28,17 @@ behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] + - type: Construction + graph: CobwebsGraph + node: start - type: entity - id: Cobweb2 parent: Cobweb1 + id: Cobweb2 components: - type: Sprite sprite: Structures/Decoration/cobweb.rsi state: cobweb2 - type: Icon sprite: Structures/Decoration/cobweb.rsi - state: cobweb2 \ No newline at end of file + state: cobweb2 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index b5740bb345..539d2864c8 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -130,8 +130,6 @@ enum.WiresUiKey.Key: type: WiresBoundUserInterface - type: RadarConsole - - type: WorldLoader - radius: 256 - type: PointLight radius: 1.5 energy: 4.0 @@ -200,8 +198,6 @@ - Syndicate - type: RadarConsole maxRange: 384 - - type: WorldLoader - radius: 1536 - type: PointLight radius: 1.5 energy: 4.0 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/techdiskterminal.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/techdiskterminal.yml index dd58b9709d..5b250969ce 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/techdiskterminal.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/techdiskterminal.yml @@ -31,6 +31,8 @@ energy: 0.5 color: "#b53ca1" +# The value per-disk equate to an average of 1000 research points -> 100 spesos +# Average is calculated from this table, default speso value in TechnologyDiskComponent, and default point cost in DiskConsoleComponent - type: weightedRandom id: TechDiskTierWeights weights: diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index b9e4fb846b..005c597700 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -677,8 +677,6 @@ name: cutter machine description: This is a cutter. It cuts. Add variety to your station floor with eye-pleasing patterns! Don't stick your fingers in. components: - - type: Transform - noRot: false - type: Sprite sprite: Structures/Machines/cuttermachine.rsi snapCardinals: true @@ -701,10 +699,15 @@ - FloorSteelTilesStatic - FloorWhiteTilesStatic - FloorMaintsTilesStatic + - FloorIndustrialTilesStatic - FloorWoodTilesStatic - FloorConcreteTilesStatic - CircuitFloorsStatic - FloorMarbleTilesStatic + - FloorShuttleTilesStatic + - PlasticTilesStatic + - CarpetTilesStatic + - PreciousTilesStatic dynamicPacks: - FauxTiles - type: MaterialStorage diff --git a/Resources/Prototypes/Entities/Structures/Machines/salvage.yml b/Resources/Prototypes/Entities/Structures/Machines/salvage.yml index cafdc7cef2..dad4176617 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/salvage.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/salvage.yml @@ -54,11 +54,3 @@ - type: SalvageMagnet - type: ApcPowerReceiver powerLoad: 1000 - -- type: weightedRandomEntity - id: RandomAsteroidPool - weights: - AsteroidSalvageSmall: 3 - AsteroidSalvageMedium: 7 - AsteroidSalvageLarge: 5 - AsteroidSalvageHuge: 3 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml index aba7531ef5..90c33838ca 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml @@ -63,7 +63,7 @@ - type: AtmosDevice - type: TegGenerator thermalEfficiency: 0.1 - powerFactor: 1.75 + powerFactor: 2 - type: DeviceNetwork deviceNetId: AtmosDevices diff --git a/Resources/Prototypes/Entities/Structures/Power/chargers.yml b/Resources/Prototypes/Entities/Structures/Power/chargers.yml index 80d7cb0ba1..34dfd66216 100644 --- a/Resources/Prototypes/Entities/Structures/Power/chargers.yml +++ b/Resources/Prototypes/Entities/Structures/Power/chargers.yml @@ -111,6 +111,9 @@ blacklist: tags: - PotatoBattery + - type: Item + size: Ginormous + - type: MultiHandedItem - type: entity parent: [ BaseItemRecharger, ConstructibleMachine ] @@ -175,6 +178,9 @@ blacklist: tags: - PotatoBattery + - type: Item + size: Ginormous + - type: MultiHandedItem - type: entity parent: BaseItemRecharger @@ -200,6 +206,9 @@ blacklist: tags: - PotatoBattery + - type: Item + size: Ginormous + - type: MultiHandedItem - type: entity parent: [ BaseItemRecharger, BaseWallmount ] diff --git a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml index 038ac70d82..4311cd389e 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml @@ -122,18 +122,18 @@ parent: MopBucket suffix: full components: - - type: Sprite - layers: - - state: mopbucket - - state: mopbucket_water-3 - map: [ "enum.SolutionContainerLayers.Fill" ] - - type: SolutionContainerManager - solutions: - bucket: - maxVol: 600 - reagents: - - ReagentId: Water - Quantity: 600 + - type: Sprite + layers: + - state: mopbucket + - state: mopbucket_water-3 + map: [ "enum.SolutionContainerLayers.Fill" ] + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 600 + reagents: + - ReagentId: Water + Quantity: 600 - type: entity parent: BaseWrappedCube @@ -154,224 +154,220 @@ parent: [BaseStructureDynamic, StructureWheeled] description: This is the alpha and omega of sanitation. components: - - type: Sprite - noRot: true - sprite: Objects/Specific/Janitorial/janitorial_cart.rsi - layers: - - state: cart - - state: cart_water-1 - map: ["enum.SolutionContainerLayers.Fill"] - visible: false - - type: Rotatable - - type: InteractionOutline - # Removing storage until OnInteractUsing logic resolved - #- type: Storage - # popup: false - # capacity: 80 - # blacklist: # there is exclusive item slots for that - # tags: - # - Mop - # - TrashBag - # - Bucket - - type: ItemSlots - slots: - mop_slot: - name: janitorial-trolley-slot-component-slot-name-mop - whitelist: - tags: - - Mop - insertOnInteract: false # or it conflicts with bucket logic - priority: 9 # Higher than bucket slot - plunger_slot: - name: janitorial-trolley-slot-component-slot-name-plunger - whitelist: - tags: - - Plunger - - GoldenPlunger - priority: 8 - wetfloorsign_slot4: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot3: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot2: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot1: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - lightreplacer_slot: - name: janitorial-trolley-slot-component-slot-name-lightreplacer - whitelist: - components: - - LightReplacer - priority: 6 - spraybottle_slot: - name: janitorial-trolley-slot-component-slot-name-spray - whitelist: - tags: - - Spray - insertOnInteract: false # or it conflicts with bucket logic - priority: 5 # Higher than bucket slot - bucket_slot: - name: janitorial-trolley-slot-component-slot-name-bucket - whitelist: - tags: - - Bucket - insertOnInteract: false # or it also conflicts with bucket logic - priority: 4 # Higher than trash bag slot - trashbag_slot: - name: janitorial-trolley-slot-component-slot-name-trashbag - whitelist: - tags: - - TrashBag - priority: 3 # Higher than drinking priority - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.3 - density: 250 - layer: - - MobLayer - mask: - - MobMask - - type: Spillable - solution: bucket - spillDelay: 3.0 - spillWhenThrown: false - - type: SolutionContainerManager - solutions: - bucket: - maxVol: 800 - reagents: - - ReagentId: Water - Quantity: 600 # 3 quarters full at roundstart to make it more appealing - - type: DrainableSolution - solution: bucket - - type: RefillableSolution - solution: bucket - - type: ExaminableSolution - solution: bucket - - type: Damageable - damageContainer: Inorganic - damageModifierSet: Metallic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:EmptyAllContainersBehaviour - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: ItemMapper - mapLayers: - cart_plunger: - whitelist: - tags: - - Plunger - cart_goldenplunger: - whitelist: - tags: - - GoldenPlunger - cart_mop: - whitelist: - tags: - - MopBasic - cart_advmop: - whitelist: - tags: - - MopAdv - cart_garbage: - whitelist: - tags: - - TrashBag - cart_replacer: - whitelist: - components: - - LightReplacer - cart_spray: - whitelist: - tags: - - Spray - cart_sign1: # this is like stack of floor signs - minCount: 1 - whitelist: - tags: - - WetFloorSign - cart_sign2: - minCount: 2 - whitelist: - tags: - - WetFloorSign - cart_sign3: - minCount: 3 - whitelist: - tags: - - WetFloorSign - cart_sign4: - minCount: 4 - whitelist: - tags: - - WetFloorSign - cart_bucket: - whitelist: - tags: - - Bucket - sprite: Objects/Specific/Janitorial/janitorial_cart.rsi - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 3 - fillBaseName: cart_water- - - type: UserInterface - interfaces: - enum.StorageUiKey.Key: - type: StorageBoundUserInterface - - type: Edible - edible: Drink - solution: bucket - destroyOnEmpty: false - utensil: Spoon - - type: ContainerContainer - containers: - storagebase: !type:Container - ents: [] - mop_slot: !type:ContainerSlot {} - trashbag_slot: !type:ContainerSlot {} - bucket_slot: !type:ContainerSlot {} - plunger_slot: !type:ContainerSlot {} - goldenplunger_slot: !type:ContainerSlot {} - wetfloorsign_slot4: !type:ContainerSlot {} - wetfloorsign_slot3: !type:ContainerSlot {} - wetfloorsign_slot2: !type:ContainerSlot {} - wetfloorsign_slot1: !type:ContainerSlot {} - lightreplacer_slot: !type:ContainerSlot {} - spraybottle_slot: !type:ContainerSlot {} - - type: GuideHelp - guides: - - Janitorial - - type: DnaSubstanceTrace + - type: Sprite + noRot: true + sprite: Objects/Specific/Janitorial/janitorial_cart.rsi + layers: + - state: cart + - state: cart_water-1 + map: ["enum.SolutionContainerLayers.Fill"] + visible: false + - type: Rotatable + - type: InteractionOutline + - type: ItemSlots + slots: + mop_slot: + name: janitorial-trolley-slot-component-slot-name-mop + whitelist: + tags: + - Mop + insertOnInteract: false # or it conflicts with bucket logic + priority: 9 # Higher than bucket slot + plunger_slot: + name: janitorial-trolley-slot-component-slot-name-plunger + whitelist: + tags: + - Plunger + - GoldenPlunger + priority: 8 + wetfloorsign_slot4: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot3: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot2: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot1: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + lightreplacer_slot: + name: janitorial-trolley-slot-component-slot-name-lightreplacer + whitelist: + components: + - LightReplacer + priority: 6 + spraybottle_slot: + name: janitorial-trolley-slot-component-slot-name-spray + whitelist: + tags: + - Spray + insertOnInteract: false # or it conflicts with bucket logic + priority: 5 # Higher than bucket slot + bucket_slot: + name: janitorial-trolley-slot-component-slot-name-bucket + whitelist: + tags: + - Bucket + insertOnInteract: false # or it also conflicts with bucket logic + priority: 4 # Higher than trash bag slot + trashbag_slot: + name: janitorial-trolley-slot-component-slot-name-trashbag + whitelist: + tags: + - TrashBag + - TrashBagBlue + priority: 3 # Higher than drinking priority + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.3 + density: 250 + layer: + - MobLayer + mask: + - MobMask + - type: Spillable + solution: bucket + spillDelay: 3.0 + spillWhenThrown: false + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 800 + reagents: + - ReagentId: Water + Quantity: 600 # 3 quarters full at roundstart to make it more appealing + - type: DrainableSolution + solution: bucket + - type: RefillableSolution + solution: bucket + - type: ExaminableSolution + solution: bucket + - type: Damageable + damageContainer: Inorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 400 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: ItemMapper + mapLayers: + cart_plunger: + whitelist: + tags: + - Plunger + cart_goldenplunger: + whitelist: + tags: + - GoldenPlunger + cart_mop: + whitelist: + tags: + - MopBasic + cart_advmop: + whitelist: + tags: + - MopAdv + cart_garbage: + whitelist: + tags: + - TrashBag + cart_garbage_blue: + whitelist: + tags: + - TrashBagBlue + cart_replacer: + whitelist: + components: + - LightReplacer + cart_spray: + whitelist: + tags: + - Spray + cart_sign1: # this is like stack of floor signs + minCount: 1 + whitelist: + tags: + - WetFloorSign + cart_sign2: + minCount: 2 + whitelist: + tags: + - WetFloorSign + cart_sign3: + minCount: 3 + whitelist: + tags: + - WetFloorSign + cart_sign4: + minCount: 4 + whitelist: + tags: + - WetFloorSign + cart_bucket: + whitelist: + tags: + - Bucket + sprite: Objects/Specific/Janitorial/janitorial_cart.rsi + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 3 + fillBaseName: cart_water- + - type: UserInterface + interfaces: + enum.StorageUiKey.Key: + type: StorageBoundUserInterface + - type: Edible + edible: Drink + solution: bucket + destroyOnEmpty: false + utensil: Spoon + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] + mop_slot: !type:ContainerSlot {} + trashbag_slot: !type:ContainerSlot {} + bucket_slot: !type:ContainerSlot {} + plunger_slot: !type:ContainerSlot {} + goldenplunger_slot: !type:ContainerSlot {} + wetfloorsign_slot4: !type:ContainerSlot {} + wetfloorsign_slot3: !type:ContainerSlot {} + wetfloorsign_slot2: !type:ContainerSlot {} + wetfloorsign_slot1: !type:ContainerSlot {} + lightreplacer_slot: !type:ContainerSlot {} + spraybottle_slot: !type:ContainerSlot {} + - type: GuideHelp + guides: + - Janitorial + - type: DnaSubstanceTrace diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index 38ec6f8cc4..7ffc594342 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -17,6 +17,13 @@ - state: paper visible: false map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-grey + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false - type: Appearance - type: GenericVisualizer visuals: @@ -114,13 +121,24 @@ rotationsEnabled: false volume: 1 - type: GasPortable + - type: MaxPressureVisuals + integrityMask: mask-grey - type: GasCanister + maxIntegrity: 15 # 5 times stronger than a gas tank + integrity: 15 + maxReleasePressure: 1013.25 + safetyPressure: 10132.5 # High enough that liquid tanks don't burst immediately, with an extra 33% wiggle room to work with + overpressure: 20265 # Purposefully high because cans react really fucking fast, may need to redo this value if reactions change! gasTankSlot: name: comp-gas-canister-slot-name-gas-tank ejectOnBreak: true whitelist: components: - GasTank + - type: Explosive + explosionType: Minibomb # Mostly a pressure wave explosive so primarily blunt damage + maxIntensity: 100 + tileBreakScale: 0.2 # Keeps the can from spacing all the hot gas it has now vented :). If this proves to be too much it can be removed. - type: StaticPrice price: 200 - type: AccessReader @@ -140,9 +158,21 @@ components: - type: Sprite layers: - - state: yellow + - state: yellow + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-yellow + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-yellow - type: GasCanister - gasMixture: + air: volume: 1500 temperature: 293.15 - type: Destructible @@ -178,10 +208,23 @@ description: A canister that can contain any type of gas. This one is supposed to contain air mixture. It can be attached to connector ports using a wrench. components: - type: Sprite + sprite: Structures/Storage/canister.rsi layers: - - state: grey + - state: grey + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-grey + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-grey - type: GasCanister - gasMixture: + air: volume: 1500 moles: Oxygen: 581.56 # oxygen 21% @@ -219,9 +262,21 @@ components: - type: Sprite layers: - - state: blue + - state: blue + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-blue + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-blue - type: GasCanister - gasMixture: + air: volume: 1500 moles: Oxygen: 2769.36 # oxygen @@ -257,7 +312,7 @@ description: A canister that can contain any type of gas. This one is supposed to contain liquid oxygen. It can be attached to connector ports using a wrench. components: - type: GasCanister - gasMixture: + air: volume: 1500 moles: Oxygen: 18710.71051 # oxygen @@ -273,9 +328,21 @@ components: - type: Sprite layers: - - state: red + - state: red + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-red + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-red - type: GasCanister - gasMixture: + air: volume: 1500 moles: Nitrogen: 2769.36 # nitrogen @@ -311,7 +378,7 @@ description: A canister that can contain any type of gas. This one is supposed to contain liquid nitrogen. It can be attached to connector ports using a wrench. components: - type: GasCanister - gasMixture: + air: volume: 1500 moles: Nitrogen: 18710.71051 # nitrogen @@ -327,9 +394,21 @@ components: - type: Sprite layers: - - state: black + - state: black + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-black + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-black - type: GasCanister - gasMixture: + air: volume: 1500 moles: CarbonDioxide: 2769.36 # CO2 @@ -367,7 +446,7 @@ description: A canister that can contain any type of gas. This one is supposed to contain liquid carbon dioxide. It can be attached to connector ports using a wrench. components: - type: GasCanister - gasMixture: + air: volume: 1500 moles: CarbonDioxide: 18710.71051 # CO2 @@ -383,9 +462,21 @@ components: - type: Sprite layers: - - state: orange + - state: orange + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-orange + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-orange - type: GasCanister - gasMixture: + air: volume: 1500 moles: Plasma: 2769.36 # plasma @@ -424,9 +515,21 @@ components: - type: Sprite layers: - - state: green + - state: green + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-green + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-green - type: GasCanister - gasMixture: + air: volume: 1500 moles: Tritium: 2769.36 # Tritium @@ -465,9 +568,21 @@ components: - type: Sprite layers: - - state: water_vapor + - state: water_vapor + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-water_vapor + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-water_vapor - type: GasCanister - gasMixture: + air: volume: 1500 moles: WaterVapor: 2769.36 # Water vapor @@ -504,9 +619,21 @@ components: - type: Sprite layers: - - state: greenys + - state: greenys + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-greenys + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-greenys - type: GasCanister - gasMixture: + air: volume: 1500 moles: Ammonia: 2769.36 # Ammonia @@ -545,9 +672,21 @@ components: - type: Sprite layers: - - state: redws + - state: redws + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-redws + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-redws - type: GasCanister - gasMixture: + air: volume: 1500 moles: NitrousOxide: 2769.36 # N2O @@ -587,8 +726,20 @@ - type: Sprite layers: - state: frezon + - state: paper + visible: false + map: [ "enum.PaperLabelVisuals.Layer" ] + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask-frezon + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: MaxPressureVisuals + integrityMask: mask-frezon - type: GasCanister - gasMixture: + air: volume: 1500 moles: Frezon: 2769.36 # Frezon @@ -619,6 +770,21 @@ - type: Lock locked: true + +- type: entity + parent: GasCanister + id: MaxCapCanister # As of writing canisters currently have about 9000 explosive intensity. 95% the total damage of a syndicate bomb but spread across a larger area. + name: max cap in a can + suffix: DEBUG, Max Cap + components: + - type: GasCanister + air: + volume: 1500 + moles: + Oxygen: 1400 + Tritium: 700 + temperature: 373.149 + # Broke Entities - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml index 8f22f8d68c..1410eb4e1c 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml @@ -454,6 +454,7 @@ hard: True restitution: 0 friction: 0.4 + - type: EntityStorage - type: entity parent: LockerPrisoner diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml index aceb8dc4f8..4c03c3107f 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml @@ -1,7 +1,7 @@ - type: entity + abstract: true parent: BaseSign id: PosterBase - abstract: true components: - type: Sprite sprite: Structures/Wallmounts/posters.rsi @@ -34,6 +34,9 @@ min: 1 max: 1 offset: 0 + - type: Construction + graph: PosterGraph + node: start - type: entity parent: BaseSign @@ -45,6 +48,8 @@ drawdepth: WallTops sprite: Structures/Wallmounts/posters.rsi state: poster_broken + - type: Damageable + damageModifierSet: Card - type: Destructible thresholds: - trigger: @@ -56,6 +61,9 @@ path: /Audio/Effects/poster_broken.ogg - !type:DoActsBehavior acts: [ "Destruction" ] + - type: Construction + graph: PosterGraph + node: tornPoster # Contraband - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml index f1d182fa60..fda76c6ce1 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml @@ -128,6 +128,25 @@ - state: panel visible: false map: [ "wires" ] + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: Appearance - type: GenericVisualizer visuals: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml index 561991cd26..a12bca758d 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml @@ -11,7 +11,7 @@ light: shape: !type:PhysShapeCircle - radius: 5 + radius: 0.5 hard: false mask: - GhostImpassable @@ -24,7 +24,8 @@ - None layer: - TabletopMachineLayer - - type: LightOnCollide + - type: CameraActiveOnCollide + requiresPower: false # TODO: Change this when AI can no longer see via unpowered cameras - type: PointLight enabled: false radius: 5 diff --git a/Resources/Prototypes/Entities/Structures/Walls/grille.yml b/Resources/Prototypes/Entities/Structures/Walls/grille.yml index 55b06f1e60..b20256ec7e 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/grille.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/grille.yml @@ -59,6 +59,8 @@ shape: !type:PhysShapeAabb bounds: "-0.5,-0.5,0.5,0.5" + mask: + - FullTileMask layer: - GlassLayer - type: Destructible diff --git a/Resources/Prototypes/Entities/Structures/spider_web.yml b/Resources/Prototypes/Entities/Structures/spider_web.yml index 1049129c7b..1b039be8ea 100644 --- a/Resources/Prototypes/Entities/Structures/spider_web.yml +++ b/Resources/Prototypes/Entities/Structures/spider_web.yml @@ -1,6 +1,6 @@ - type: entity - id: SpiderWebBase abstract: true + id: SpiderWebBase placement: mode: SnapgridCenter snap: @@ -45,6 +45,9 @@ additionalKeys: - walls base: web_ + - type: Construction + graph: CobwebsGraph + node: start - type: entity id: SpiderWeb diff --git a/Resources/Prototypes/Entities/World/Debris/asteroids.yml b/Resources/Prototypes/Entities/World/Debris/asteroids.yml deleted file mode 100644 index bb33801e2d..0000000000 --- a/Resources/Prototypes/Entities/World/Debris/asteroids.yml +++ /dev/null @@ -1,144 +0,0 @@ -- type: entity - id: BaseAsteroidDebris - parent: BaseDebris - name: asteroid debris - abstract: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorTileset: - - FloorAsteroidSand - blobDrawProb: 0.5 - radius: 6 - floorPlacements: 16 - - type: SimpleFloorPlanPopulator - entries: - FloorAsteroidSand: - - id: WallRock - prob: 0.5 - orGroup: rock - - id: WallRockCoal - prob: 0.15 - orGroup: rock - - id: WallRockTin - prob: 0.15 - orGroup: rock - - id: WallRockQuartz - prob: 0.15 - orGroup: rock - - id: WallRockSalt - prob: 0.15 - orGroup: rock - - id: WallRockGold - prob: 0.05 - orGroup: rock - - id: WallRockDiamond - prob: 0.005 - orGroup: rock - - id: WallRockSilver - prob: 0.05 - orGroup: rock - - id: WallRockPlasma - prob: 0.05 - orGroup: rock - - id: WallRockUranium - prob: 0.02 - orGroup: rock - - id: WallRockBananium - prob: 0.02 - orGroup: rock - - id: WallRockArtifactFragment - prob: 0.01 - orGroup: rock - - type: IFF - flags: HideLabel - color: "#d67e27" - -- type: entity - id: AsteroidDebrisSmall - parent: BaseAsteroidDebris - name: asteroid debris small - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 8 - -- type: entity - id: AsteroidDebrisMedium - parent: BaseAsteroidDebris - name: asteroid debris medium - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 16 - -- type: entity - id: AsteroidDebrisLarge - parent: BaseAsteroidDebris - name: asteroid debris large - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 24 - -- type: entity - id: AsteroidDebrisLarger - parent: BaseAsteroidDebris - name: asteroid debris larger - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - radius: 12 - floorPlacements: 36 - -- type: entity - id: AsteroidSalvageSmall - parent: BaseAsteroidDebris - name: salvage asteroid small - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - blobDrawProb: 0.66 - radius: 15 - floorPlacements: 100 - -- type: entity - id: AsteroidSalvageMedium - parent: BaseAsteroidDebris - name: salvage asteroid medium - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - blobDrawProb: 0.66 - radius: 17 - floorPlacements: 150 - -- type: entity - id: AsteroidSalvageLarge - parent: BaseAsteroidDebris - name: salvage asteroid large - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - blobDrawProb: 0.66 - radius: 20 - floorPlacements: 200 - -- type: entity - id: AsteroidSalvageHuge - parent: BaseAsteroidDebris - name: salvage asteroid huge - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - blobDrawProb: 0.66 - radius: 23 - floorPlacements: 250 diff --git a/Resources/Prototypes/Entities/World/Debris/base_debris.yml b/Resources/Prototypes/Entities/World/Debris/base_debris.yml deleted file mode 100644 index c125d991fd..0000000000 --- a/Resources/Prototypes/Entities/World/Debris/base_debris.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: entity - id: BaseDebris - abstract: true - components: - - type: OwnedDebris - - type: LocalityLoader diff --git a/Resources/Prototypes/Entities/World/Debris/wrecks.yml b/Resources/Prototypes/Entities/World/Debris/wrecks.yml deleted file mode 100644 index 7bbeadeb5b..0000000000 --- a/Resources/Prototypes/Entities/World/Debris/wrecks.yml +++ /dev/null @@ -1,80 +0,0 @@ -- type: entity - id: BaseScrapDebris - parent: BaseDebris - name: scrap debris - abstract: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorTileset: - - Plating - - Plating - - Plating - - FloorSteel - - Lattice - blobDrawProb: 0.5 - radius: 6 - floorPlacements: 16 - - type: SimpleFloorPlanPopulator - entries: - Plating: - - prob: 3 # Intentional blank. - - id: SalvageMaterialCrateSpawner - prob: 1 - - id: SalvageCanisterSpawner - prob: 0.2 - - id: SalvageMobSpawner - prob: 0.7 - - id: WallSolid - prob: 1 - - id: Grille - prob: 0.5 - Lattice: - - prob: 2 - - id: Grille - prob: 0.2 - - id: SalvageMaterialCrateSpawner - prob: 0.3 - - id: SalvageCanisterSpawner - prob: 0.2 - FloorSteel: - - prob: 3 # Intentional blank. - - id: SalvageMaterialCrateSpawner - prob: 1 - - id: SalvageCanisterSpawner - prob: 0.2 - - id: SalvageMobSpawner - prob: 0.7 - - type: IFF - flags: HideLabel - color: "#88b0d1" - -- type: entity - id: ScrapDebrisSmall - parent: BaseScrapDebris - name: scrap debris small - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 8 - -- type: entity - id: ScrapDebrisMedium - parent: BaseScrapDebris - name: scrap debris medium - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 16 - -- type: entity - id: ScrapDebrisLarge - parent: BaseScrapDebris - name: scrap debris large - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 24 diff --git a/Resources/Prototypes/Entities/World/chunk.yml b/Resources/Prototypes/Entities/World/chunk.yml deleted file mode 100644 index c7cb09c2a4..0000000000 --- a/Resources/Prototypes/Entities/World/chunk.yml +++ /dev/null @@ -1,14 +0,0 @@ -- type: entity - id: WorldChunk - parent: MarkerBase - name: world chunk - description: | - It's rude to stare. - It's also a bit odd you're looking at the abstract representation of the grid of reality. - categories: [ HideSpawnMenu ] - components: - - type: WorldChunk - - type: Sprite - sprite: Markers/cross.rsi - layers: - - state: blue diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index e41cb20668..876cd46b46 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -353,8 +353,8 @@ components: - type: StationEvent weight: 1 # rare - duration: 1 - earliestStart: 40 # Corvax-MRP # 30 + duration: null + earliestStart: 30 reoccurrenceDelay: 60 minimumPlayers: 30 - type: AntagSelection diff --git a/Resources/Prototypes/GameRules/meteorswarms.yml b/Resources/Prototypes/GameRules/meteorswarms.yml index 99a803f1d7..9d047a25d5 100644 --- a/Resources/Prototypes/GameRules/meteorswarms.yml +++ b/Resources/Prototypes/GameRules/meteorswarms.yml @@ -4,14 +4,14 @@ id: MeteorSwarmDustEventsTable table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp children: - - id: GameRuleSpaceDustMinor - - id: GameRuleSpaceDustMajor + - id: SpaceDustMinor + - id: SpaceDustMajor - type: entityTable id: MeteorSwarmSmallChanceEventsTable table: !type:AllSelector # we need to pass a list of rules, since rules have further restrictions to consider via StationEventComp children: - - id: GameRuleMeteorSwarmSmall + - id: MeteorSwarmSmall prob: 0.15 - type: entityTable @@ -29,14 +29,14 @@ children: - !type:NestedSelector tableId: MeteorSwarmDustEventsTable - - id: GameRuleMeteorSwarmSmall - - id: GameRuleMeteorSwarmMedium - - id: GameRuleMeteorSwarmLarge - - id: GameRuleUristSwarm - - id: GameRuleClownSwarm - - id: GameRuleCowSwarm - - id: GameRulePotatoSwarm - - id: GameRuleFunSwarm + - id: MeteorSwarmSmall + - id: MeteorSwarmMedium + - id: MeteorSwarmLarge + - id: UristSwarm + - id: ClownSwarm + - id: CowSwarm + - id: PotatoSwarm + - id: FunSwarm - id: ImmovableRodSpawn - type: weightedRandomEntity @@ -94,7 +94,7 @@ - type: entity parent: BaseGameRule - id: GameRuleMeteorSwarm + id: MeteorSwarm abstract: true components: - type: GameRule @@ -106,8 +106,8 @@ announcementSound: /Audio/Announcements/meteors_start.ogg # Corvax-Announcements - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleSpaceDustMinor + parent: MeteorSwarm + id: SpaceDustMinor components: - type: StationEvent weight: 44 @@ -127,8 +127,8 @@ max: 5 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleSpaceDustMajor + parent: MeteorSwarm + id: SpaceDustMajor components: - type: StationEvent weight: 22 @@ -147,8 +147,8 @@ max: 12 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleMeteorSwarmSmall + parent: MeteorSwarm + id: MeteorSwarmSmall components: - type: StationEvent weight: 18 @@ -159,8 +159,8 @@ MeteorMedium: 3 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleMeteorSwarmMedium + parent: MeteorSwarm + id: MeteorSwarmMedium components: - type: StationEvent weight: 10 @@ -171,8 +171,8 @@ MeteorLarge: 1 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleMeteorSwarmLarge + parent: MeteorSwarm + id: MeteorSwarmLarge components: - type: StationEvent weight: 5 @@ -183,8 +183,8 @@ MeteorLarge: 4 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleUristSwarm + parent: MeteorSwarm + id: UristSwarm components: - type: StationEvent weight: 0.05 @@ -244,8 +244,8 @@ orGroup: rodProto - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleCowSwarm + parent: MeteorSwarm + id: CowSwarm components: - type: StationEvent weight: 0.05 @@ -262,8 +262,8 @@ max: 10 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleClownSwarm + parent: MeteorSwarm + id: ClownSwarm components: - type: StationEvent weight: 0.05 @@ -280,8 +280,8 @@ max: 10 - type: entity - parent: GameRuleMeteorSwarm - id: GameRulePotatoSwarm + parent: MeteorSwarm + id: PotatoSwarm components: - type: StationEvent weight: 0.05 @@ -298,8 +298,8 @@ max: 10 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleFunSwarm + parent: MeteorSwarm + id: FunSwarm components: - type: StationEvent weight: 0.03 diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index 8e4ccf14d9..d177734120 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -393,11 +393,6 @@ - ChangelingDNA balance: ChangelingDNA: 50 - - type: UserInterface - interfaces: - enum.StoreUiKey.Key: - type: StoreBoundUserInterface - requireInputValidation: false mindRoles: - MindRoleChangeling - type: AntagObjectives diff --git a/Resources/Prototypes/Hydroponics/randomChemicals.yml b/Resources/Prototypes/Hydroponics/randomChemicals.yml index 3b8d225c36..b3468c0848 100644 --- a/Resources/Prototypes/Hydroponics/randomChemicals.yml +++ b/Resources/Prototypes/Hydroponics/randomChemicals.yml @@ -114,7 +114,6 @@ - SodaWater - Ice - JuiceCarrot - - JuicePotato - Protein - Flour - Soysauce diff --git a/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml index 1c545ddbe8..f1201b6fa8 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml @@ -9,6 +9,11 @@ equipment: head: ClothingHeadHatJesterAlt +- type: loadout + id: ClownMitre + equipment: + head: ClothingHeadHatMitreClown + # Jumpsuit - type: loadout id: ClownSuit diff --git a/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml b/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml index bb1b1453d5..26ea3ce6e9 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml @@ -6,12 +6,12 @@ requirement: !type:RoleTimeRequirement role: JobAtmosphericTechnician - time: 20h # Corvax-RoleTime + time: 6h # Corvax-RoleTime - !type:JobRequirementLoadoutEffect requirement: !type:RoleTimeRequirement role: JobStationEngineer - time: 20h # Corvax-RoleTime + time: 6h # Corvax-RoleTime - !type:JobRequirementLoadoutEffect requirement: !type:DepartmentTimeRequirement @@ -42,9 +42,9 @@ - type: loadout id: SeniorEngineerBeret startingGear: EngineeringBeret - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorEngineering + effects: + - !type:GroupLoadoutEffect + proto: SeniorEngineering #WL Workers Lyvsi engi resprite start - type: loadout @@ -74,18 +74,18 @@ - type: loadout id: SeniorEngineerJumpsuit - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorEngineering + effects: + - !type:GroupLoadoutEffect + proto: SeniorEngineering equipment: jumpsuit: ClothingUniformJumpsuitSeniorEngineer groupBy: "WLJJ" # WL-change-add - type: loadout id: SeniorEngineerJumpskirt - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorEngineering + effects: + - !type:GroupLoadoutEffect + proto: SeniorEngineering equipment: jumpsuit: ClothingUniformJumpskirtSeniorEngineer groupBy: "WLJJ" # WL-change-add @@ -136,8 +136,8 @@ - type: loadout id: SeniorEngineerPDA - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorEngineering + effects: + - !type:GroupLoadoutEffect + proto: SeniorEngineering equipment: id: SeniorEngineerPDA diff --git a/Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml b/Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml index 65fd9d80c2..77e0b04576 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml @@ -1,9 +1,9 @@ # Head - type: loadout id: SeniorPhysicianBeret - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorPhysician + effects: + - !type:GroupLoadoutEffect + proto: SeniorPhysician equipment: head: ClothingHeadHatBeretSeniorPhysician @@ -55,18 +55,18 @@ - type: loadout id: SeniorPhysicianJumpsuit - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorPhysician + effects: + - !type:GroupLoadoutEffect + proto: SeniorPhysician equipment: jumpsuit: ClothingUniformJumpsuitSeniorPhysician groupBy: "WLJJ" # WL-change-add - type: loadout id: SeniorPhysicianJumpskirt - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorPhysician + effects: + - !type:GroupLoadoutEffect + proto: SeniorPhysician equipment: jumpsuit: ClothingUniformJumpskirtSeniorPhysician groupBy: "WLJJ" # WL-change-add @@ -113,9 +113,9 @@ - type: loadout id: SeniorPhysicianLabCoat - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorPhysician + effects: + - !type:GroupLoadoutEffect + proto: SeniorPhysician equipment: outerClothing: ClothingOuterCoatLabSeniorPhysician @@ -133,9 +133,9 @@ - type: loadout id: SeniorPhysicianPDA - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorPhysician + effects: + - !type:GroupLoadoutEffect + proto: SeniorPhysician equipment: id: SeniorPhysicianPDA diff --git a/Resources/Prototypes/Loadouts/Jobs/Medical/role_timers.yml b/Resources/Prototypes/Loadouts/Jobs/Medical/role_timers.yml index 7eeab8ea8e..b07be72f89 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/role_timers.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/role_timers.yml @@ -6,12 +6,12 @@ requirement: !type:RoleTimeRequirement role: JobChemist - time: 20h # Corvax-RoleTime + time: 6h # Corvax-RoleTime - !type:JobRequirementLoadoutEffect requirement: !type:RoleTimeRequirement role: JobMedicalDoctor - time: 20h # Corvax-RoleTime + time: 6h # Corvax-RoleTime - !type:JobRequirementLoadoutEffect requirement: !type:DepartmentTimeRequirement diff --git a/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml b/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml index 48f8d15cef..380d5d39a3 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml @@ -17,9 +17,9 @@ - type: loadout id: ScientificBeret startingGear: ScientificBeret - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorResearcher + effects: + - !type:GroupLoadoutEffect + proto: SeniorResearcher - type: loadout id: RoboticistCap @@ -65,18 +65,18 @@ - type: loadout id: SeniorResearcherJumpsuit - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorResearcher + effects: + - !type:GroupLoadoutEffect + proto: SeniorResearcher equipment: jumpsuit: ClothingUniformJumpsuitSeniorResearcher groupBy: "WLJJ" # WL-change-add - type: loadout id: SeniorResearcherJumpskirt - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorResearcher + effects: + - !type:GroupLoadoutEffect + proto: SeniorResearcher equipment: jumpsuit: ClothingUniformJumpskirtSeniorResearcher groupBy: "WLJJ" # WL-change-add @@ -125,9 +125,9 @@ - type: loadout id: SeniorResearcherLabCoat - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorResearcher + effects: + - !type:GroupLoadoutEffect + proto: SeniorResearcher equipment: outerClothing: ClothingOuterCoatLabSeniorResearcher @@ -156,8 +156,8 @@ - type: loadout id: SeniorResearcherPDA - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorResearcher + effects: + - !type:GroupLoadoutEffect + proto: SeniorResearcher equipment: id: SeniorResearcherPDA diff --git a/Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml b/Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml index 62e9c163b7..b3d3d24eea 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml @@ -6,7 +6,17 @@ requirement: !type:RoleTimeRequirement role: JobWarden - time: 20h # Corvax-RoleTime + time: 6h # Corvax-RoleTime-Start + - !type:JobRequirementLoadoutEffect + requirement: + !type:RoleTimeRequirement + role: JobDetective + time: 2h + - !type:JobRequirementLoadoutEffect + requirement: + !type:RoleTimeRequirement + role: JobSecurityOfficer + time: 6h # Corvax-RoleTime-End - !type:JobRequirementLoadoutEffect requirement: !type:DepartmentTimeRequirement @@ -67,18 +77,18 @@ - type: loadout id: SeniorOfficerJumpsuit - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorOfficer + effects: + - !type:GroupLoadoutEffect + proto: SeniorOfficer equipment: jumpsuit: ClothingUniformJumpsuitSeniorOfficer groupBy: "WLJJ" # WL-change-add - type: loadout id: SeniorOfficerJumpskirt - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorOfficer + effects: + - !type:GroupLoadoutEffect + proto: SeniorOfficer equipment: jumpsuit: ClothingUniformJumpskirtSeniorOfficer groupBy: "WLJJ" # WL-change-add @@ -121,6 +131,13 @@ equipment: outerClothing: ClothingOuterArmorBasicSlim +# Corvax-Start +- type: loadout + id: SecurityOfficerOvercoat + equipment: + outerClothing: ClothingOuterCoatSecurityOvercoat +# Corvax-End + - type: loadout id: SecurityOfficerWintercoat equipment: @@ -150,8 +167,8 @@ - type: loadout id: SeniorOfficerPDA - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorOfficer + effects: + - !type:GroupLoadoutEffect + proto: SeniorOfficer equipment: id: SeniorOfficerPDA diff --git a/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml b/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml index c718821f72..8e5cfbd316 100644 --- a/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml @@ -1158,6 +1158,7 @@ loadouts: - JesterHat - JesterAltHat + - ClownMitre - type: loadoutGroup id: ClownJumpsuit @@ -2786,6 +2787,7 @@ - ArmorVestSec # Corvax - ArmorVest # - ArmorVestSlim # WL-Changes-RemovingSlimArmorFromSec + - SecurityOfficerOvercoat # Corvax - SecurityOfficerWintercoat - type: loadoutGroup diff --git a/Resources/Prototypes/NPCs/Combat/gun.yml b/Resources/Prototypes/NPCs/Combat/gun.yml index 2b5ccd9fa5..abecd18719 100644 --- a/Resources/Prototypes/NPCs/Combat/gun.yml +++ b/Resources/Prototypes/NPCs/Combat/gun.yml @@ -96,6 +96,29 @@ - type: htnCompound id: RangedCombatCompound branches: + # Close or rack the gun chamber if necessary + - preconditions: + - !type:ActiveHandComponentPrecondition + components: + - type: Gun + - !type:GunAmmoPrecondition + minPercent: 0.001 + - !type:NeedToRackBoltPrecondition + tasks: + - !type:HTNPrimitiveTask + operator: !type:RackBoltOperator + + # Wield gun + - preconditions: + - !type:ActiveHandComponentPrecondition + components: + - type: Gun + - !type:WieldedPrecondition + wielded: false + tasks: + - !type:HTNPrimitiveTask + operator: !type:WieldOperator + # Move to target and shoot them if ammo - preconditions: - !type:GunAmmoPrecondition diff --git a/Resources/Prototypes/NPCs/Combat/melee.yml b/Resources/Prototypes/NPCs/Combat/melee.yml index e25284bb88..c6fce02416 100644 --- a/Resources/Prototypes/NPCs/Combat/melee.yml +++ b/Resources/Prototypes/NPCs/Combat/melee.yml @@ -50,6 +50,34 @@ - !type:HTNCompoundTask task: PickupMeleeCompound + # Wield melee weapon + - preconditions: + - !type:ActiveHandComponentPrecondition + components: + - type: MeleeWeapon + damage: + types: + Blunt: 0 + - !type:WieldedPrecondition + wielded: false + tasks: + - !type:HTNPrimitiveTask + operator: !type:WieldOperator + + # Activate toggleable weapons + - preconditions: + - !type:ActiveHandComponentPrecondition + components: + - type: MeleeWeapon + damage: + types: + Blunt: 0 + - !type:ItemTogglePrecondition + activated: false + tasks: + - !type:HTNPrimitiveTask + operator: !type:UseItemInHandOperator + # Melee combat (unarmed or otherwise) - tasks: - !type:HTNCompoundTask diff --git a/Resources/Prototypes/NPCs/monkey.yml b/Resources/Prototypes/NPCs/monkey.yml new file mode 100644 index 0000000000..3e321a2d38 --- /dev/null +++ b/Resources/Prototypes/NPCs/monkey.yml @@ -0,0 +1,61 @@ +- type: htnCompound + id: MonkeyCompound + branches: + - tasks: + - !type:HTNCompoundTask + task: DisposalsNearbyCompound + - tasks: + - !type:HTNCompoundTask + task: SimpleHostileCompound + +- type: htnCompound + id: DisposalsNearbyCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:PickEntityNearMobOperator + rangeKey: VisionRadius + targetKey: FlushableTarget + nearbyEntityTargetKey: DisposalBinTarget + targetMoveKey: TargetCoordinates + mobState: Critical + mobRangeKey: InteractRange + whitelist: + components: + - DisposalUnit + blacklist: + components: + - MailingUnit + + - !type:HTNPrimitiveTask + operator: !type:MoveToOperator + pathfindInPlanning: false + + - !type:HTNPrimitiveTask + operator: !type:SetFloatOperator + targetKey: IdleTime + amount: 1 + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime + + - !type:HTNPrimitiveTask + operator: !type:DisposalInsertOperator + targetKey: FlushableTarget + disposalTargetKey: DisposalBinTarget + + - !type:HTNPrimitiveTask + operator: !type:SetFloatOperator + targetKey: IdleTime + amount: 1 + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime diff --git a/Resources/Prototypes/NPCs/utility_queries.yml b/Resources/Prototypes/NPCs/utility_queries.yml index bb2bb08cc4..3c1d3fe5f2 100644 --- a/Resources/Prototypes/NPCs/utility_queries.yml +++ b/Resources/Prototypes/NPCs/utility_queries.yml @@ -88,6 +88,8 @@ considerations: - !type:TargetIsAliveCon curve: !type:BoolCurve + - !type:TargetIsVisibleCon + curve: !type:BoolCurve - !type:TargetDistanceCon curve: !type:PresetCurve preset: TargetDistance diff --git a/Resources/Prototypes/Objectives/base_objectives.yml b/Resources/Prototypes/Objectives/base_objectives.yml index 1abbe74f3a..12204153c2 100644 --- a/Resources/Prototypes/Objectives/base_objectives.yml +++ b/Resources/Prototypes/Objectives/base_objectives.yml @@ -119,3 +119,13 @@ id: BaseFreeObjective components: - type: FreeObjective + +# requires the player to perform some action a certain number of times +- type: entity + abstract: true + parent: BaseObjective + id: BaseCounterObjective + description: We're interested in Nanotrasen's correspondence. Letter opener not provided. + components: + - type: CounterCondition + - type: NumberObjective # min and max to be set in the inheritor \ No newline at end of file diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index 131d00ceaf..d548bada5f 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -6,6 +6,8 @@ TraitorObjectiveGroupKill: 1 TraitorObjectiveGroupState: 1 #As in, something about your character. Alive, dead, arrested, gained an ability... TraitorObjectiveGroupSocial: 1 #Involves helping/harming others without killing them or stealing their stuff + TraitorObjectiveGroupSabotage: 1 + TraitorObjectiveGroupOther: 1 TraitorObjectiveGroupNukieDisk: 0.05 #Corvax - type: weightedRandom @@ -29,6 +31,7 @@ id: TraitorObjectiveGroupKill weights: KillRandomPersonObjective: 1 + KillStationAiObjective: 0.25 KillRandomHeadObjective: 0.25 - type: weightedRandom @@ -44,6 +47,19 @@ # RandomTraitorAliveObjective: 1 - Removed because the objective was boring and didn't progress the round. RandomTraitorProgressObjective: 1 +- type: weightedRandom + id: TraitorObjectiveGroupSabotage + weights: + SupercritAnomaliesObjective: 1 + +#misc objectives that don't fall other another category +- type: weightedRandom + id: TraitorObjectiveGroupOther + weights: + HijackTradeStationObjective: 1 + MailFraudObjective: 1 + + #Thief groups - type: weightedRandom id: ThiefObjectiveGroups diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index 6897ff16ed..df207165e0 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -131,6 +131,31 @@ requireDead: true requireMaroon: true +- type: entity + parent: [BaseTraitorObjective, BaseKillObjective] + id: KillStationAiObjective + description: Nanotrasen proudly boasts about their state of the art artificial intelligence. Remind them it's just another toy that can be broken. + components: + - type: Objective + difficulty: 3.0 + # there's probably only one AI + unique: true + icon: + sprite: Mobs/Silicon/station_ai.rsi + state: ai_dead # TODO: Change this to broken when we have a better objectives UI, broken sprite is currently too big! + - type: TargetObjective + title: objective-condition-kill-station-ai + - type: PickRandomPerson + pool: !type:AliveAiPool + filters: + # Can't have multiple objectives to kill the same person. + - !type:TargetObjectiveMindFilter + blacklist: + components: + - KillPersonCondition + - type: KillPersonCondition + requireDead: true + # social - type: entity @@ -354,3 +379,57 @@ # stealGroup: NukeDisk # owner: objective-condition-steal-station # Corvax-MRP-End + +# sabotage +- type: entity + parent: BaseTraitorObjective + id: SupercritAnomaliesObjective + description: Nanotrasen is very interested in anomalies with potentially catastrophic consequences. Introduce them to the fire they're playing with. + components: + - type: Objective + difficulty: 2 + icon: + sprite: Structures/Specific/anomaly.rsi + state: anom1 + - type: SupercriticalAnomaliesCondition + - type: NumberObjective + min: 3 + max: 4 + title: objective-condition-supercrit-anomalies-title + - type: ObjectiveLimit + limit: 1 + +# other +- type: entity + parent: BaseTraitorObjective + id: HijackTradeStationObjective + name: Hijack the Automated Trade Station. + description: Your uplink has been authorized one hijack beacon. Deploy it on the Automated Trade Station and defend it whilst it hijacks the trade station. + components: + - type: Objective + difficulty: 3 + icon: + sprite: Objects/Tools/hijack_beacon.rsi + state: extraction_point + - type: HijackTradeStationCondition + - type: ObjectiveLimit + # There is only one trade station so there should never be more than one of these objectives. + limit: 1 + +- type: entity + parent: [BaseTraitorObjective, BaseCounterObjective] + id: MailFraudObjective + description: We're interested in Nanotrasen's correspondence. Letter opener not provided. + components: + - type: Objective + difficulty: 0.5 + icon: + sprite: Objects/Specific/Cargo/mail_bag.rsi + state: icon + - type: MailFraudCondition + - type: NumberObjective + min: 8 + max: 12 + title: objective-condition-mail-fraud-title + - type: ObjectiveLimit + limit: 1 diff --git a/Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml b/Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml index 661350f6de..8d4632c0f3 100644 --- a/Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml +++ b/Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml @@ -4,7 +4,7 @@ OreIron: 1.0 OreQuartz: 1.0 OreCoal: 0.33 - OreSalt: 0.25 + OreSalt: 0.20 OreGold: 0.25 OreSilver: 0.25 OrePlasma: 0.20 diff --git a/Resources/Prototypes/Procedural/vgroid.yml b/Resources/Prototypes/Procedural/vgroid.yml index 0caa9f0e1f..7cc40dbb8f 100644 --- a/Resources/Prototypes/Procedural/vgroid.yml +++ b/Resources/Prototypes/Procedural/vgroid.yml @@ -50,7 +50,7 @@ - !type:OreDunGen replacement: IronRock entity: IronRockSalt - count: 50 + count: 25 minGroupSize: 8 maxGroupSize: 12 - !type:OreDunGen diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml b/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml index 82d7fd567c..72b9c8ac44 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml @@ -144,15 +144,6 @@ flavor: pineapple color: yellow -- type: reagent - id: JuicePotato - name: reagent-name-juice-potato - parent: BaseJuice - desc: reagent-desc-juice-potato - physicalDesc: reagent-physical-desc-starchy - flavor: potatoes - color: "#302000" - - type: reagent id: JuiceTomato name: reagent-name-juice-tomato diff --git a/Resources/Prototypes/Reagents/botany.yml b/Resources/Prototypes/Reagents/botany.yml index 7c5e39576b..e7a4bbe299 100644 --- a/Resources/Prototypes/Reagents/botany.yml +++ b/Resources/Prototypes/Reagents/botany.yml @@ -89,6 +89,7 @@ amount: -20 - !type:PlantAdjustMutationMod amount: 0.1 + - !type:PlantRemoveKudzu metabolisms: Bloodstream: effects: diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index 5c81296adf..a0f29bba0d 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -1720,9 +1720,8 @@ metabolisms: Bloodstream: effects: - - !type:GenericStatusEffect - key: RadiationProtection - component: RadiationProtection + - !type:ModifyStatusEffect + effectProto: StatusEffectRadiationProtection time: 2 type: Add - !type:HealthChange diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/structures/posters.yml b/Resources/Prototypes/Recipes/Construction/Graphs/structures/posters.yml new file mode 100644 index 0000000000..ce7ca40ab7 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/structures/posters.yml @@ -0,0 +1,26 @@ +- type: constructionGraph + id: PosterGraph + start: start + graph: + - node: start + edges: + - to: tornPoster + steps: + - tool: Brushing + doAfter: 2 + completed: + - !type:PlaySound + sound: + path: /Audio/Effects/poster_broken.ogg + + - node: tornPoster + entity: PosterBroken + edges: + - to: removed + steps: + - tool: Brushing + doAfter: 2 + + - node: removed + actions: + - !type:DestroyEntity {} diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/web.yml b/Resources/Prototypes/Recipes/Construction/Graphs/web.yml index 4eb368f5ab..3ace442516 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/web.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/web.yml @@ -1,3 +1,18 @@ +- type: constructionGraph + id: CobwebsGraph + start: start + graph: + - node: start + edges: + - to: removed + steps: + - tool: Brushing + doAfter: 2 + + - node: removed + actions: + - !type:DestroyEntity {} + - type: constructionGraph id: WebStructures start: start diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml b/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml index 2df2891b7c..f9147efe4f 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml @@ -15,6 +15,7 @@ - FloorTileItemDarkPavement - FloorTileItemDarkPavementVertical - FloorTileItemDarkOffset + - FloorTileItemDarkSquiggly - type: latheRecipePack id: FloorSteelTilesStatic @@ -58,6 +59,18 @@ - FloorTileItemSteelMaint - FloorTileItemTechmaintDark +- type: latheRecipePack + id: FloorIndustrialTilesStatic + recipes: + - FloorTileItemMining + - FloorTileItemMiningDark + - FloorTileItemMiningLight + - FloorTileItemElevatorShaft + - FloorTileItemRockVault + - FloorTileItemMetalDiamond + - FloorTileItemFreezer + - FloorTileItemShowroom + - type: latheRecipePack id: FloorWoodTilesStatic recipes: @@ -93,6 +106,42 @@ recipes: - FloorTileItemWhiteMarble - FloorTileItemDarkMarble + - FloorTileItemUraniumMarble + - FloorTileItemPlasmaMarble + +- type: latheRecipePack + id: FloorShuttleTilesStatic + recipes: + - FloorTileItemShuttleWhite + - FloorTileItemShuttleBlue + - FloorTileItemShuttleOrange + - FloorTileItemShuttlePurple + - FloorTileItemShuttleRed + - FloorTileItemShuttleGrey + - FloorTileItemShuttleBlack + +- type: latheRecipePack + id: PlasticTilesStatic + recipes: + - FloorTileItemLino + - FloorTileItemBoxing + - FloorTileItemGym + +- type: latheRecipePack + id: CarpetTilesStatic + recipes: + - FloorTileItemArcadeBlue + - FloorTileItemArcadeBlue2 + - FloorTileItemArcadeRed + - FloorTileItemEighties + - FloorTileItemCarpetClown + - FloorTileItemCarpetOffice + +- type: latheRecipePack + id: PreciousTilesStatic + recipes: + - FloorTileItemGold + - FloorTileItemSilver ## Dynamic @@ -107,6 +156,7 @@ - FauxTileAstroIce - FauxTileAstroSnow - FauxTileAstroAsteroidSand + - FauxTileAstroAsteroidSandBorderless - FauxTileDesertAstroSand - FauxTileAstroIronsand - FauxTileAstroIronsandBorderless diff --git a/Resources/Prototypes/Recipes/Lathes/categories.yml b/Resources/Prototypes/Recipes/Lathes/categories.yml index fd25394e67..99c1ac30e5 100644 --- a/Resources/Prototypes/Recipes/Lathes/categories.yml +++ b/Resources/Prototypes/Recipes/Lathes/categories.yml @@ -114,6 +114,22 @@ id: Marble name: lathe-category-marble +- type: latheCategory + id: Shuttle + name: lathe-category-shuttle-tile + +- type: latheCategory + id: Plastic + name: lathe-category-plastic-tile + +- type: latheCategory + id: Precious + name: lathe-category-precious-tile + +- type: latheCategory + id: Industrial + name: lathe-category-industrial-tile + # Science - type: latheCategory id: Mech diff --git a/Resources/Prototypes/Recipes/Lathes/tiles.yml b/Resources/Prototypes/Recipes/Lathes/tiles.yml index e30174a6ae..29aa64cac5 100644 --- a/Resources/Prototypes/Recipes/Lathes/tiles.yml +++ b/Resources/Prototypes/Recipes/Lathes/tiles.yml @@ -42,6 +42,16 @@ - Tiles - Maints +- type: latheRecipe + abstract: true + parent: BaseSteelTileRecipe + id: BaseIndustrialTileRecipe + categories: + - Tiles + - Industrial + materials: + Steel: 50 + - type: latheRecipe abstract: true parent: BaseSteelTileRecipe @@ -92,6 +102,46 @@ Steel: 25 Glass: 25 +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BaseShuttleTileRecipe + categories: + - Tiles + - Shuttle + completetime: 1 + materials: + Plasteel: 25 + +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BasePlasticTileRecipe + categories: + - Tiles + - Plastic + materials: + Plastic: 25 + +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BaseCarpetTileRecipe + categories: + - Tiles + - Carpets + materials: + Cloth: 25 + +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BasePreciousTileRecipe + applyMaterialDiscount: false + categories: + - Tiles + - Precious + ## Recipes # Steel tiles @@ -313,6 +363,47 @@ id: FloorTileItemTechmaintDark result: FloorTileItemTechmaintDark +# Industrial +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemMining + result: FloorTileItemMining + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemMiningDark + result: FloorTileItemMiningDark + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemMiningLight + result: FloorTileItemMiningLight + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemElevatorShaft + result: FloorTileItemElevatorShaft + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemRockVault + result: FloorTileItemRockVault + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemMetalDiamond + result: FloorTileItemMetalDiamond + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemShowroom + result: FloorTileItemShowroom + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemFreezer + result: FloorTileItemFreezer + # Circuit - type: latheRecipe parent: BaseCircuitTileRecipe @@ -477,3 +568,119 @@ parent: BaseMarbleTileRecipe id: FloorTileItemDarkMarble result: FloorTileItemDarkMarble + +- type: latheRecipe + parent: BaseMarbleTileRecipe + id: FloorTileItemUraniumMarble + result: FloorTileItemUraniumMarble + materials: + Steel: 25 + Glass: 25 + Uranium: 25 + +- type: latheRecipe + parent: BaseMarbleTileRecipe + id: FloorTileItemPlasmaMarble + result: FloorTileItemPlasmaMarble + materials: + Steel: 25 + Glass: 25 + Plasma: 25 + +# Shuttle +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleWhite + result: FloorTileItemShuttleWhite + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleBlue + result: FloorTileItemShuttleBlue + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleOrange + result: FloorTileItemShuttleOrange + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttlePurple + result: FloorTileItemShuttlePurple + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleRed + result: FloorTileItemShuttleRed + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleGrey + result: FloorTileItemShuttleGrey + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleBlack + result: FloorTileItemShuttleBlack + +# Precious +- type: latheRecipe + parent: BasePreciousTileRecipe + id: FloorTileItemGold + result: FloorTileItemGold + materials: + Gold: 50 + +- type: latheRecipe + parent: BasePreciousTileRecipe + id: FloorTileItemSilver + result: FloorTileItemSilver + materials: + Silver: 50 + +# Carpets +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemArcadeBlue + result: FloorTileItemArcadeBlue + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemArcadeBlue2 + result: FloorTileItemArcadeBlue2 + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemArcadeRed + result: FloorTileItemArcadeRed + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemEighties + result: FloorTileItemEighties + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemCarpetClown + result: FloorTileItemCarpetClown + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemCarpetOffice + result: FloorTileItemCarpetOffice + +# Plastic +- type: latheRecipe + parent: BasePlasticTileRecipe + id: FloorTileItemLino + result: FloorTileItemLino + +- type: latheRecipe + parent: BasePlasticTileRecipe + id: FloorTileItemBoxing + result: FloorTileItemBoxing + +- type: latheRecipe + parent: BasePlasticTileRecipe + id: FloorTileItemGym + result: FloorTileItemGym diff --git a/Resources/Prototypes/Research/civilianservices.yml b/Resources/Prototypes/Research/civilianservices.yml index 736e15474a..deb4248e64 100644 --- a/Resources/Prototypes/Research/civilianservices.yml +++ b/Resources/Prototypes/Research/civilianservices.yml @@ -110,6 +110,7 @@ - FauxTileAstroIce - FauxTileAstroSnow - FauxTileAstroAsteroidSand + - FauxTileAstroAsteroidSandBorderless - FauxTileDesertAstroSand - FauxTileAstroIronsand - FauxTileAstroIronsandBorderless diff --git a/Resources/Prototypes/Roles/Antags/traitor.yml b/Resources/Prototypes/Roles/Antags/traitor.yml index 0cd255b7fd..f274d99fb2 100644 --- a/Resources/Prototypes/Roles/Antags/traitor.yml +++ b/Resources/Prototypes/Roles/Antags/traitor.yml @@ -52,30 +52,53 @@ - type: startingGear id: SyndicateReinforcementMedic - parent: SyndicateOperativeClothing equipment: - pocket1: WeaponPistolViper + jumpsuit: ClothingUniformJumpsuitOperative + shoes: ClothingShoesColorRed + gloves: ClothingHandsGlovesLatex + head: ClothingHeadHatSurgcapPurple + id: VisitorPDA + belt: ClothingBeltMedicalFilled + back: ClothingBackpackDuffelMedical + ears: ClothingHeadsetMedical + eyes: ClothingEyesHudMedical + pocket1: HandheldHealthAnalyzer + pocket2: PillCanisterBicaridine inhand: - MedkitCombatFilled - + storage: + back: + - Defibrillator + - CigPackSyndicate + - UniformScrubsColorPurple + - ChemistryBottleToxin + - WeaponPistolViper + - Syringe + - Syringe + - type: startingGear id: SyndicateReinforcementSpy - parent: SyndicateOperativeClothing equipment: - id: AgentIDCard - mask: ClothingMaskGasVoiceChameleon + jumpsuit: ClothingUniformJumpsuitOperative + back: ClothingBackpack + shoes: ClothingShoesColorBlack + gloves: ClothingHandsGlovesColorBlack pocket1: WeaponPistolViper - + pocket2: VoiceMaskImplanter + inhand: + - ClothingBackpackChameleonFillAgent - type: startingGear id: SyndicateReinforcementThief parent: SyndicateOperativeClothing equipment: pocket1: WeaponPistolViper + pocket2: MagazinePistolHighCapacity inhand: - ToolboxSyndicateFilled storage: back: - SyndicateJawsOfLife + - CameraBug #Syndicate Operative Outfit - Basic - type: startingGear diff --git a/Resources/Prototypes/Shaders/displacement.yml b/Resources/Prototypes/Shaders/displacement.yml index 70c9dce6f7..debb68fb1f 100644 --- a/Resources/Prototypes/Shaders/displacement.yml +++ b/Resources/Prototypes/Shaders/displacement.yml @@ -15,3 +15,10 @@ path: "/Textures/Shaders/displacement.swsl" params: displacementSize: 127 + +- type: shader + id: DisplacedDrawUnshaded + kind: source + path: "/Textures/Shaders/displacement_unshaded.swsl" + params: + displacementSize: 127 diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml index 057abf0ac2..bf740ba1f6 100644 --- a/Resources/Prototypes/Shaders/shaders.yml +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -115,3 +115,8 @@ id: Hologram kind: source path: "/Textures/Shaders/hologram.swsl" + +- type: shader + id: HeatBlur + kind: source + path: "/Textures/Shaders/heatBlur.swsl" diff --git a/Resources/Prototypes/SoundCollections/footsteps.yml b/Resources/Prototypes/SoundCollections/footsteps.yml index 26ecee5269..8c619226ff 100644 --- a/Resources/Prototypes/SoundCollections/footsteps.yml +++ b/Resources/Prototypes/SoundCollections/footsteps.yml @@ -94,12 +94,6 @@ files: - /Audio/Items/Toys/weh.ogg -- type: soundCollection - id: FootstepHeavy - files: - - /Audio/Effects/Footsteps/suitstep1.ogg - - /Audio/Effects/Footsteps/suitstep2.ogg - - type: soundCollection id: FootstepAsteroid files: diff --git a/Resources/Prototypes/SoundCollections/vulpkanin.yml b/Resources/Prototypes/SoundCollections/vulpkanin.yml index de3ea1f6a1..4ad5712ae4 100644 --- a/Resources/Prototypes/SoundCollections/vulpkanin.yml +++ b/Resources/Prototypes/SoundCollections/vulpkanin.yml @@ -8,13 +8,13 @@ - type: soundCollection id: VulpkaninGrowls files: - - /Audio/Voice/Vulpkanin/dog_growl1.ogg - - /Audio/Voice/Vulpkanin/dog_growl2.ogg - - /Audio/Voice/Vulpkanin/dog_growl3.ogg - - /Audio/Voice/Vulpkanin/dog_growl4.ogg - - /Audio/Voice/Vulpkanin/dog_growl5.ogg - - /Audio/Voice/Vulpkanin/dog_growl6.ogg -# Corvax-CorvaxVulp_Port-Start + # Corvax-CorvaxVulp_Port-Start + #- /Audio/Voice/Vulpkanin/dog_growl1.ogg + #- /Audio/Voice/Vulpkanin/dog_growl2.ogg + #- /Audio/Voice/Vulpkanin/dog_growl3.ogg + #- /Audio/Voice/Vulpkanin/dog_growl4.ogg + #- /Audio/Voice/Vulpkanin/dog_growl5.ogg + #- /Audio/Voice/Vulpkanin/dog_growl6.ogg - /Audio/Corvax/Effects/Growl/growl1.ogg - /Audio/Corvax/Effects/Growl/growl2.ogg - /Audio/Corvax/Effects/Growl/growl3.ogg @@ -35,4 +35,5 @@ - type: soundCollection id: VulpkaninHowls files: - - /Audio/Voice/Vulpkanin/howl.ogg + #- /Audio/Voice/Vulpkanin/howl.ogg + - /Audio/Corvax/Effects/howl.ogg # Corvax-CorvaxVulp_Port \ No newline at end of file diff --git a/Resources/Prototypes/Stacks/Tiles/concrete.yml b/Resources/Prototypes/Stacks/Tiles/concrete.yml index 108a7f56e8..a32f03ed8d 100644 --- a/Resources/Prototypes/Stacks/Tiles/concrete.yml +++ b/Resources/Prototypes/Stacks/Tiles/concrete.yml @@ -4,7 +4,7 @@ parent: BaseTileStack id: FloorTileConcrete name: stack-concrete-tile - spawn: FloorTileItemGrayConcrete + spawn: FloorTileItemConcrete - type: stack parent: BaseTileStack diff --git a/Resources/Prototypes/Store/categories.yml b/Resources/Prototypes/Store/categories.yml index ccdb349d9c..a4c339d0e7 100644 --- a/Resources/Prototypes/Store/categories.yml +++ b/Resources/Prototypes/Store/categories.yml @@ -89,6 +89,11 @@ name: store-category-pointless priority: 10 +- type: storeCategory + id: UplinkObjective + name: store-category-objective + priority: 11 + #nukie delivery - type: storeCategory diff --git a/Resources/Prototypes/Store/presets.yml b/Resources/Prototypes/Store/presets.yml index 37329d4096..d4c1245c55 100644 --- a/Resources/Prototypes/Store/presets.yml +++ b/Resources/Prototypes/Store/presets.yml @@ -16,6 +16,7 @@ - UplinkWearables - UplinkJob - UplinkPointless + - UplinkObjective currencyWhitelist: - Telecrystal balance: @@ -50,3 +51,10 @@ - ChangelingAbilities currencyWhitelist: - ChangelingDNA + +- type: entity + parent: StorePresetUplink + id: StorePresetRemoteUplink + categories: [ HideSpawnMenu ] + components: + - type: RingerAccessUplink diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index 299032bf77..de3e93223b 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -86,7 +86,7 @@ specials: - !type:ApplyStatusEffectSpecial statusEffects: - - PainNumbnessTraitStatusEffect + - TraitStatusEffectPainNumbness - type: trait id: Hemophilia @@ -96,7 +96,7 @@ specials: - !type:ApplyStatusEffectSpecial statusEffects: - - StatusEffectHemophiliaTrait + - TraitStatusEffectHemophilia - type: trait id: ImpairedMobility diff --git a/Resources/Prototypes/World/Biomes/basic.yml b/Resources/Prototypes/World/Biomes/basic.yml deleted file mode 100644 index 5ecd85bbc4..0000000000 --- a/Resources/Prototypes/World/Biomes/basic.yml +++ /dev/null @@ -1,26 +0,0 @@ -- type: spaceBiome - id: AsteroidsStandard - priority: 0 # This probably shouldn't get selected. - noiseRanges: {} - chunkComponents: - - type: DebrisFeaturePlacerController - densityNoiseChannel: Density - - type: SimpleDebrisSelector - debrisTable: - - id: AsteroidDebrisSmall - - id: AsteroidDebrisMedium - - id: AsteroidDebrisLarge - prob: 0.7 - - id: AsteroidDebrisLarger - prob: 0.4 - - type: NoiseDrivenDebrisSelector - noiseChannel: Wreck - debrisTable: - - id: ScrapDebrisSmall - - id: ScrapDebrisMedium - - id: ScrapDebrisLarge - prob: 0.5 - - type: NoiseRangeCarver - ranges: - - 0.4, 0.6 - noiseChannel: Carver diff --git a/Resources/Prototypes/World/Biomes/failsafes.yml b/Resources/Prototypes/World/Biomes/failsafes.yml deleted file mode 100644 index 5e3c50b44c..0000000000 --- a/Resources/Prototypes/World/Biomes/failsafes.yml +++ /dev/null @@ -1,21 +0,0 @@ -- type: spaceBiome - id: Failsafe - priority: -999999 # This DEFINITELY shouldn't get selected! - noiseRanges: {} - -- type: spaceBiome - id: AsteroidsFallback - priority: -999998 # This probably shouldn't get selected. - noiseRanges: {} - chunkComponents: - - type: DebrisFeaturePlacerController - densityNoiseChannel: Density - - type: SimpleDebrisSelector - debrisTable: - - id: AsteroidDebrisSmall - - id: AsteroidDebrisMedium - - id: AsteroidDebrisLarge - prob: 0.7 - - id: AsteroidDebrisLarger - prob: 0.4 - diff --git a/Resources/Prototypes/World/noise_channels.yml b/Resources/Prototypes/World/noise_channels.yml deleted file mode 100644 index 668b338dd3..0000000000 --- a/Resources/Prototypes/World/noise_channels.yml +++ /dev/null @@ -1,44 +0,0 @@ -- type: noiseChannel - id: Density - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - clippingRanges: - - 0.4, 0.6 - clippedValue: 1.658 # magic number for chunk size. - inputMultiplier: 6 # Makes density hopefully low noise in the local area while still being interesting at scale. - outputMultiplier: 50.0 # We scale density up significantly for more human-friendly numbers. - minimum: 45.0 - -- type: noiseChannel - id: DensityUnclipped - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 # Makes density hopefully low noise in the local area while still being interesting at scale. - outputMultiplier: 50.0 # We scale density up significantly for more human-friendly numbers. - minimum: 45.0 - -- type: noiseChannel - id: Carver - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 - -- type: noiseChannel - id: Wreck - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - clippingRanges: - - 0.0, 0.4 - clippedValue: 0 - remapTo0Through1: true - inputMultiplier: 16 # Makes wreck concentration very low noise at scale. - -- type: noiseChannel - id: Temperature - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 # Makes wreck concentration very low noise at scale. diff --git a/Resources/Prototypes/World/worldgen_default.yml b/Resources/Prototypes/World/worldgen_default.yml deleted file mode 100644 index af52c30cf1..0000000000 --- a/Resources/Prototypes/World/worldgen_default.yml +++ /dev/null @@ -1,9 +0,0 @@ -- type: worldgenConfig - id: Default - components: - - type: WorldController - - type: BiomeSelection - biomes: - - AsteroidsFallback - - Failsafe - - AsteroidsStandard diff --git a/Resources/Prototypes/lobbyscreens.yml b/Resources/Prototypes/lobbyscreens.yml index d0f6da99d5..94fc8bd3ba 100644 --- a/Resources/Prototypes/lobbyscreens.yml +++ b/Resources/Prototypes/lobbyscreens.yml @@ -4,6 +4,12 @@ title: lobby-state-background-warden-title artist: lobby-state-background-warden-artist +- type: lobbyBackground + id: InvisibleWall + background: /Textures/LobbyScreens/invisiblewall.webp + title: lobby-state-background-invisiblewall-title + artist: lobby-state-background-invisiblewall-artist + - type: lobbyBackground id: Pharmacy background: /Textures/LobbyScreens/pharmacy.webp diff --git a/Resources/Prototypes/status_effects.yml b/Resources/Prototypes/status_effects.yml index a48758716c..c40b26fe85 100644 --- a/Resources/Prototypes/status_effects.yml +++ b/Resources/Prototypes/status_effects.yml @@ -53,9 +53,6 @@ - type: statusEffect id: Flashed -- type: statusEffect - id: RadiationProtection - - type: statusEffect id: Adrenaline alert: Adrenaline diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index c2dc4c028f..5c7c8c1ca6 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -318,6 +318,9 @@ - type: Tag id: Cleaver # Storage whitelist: ClothingBeltChef. ItemMapper: ClothingBeltChef +- type: Tag + id: CleanerGrenade # Storage whitelist: ClothingBeltJanitor. ItemMapper: ClothingBeltJanitor + - type: Tag id: ClothMade # SpecialDigestible: OrganMothStomach. Storage whitelist: FoodBoxCloth @@ -863,6 +866,9 @@ - type: Tag id: LightBulb # Storage whitelist: BoxLightbulb. ConstructionGraph: HelmetJustice +- type: Tag + id: LightReplacer # Storage whitelist: ClothingBeltJanitor. ItemMapper: ClothingBeltJanitor + - type: Tag id: Lime # CargoBounty: BountyLime @@ -1454,6 +1460,9 @@ - type: Tag id: TrashBag # Storage whitelist: BoxTrashbag, ClothingBeltJanitor, JanitorialTrolley. ItemMapper: JanitorialTrolley +- type: Tag + id: TrashBagBlue # Storage whitelist: BoxTrashbag, ClothingBeltJanitor, JanitorialTrolley. ItemMapper: JanitorialTrolley + - type: Tag id: Truncheon # Storage whitelist: ClothingBeltSecurity diff --git a/Resources/Prototypes/wizardsDenWhitelists.yml b/Resources/Prototypes/wizardsDenWhitelists.yml index 8b8420622a..244244a3c1 100644 --- a/Resources/Prototypes/wizardsDenWhitelists.yml +++ b/Resources/Prototypes/wizardsDenWhitelists.yml @@ -1,4 +1,4 @@ -# This is the whitelist used for Wizard's Den Salamander +# This is the whitelist used for Wizard's Den Salamander - type: playerConnectionWhitelist id: salamanderMrpWhitelist @@ -19,21 +19,21 @@ range: 90 # 90 Days action: Deny includeSecret: false -# - !type:ConditionNotesPlaytimeRange # Deny for >=3 low severity notes in the last 14 days -# includeExpired: false -# minimumSeverity: 1 # Low -# minimumNotes: 3 -# range: 14 # 14 Days -# action: Deny -# includeSecret: false + - !type:ConditionNotesPlaytimeRange # Deny for >=3 low severity notes in the last 14 days + includeExpired: false + minimumSeverity: 1 # Low + minimumNotes: 3 + range: 30 # 30 Days + action: Deny + includeSecret: false - !type:ConditionManualWhitelistMembership # Allow whitelisted players action: Allow - !type:ConditionPlayerCount # Allow when <= 15 players are online minimumPlayers: 0 maximumPlayers: 15 action: Allow - #- !type:ConditionPlaytime - # minimumPlaytime: 1200 # 20 hours to be whitelisted - # action: Deny + - !type:ConditionPlaytime + minimumPlaytime: 1200 # 20 hours to be whitelisted + action: Allow - !type:ConditionAlwaysMatch action: Deny diff --git a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter31.xml b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter31.xml index 179d0622e3..f8efb31941 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter31.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter31.xml @@ -24,7 +24,7 @@ -## 315 - Разбой +## 315 - Хищение особо ценного имущества diff --git a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter32.xml b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter32.xml index fb2b548321..b4cd74da64 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter32.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter32.xml @@ -24,11 +24,6 @@ -## 325 - Хищение особо ценного имущества - - - - ## 326 - Уничтожение особо ценного имущества diff --git a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter42.xml b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter42.xml index dd88b6eb6f..ec73acd82c 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter42.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter42.xml @@ -19,19 +19,9 @@ -## 424 - Проникновение в защищенную стратегическую точку - - - - -## 425 - Незаконная эвакуация с территории комплекса +## 425 - Проникновение на закрытую корпоративную территорию -## 426 - Проникновение на территорию объекта NanoTrasen - - - - diff --git a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/CrimeList.xml b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/CrimeList.xml index 43e151212d..2fb185c9f8 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/CrimeList.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/CrimeList.xml @@ -373,7 +373,7 @@ 315 - [textlink="Разбой" link="CLChapter31"] + [textlink="Хищение особо ценного имущества" link="CLChapter32"] @@ -415,13 +415,11 @@ - 325 - [textlink="Хищение особо ценного имущества" link="CLChapter32"] - 326 + 326 [textlink="Уничтожение особо ценного имущества" link="CLChapter32"] @@ -496,20 +494,16 @@ - 424 - [textlink="Проникновение в защищенную стратегическую точку" link="CLChapter42"] 425 - [textlink="Незаконная эвакуация с территории комплекса" link="CLChapter42"] + [textlink="Проникновение на закрытую корпоративную территорию" link="CLChapter42"] - 426 - [textlink="Проникновение на территорию объекта NanoTrasen" link="CLChapter42"] diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml new file mode 100644 index 0000000000..19d5ad7698 --- /dev/null +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml @@ -0,0 +1,138 @@ + + + + + + + + # Общие СРП + ### Оперативник ОБР обязан + + + ### Оперативник ОБР имеет право + + + ### Оперативнику ОБР запрещено + + + # Должностные СРП + ## СРП Командира РХБЗЗ + ### Командир РХБЗЗ обязан + + + ### Командир РХБЗЗ имеет право + + + ### Командиру РХБЗЗ запрещено + + + ## СРП Агента РХБЗЗ + ### Агент РХБЗЗ обязан + + + ### Агент РХБЗЗ имеет право + + + ### Агенту РХБЗЗ запрещено + + + ## СРП Лидера ОБР + ### Лидер ОБР обязан + + + ### Лидер ОБР имеет право + + + ### Лидеру ОБР запрещено + + + ## СРП Cвященника ОБР + ### Cвященник ОБР обязан + + + ### Cвященник ОБР имеет право + + + ### Cвященнику ОБР запрещено + + + ## СРП Инженера ОБР + ### Инженер ОБР обязан + + + ### Инженер ОБР имеет право + + + ### Инженеру ОБР запрещено + + + ## СРП Медика ОБР + ### Медик ОБР обязан + + + ### Медик ОБР право + + + ### Медику ОБР запрещено + + + ## СРП Офицера Безопасности ОБР + ### Офицер Безопасности ОБР обязан + + + ### Офицер Безопасности ОБР имеет право + + + ### Офицеру Безопасности ОБР запрещено + + + ## СРП Уборщика ОБР + ### Уборщик ОБР обязан + + + ### Уборщик ОБР имеет право + + + ### Уборщику ОБР запрещено + + + ## СРП Лидера Эскадрона Смерти — Foxtrot + ### Лидер Эскадрона Смерти обязан + + + ### Лидер Эскадрона Смерти имеет право + + + ### Лидеру Эскадрона Смерти запрещено + + + ## СРП Оперативника Эскадрона Смерти — Foxtrot + ### Оперативник Эскадрона Смерти обязан + + + ### Оперативник Эскадрона Смерти имеет право + + + ### Оперативнику Эскадрона Смерти запрещено + + + ## СРП Лидера Эскадрона Смерти — Tango + ### Лидер Эскадрона Смерти обязан + + + ### Лидер Эскадрона Смерти имеет право + + + ### Лидеру Эскадрона Смерти запрещено + + + ## СРП Оперативника Эскадрона Смерти — Tango + ### Оперативник Эскадрона Смерти обязан + + + ### Оперативник Эскадрона Смерти имеет право + + + ### Оперативнику Эскадрона Смерти запрещено + + diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml index bee73ba66d..58915ea46f 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml @@ -1,4 +1,12 @@ + # Общие СРП + ### Сотрудник инженерного отдела обязан + + ### Сотрудник инженерного отдела имеет право + + ### Сотруднику инженерного отдела запрещено + + # Должностные СРП ## СРП Старшего инженера ### Старший инженер обязан @@ -10,6 +18,16 @@ ### Старшему инженеру запрещено + ## СРП Ведущего инженера + ### Ведущий инженер обязан + + + ### Ведущий инженер имеет право + + + ### Ведущему инженеру запрещено + + ## СРП Инженера ### Инженер обязан diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml index af4a9492a7..044ec54d7c 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml @@ -10,6 +10,16 @@ ### Магистрату запрещено + ## СРП Адвоката + ### Адвокат обязан + + + ### Адвокат имеет право + + + ### Адвокату запрещено + + ## СРП Агента внутренних дел ### Агент внутренних дел обязан @@ -24,19 +34,21 @@ ## Процедура внутреннего расследования - ## Процедура обращения к магистрату - - - ## Процедура суда + ## Процедура корпоративного суда ### Общие положения - ### Нормы поведения в суде - + ### Общие нормы суда + - ### Открытый суд - + ### Обязанности сторон + + + ## Судебное разбирательство + ### Судебное разбирательство + + + ### Вынесение вердикта + - ### Закрытый суд - diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml index 55979b5966..ee4d5c2632 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml @@ -10,16 +10,6 @@ ### Главе персонала запрещено - ## СРП Адвоката - ### Адвокат обязан - - - ### Адвокат имеет право - - - ### Адвокату запрещено - - ## СРП Радиоведущего ### Радиоведущий обязан @@ -37,9 +27,6 @@ ### Сервисный работник имеет право - ### Сервисному работнику запрещено - - ## СРП Клоуна ### Клоун обязан diff --git a/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml b/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml index a5fadbaa84..7e7afb4285 100644 --- a/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml +++ b/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml @@ -98,7 +98,7 @@ Frezon is very complicated to create, requiring a mix of tritium, oxygen, and nitrogen, all tuned precisely to create the gas. If you're looking to create this gas yourself, here's what you need to know: - Frezon is produced by the combination of oxygen, tritium, and nitrogen. - - Tritium and oxygen are mixed at a ratio of around 1:8. + - Tritium and oxygen are mixed at a ratio of around 1:50. - Nitrogen is required as a catalyst for the reaction. The amount of nitrogen consumed is dependent on the efficiency of the reaction. - The frezon reaction only occurs at cryogenic temperatures, below 73.15 K. The efficency of the reaction (how much frezon is produced) is dependent on the temperature. The closer to 73.15 K, the more frezon is produced, and the less nitrogen is consumed. diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml index 97880466d4..6816570d93 100644 --- a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml @@ -6,7 +6,7 @@ - Usernames, objects, random characters, very "low effort" names, "meta" names, or otherwise implausible names cannot be used as names. See examples below. - Admin rulings on IC names are final and disputes should be done through the forums, not by refusing to comply with an admin - Clowns and mimes are exempt from the prohibition on titles/honorifics, and have loosened restrictions on low effort and implausible names. + Jobs that have a custom name option in the loadout (e.g. clowns, mimes, cyborgs, etc) are exempt from the prohibition on titles/honorifics, and have loosened restrictions on low effort and implausible names. ## Clarification on "Meta" Names Meta names are ones which attempt to take advantage of some game mechanic or game design choice. "Urist McHands" is a meta name because it is the default name used for admin spawned humans. "Operator Whiskey" is a meta name because it follows the naming pattern of nuclear operatives. This rule is not intended to prevent things like nuclear operatives using a fake ID with names that appear to be nuclear operative names if they decide that they want to do that. diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/cleaner_grenade.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/cleaner_grenade.png new file mode 100644 index 0000000000..4108cb11fd Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/cleaner_grenade.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/golden_plunger.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/golden_plunger.png new file mode 100644 index 0000000000..0642b3c856 Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/golden_plunger.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/holosign.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/holosign.png new file mode 100644 index 0000000000..0bd1b696e1 Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/holosign.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/light_replacer.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/light_replacer.png new file mode 100644 index 0000000000..c27f0dffdd Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/light_replacer.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/meta.json b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/meta.json index 3f3418b36c..d2e6edca2c 100644 --- a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/meta.json +++ b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/dc89ef0239830774bd3d9d7d6c8da2856da2b869", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/dc89ef0239830774bd3d9d7d6c8da2856da2b869, wetfloorsign, wire_brush, plunger, golden_plunger and cleaner_grenade by AndrewFenriz (github)", "size": { "x": 32, "y": 32 @@ -16,6 +16,9 @@ { "name": "bottle_spray" }, + { + "name": "cleaner_grenade" + }, { "name": "cleaver" }, @@ -32,16 +35,16 @@ "name": "crowbar_red" }, { - "name": "cutters_brass" + "name": "cutters_blade" }, { - "name": "cutters_red" + "name": "cutters_brass" }, { "name": "cutters_handle" }, { - "name": "cutters_blade" + "name": "cutters_red" }, { "name": "cutters_yellow" @@ -55,12 +58,18 @@ { "name": "flashbang" }, + { + "name": "golden_plunger" + }, { "name": "hatchet" }, { "name": "hoe" }, + { + "name": "holosign" + }, { "name": "hotsauce" }, @@ -79,6 +88,9 @@ { "name": "kitchenknife" }, + { + "name": "light_replacer" + }, { "name": "multitool" }, @@ -91,6 +103,9 @@ { "name": "plantbgone" }, + { + "name": "plunger" + }, { "name": "rollingpin" }, @@ -113,7 +128,7 @@ "name": "secateurs" }, { - "name": "spray_med" + "name": "soap" }, { "name": "spray_brute" @@ -121,6 +136,9 @@ { "name": "spray_burn" }, + { + "name": "spray_med" + }, { "name": "spray_synth" }, @@ -130,9 +148,21 @@ { "name": "tear_gas_grenade" }, + { + "name": "trashbag" + }, + { + "name": "trashbag_blue" + }, + { + "name": "wetfloorsign" + }, { "name": "wrench" }, + { + "name": "wire_brush" + }, { "name": "wrench_brass" }, diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/plunger.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/plunger.png new file mode 100644 index 0000000000..09b5da46c9 Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/plunger.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/soap.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/soap.png new file mode 100644 index 0000000000..b25916579b Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/soap.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/trashbag.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/trashbag.png new file mode 100644 index 0000000000..6cd86793c2 Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/trashbag.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/trashbag_blue.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/trashbag_blue.png new file mode 100644 index 0000000000..0a191ee292 Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/trashbag_blue.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/wetfloorsign.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/wetfloorsign.png new file mode 100644 index 0000000000..ed4e492f8d Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/wetfloorsign.png differ diff --git a/Resources/Textures/Clothing/Belt/belt_overlay.rsi/wire_brush.png b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/wire_brush.png new file mode 100644 index 0000000000..3e435fc304 Binary files /dev/null and b/Resources/Textures/Clothing/Belt/belt_overlay.rsi/wire_brush.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/base.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/base.rsi/icon.png index 7fca8dc1d3..abfb0746e7 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/base.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/base.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/base.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/base.rsi/meta.json index b612633d44..f0616e50f3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/base.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/base.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/icon.png index 68f73226fd..23d6a1ede1 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/meta.json index b612633d44..b9e6f63cf3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/icon.png index ce11edf236..63cca39c10 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/meta.json index c3a3dcd231..b9e6f63cf3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Sprited by PuroSlavKing and some resprited by SonicHDC (github) for SS14", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/icon.png index b99dad7b10..bbb6c68005 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/icon_alt.png index 1e23bc3736..0ff64526d5 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/meta.json index f13d472e81..168f7c29d3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi, alt icon modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon.png index 9184e71509..71cd9e9624 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon_alt.png index 17cbe45cbe..f126b02458 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/meta.json index f13d472e81..cb808c9473 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/command.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/command.rsi/icon.png index ee020ded8c..0b1a3fe2ea 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/command.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/command.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/command.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/command.rsi/icon_alt.png index 9b67d0e881..5240eaed45 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/command.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/command.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/command.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/command.rsi/meta.json index f13d472e81..4023318c9b 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/command.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/command.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/icon.png index a18ba86f4b..3d096343b1 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/icon_alt.png index 95c92f59c9..2e35f7782a 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/meta.json index f13d472e81..168f7c29d3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/engineering.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi, alt icon modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/icon.png index 19c0c0c357..5f3460b6a3 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/icon_alt.png index cce4bdcef5..e94f49c323 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/meta.json index f13d472e81..cb808c9473 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon.png index 7929174053..9dec6a4aeb 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon_alt.png index 53a11ba7e1..044825fd5e 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/meta.json index f13d472e81..168f7c29d3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi, alt icon modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/icon.png index e98878e7de..2d44b18919 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/meta.json index 81f97d5702..57ed954385 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/mining.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/mining.rsi/icon.png index 74a3ff47c0..fb88afb422 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/mining.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/mining.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/mining.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/mining.rsi/meta.json index b612633d44..f0616e50f3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/mining.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/mining.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/icon.png index ca2cdf0d65..e71bf06b83 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/meta.json index 5264ee019b..b9e6f63cf3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/meta.json @@ -1,27 +1,18 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428 and recolored by Alwayswannahunt(github)", - "size": { - "x": 32, - "y": 32 + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" }, - "states": [ - { - "name": "icon", - "delays": [ - [ - 0.5, - 0.35, - 0.3, - 0.3, - 0.35 - ] - ] - }, - { - "name": "equipped-EARS", - "directions": 4 - } - ] + { + "name": "equipped-EARS", + "directions": 4 + } + ] } diff --git a/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/icon.png index eade7edf28..702f284271 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/meta.json index b612633d44..f0616e50f3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/science.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/science.rsi/icon.png index 4df42d6f1d..4515e6f303 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/science.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/science.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/science.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/science.rsi/icon_alt.png index 410b18ba09..476c8bea62 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/science.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/science.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/science.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/science.rsi/meta.json index f13d472e81..168f7c29d3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/science.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/science.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi, alt icon modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon.png index cc301f1a04..27d66e602d 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon_alt.png index 789530863d..74ff187c52 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/security.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/security.rsi/meta.json index f13d472e81..cb808c9473 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/security.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/security.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/service.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/service.rsi/icon.png index 26040fe865..e6a73b100c 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/service.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/service.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/service.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/service.rsi/meta.json index b612633d44..f0616e50f3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/service.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/service.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/icon.png index b010229b03..0e9f881d00 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/meta.json index a9efa2c815..b5c0cc4804 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/syndicate.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/syndicate.rsi/icon_alt.png index a1af823e1c..8ea18b6370 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/syndicate.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/syndicate.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/syndicate.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/syndicate.rsi/meta.json index 56b6260f35..5829a34f2e 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/syndicate.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/syndicate.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon.png index e13a2bf15c..93bf29acb7 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon.png and b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon_alt.png b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon_alt.png index 962130247f..2421c4ad3f 100644 Binary files a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon_alt.png and b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon_alt.png differ diff --git a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/meta.json index d5510a5d8b..cb808c9473 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/meta.json @@ -1,25 +1,25 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Sprite recoloured from Freelance Headsets by Entvari (Github). Sprite modified by PursuitInAshes (Github) for SS14, original sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428, resprited by @mishutka09", - "size": { - "x": 32, - "y": 32 + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" }, - "states": [ - { - "name": "icon" - }, - { - "name": "icon_alt" - }, - { - "name": "equipped-EARS", - "directions": 4 - }, - { - "name": "alt-equipped-EARS", - "directions": 4 - } - ] + { + "name": "icon_alt" + }, + { + "name": "equipped-EARS", + "directions": 4 + }, + { + "name": "alt-equipped-EARS", + "directions": 4 + } + ] } diff --git a/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/equipped-HELMET.png new file mode 100644 index 0000000000..5647e82437 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/equipped-HELMET.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/icon.png b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/icon.png new file mode 100644 index 0000000000..abf8dbbcdf Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/inhand-left.png new file mode 100644 index 0000000000..1329de8c11 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/inhand-right.png b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/inhand-right.png new file mode 100644 index 0000000000..afb2afbac9 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/meta.json new file mode 100644 index 0000000000..8084ccf467 --- /dev/null +++ b/Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from /tg/station 13 at commit https://github.com/tgstation/tgstation/commit/3dbc8c3c121e3ade9158303c2819b47206b0f93d | inhand-left and inhand-right by K-Dynamic (github) | icon modified by TiniestShark (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/inhand-left.png index dabd7cb3d3..bac0bd75d9 100644 Binary files a/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/inhand-left.png and b/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/inhand-right.png b/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/inhand-right.png index af141d7eec..cd3e95bb9a 100644 Binary files a/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/inhand-right.png and b/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/meta.json index 4e1c6c10da..1d42deb94c 100644 --- a/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/meta.json +++ b/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. In-hand by ThatGuyUSA (github).", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/equipped-HELMET.png index f457d22f2f..ca99276fce 100644 Binary files a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/equipped-HELMET.png and b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/equipped-HELMET.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/inhand-left.png index 759e6b3ed9..27ff39da48 100644 Binary files a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/inhand-left.png and b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/inhand-right.png b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/inhand-right.png index dfbf1399ce..5cee1be27c 100644 Binary files a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/inhand-right.png and b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/meta.json index 4e1c6c10da..1d42deb94c 100644 --- a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/meta.json +++ b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. In-hand by ThatGuyUSA (github).", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/equipped-HELMET.png index 51d09dc18c..3a2095c227 100644 Binary files a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/equipped-HELMET.png and b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/equipped-HELMET.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/inhand-left.png index 4a3bb5d4b8..8c45376672 100644 Binary files a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/inhand-left.png and b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/inhand-right.png b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/inhand-right.png index 4f669b4bb7..d4a9e495ff 100644 Binary files a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/inhand-right.png and b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/meta.json index cb9b30f249..7566e714e2 100644 --- a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/meta.json +++ b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. equipped-HELMET-dog modified from equipped-HELMET by Sparlight (GitHub).", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. equipped-HELMET-dog modified from equipped-HELMET by Sparlight (GitHub). In-hand by ThatGuyUSA (github).", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/inhand-left.png index d3c0f91adf..d01869b45f 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/inhand-left.png and b/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/meta.json index bacde82361..4e665a18e8 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/meta.json @@ -1,26 +1,26 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from Yogstation at https://github.com/yogstation13/Yogstation/commit/18ab2203bc47b7590f2c72b5f7969eafa723f033", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from /tg/station 13 at commit https://github.com/tgstation/tgstation/commit/3dbc8c3c121e3ade9158303c2819b47206b0f93d. inhand-left modified by K-Dynamic (github).", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-OUTERCLOTHING", - "directions": 4 - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] -} \ No newline at end of file + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-OUTERCLOTHING", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-left.png index eec3320f0f..991445e582 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-left.png and b/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-right.png index e3340fa623..5669e56e45 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-right.png and b/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/wizard.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Misc/wizard.rsi/inhand-left.png index 7b7a9696dc..142d5e7744 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Misc/wizard.rsi/inhand-left.png and b/Resources/Textures/Clothing/OuterClothing/Misc/wizard.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/wizard.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Misc/wizard.rsi/inhand-right.png index e2542d772c..84f70a58a6 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Misc/wizard.rsi/inhand-right.png and b/Resources/Textures/Clothing/OuterClothing/Misc/wizard.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING.png index 350f6a288b..1ca03449f8 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING.png and b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/icon.png b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/icon.png index 65c104d1c7..7fadd0c88b 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/icon.png and b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/inhand-left.png index 520d5c6340..0008322748 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/inhand-left.png and b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/inhand-right.png index e2842ac274..8dd5db1669 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/inhand-right.png and b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/meta.json index 84009efde3..1797234d0d 100644 --- a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/meta.json @@ -14,10 +14,6 @@ "name": "equipped-OUTERCLOTHING", "directions": 4 }, - { - "name": "equipped-OUTERCLOTHING-vox", - "directions": 4 - }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/equipped-OUTERCLOTHING.png index b9273f213f..4f08ace712 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/equipped-OUTERCLOTHING.png and b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/equipped-OUTERCLOTHING.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/icon.png b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/icon.png index aac777b0f9..61811311f7 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/icon.png and b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/inhand-left.png index 8ecff94859..0aca74f6b6 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/inhand-left.png and b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/inhand-right.png index 3abdad0857..0ecd48a486 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/inhand-right.png and b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/equipped-OUTERCLOTHING-vox.png b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/equipped-OUTERCLOTHING-vox.png deleted file mode 100644 index 17d75d29b3..0000000000 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/equipped-OUTERCLOTHING-vox.png and /dev/null differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/equipped-OUTERCLOTHING.png index a6947e2278..4b45304637 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/equipped-OUTERCLOTHING.png and b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/equipped-OUTERCLOTHING.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/icon.png b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/icon.png index c208aba1f8..8edafea2ee 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/icon.png and b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/inhand-left.png index bb043734cd..910c02b87f 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/inhand-left.png and b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/inhand-right.png index 36c7711a7d..7b3ddbcc1f 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/inhand-right.png and b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/meta.json index 0e30ab4d3f..789f9d124f 100644 --- a/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/meta.json @@ -14,10 +14,6 @@ "name": "equipped-OUTERCLOTHING", "directions": 4 }, - { - "name": "equipped-OUTERCLOTHING-vox", - "directions": 4 - }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/equipped-FEET-vox.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/equipped-FEET-vox.png index 4d0f8d2ea2..0b3d47be05 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/equipped-FEET-vox.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/equipped-FEET-vox.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/equipped-FEET.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/equipped-FEET.png index c4006135e3..41e9e6d063 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/equipped-FEET.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/equipped-FEET.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/icon-on.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/icon-on.png index b058f6004d..8ec188ea16 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/icon-on.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/icon-on.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/icon.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/icon.png index e0bfcc67a0..31c0b16dac 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/icon.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/inhand-left.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/inhand-left.png index 2b35b6a700..4401465b08 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/inhand-left.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/inhand-right.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/inhand-right.png index d54a3b6f3d..52c71d0b6a 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/inhand-right.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/meta.json b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/meta.json index 6e4a57f3c0..70d8a4d43d 100644 --- a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/meta.json +++ b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/debf90acfcafa4fb8d6723a37e0b8ac556c0702b. equipped-FEET-vox & on-equipped-FEET-vox state taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/31d6576ba8102135d058ef49c3cb6ecbe8db8a79/icons/mob/species/vox/shoes.dmi, and equipped-FEET-vox state modified to fix inconsistencies. ERT recolor by davyei for SS14.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/debf90acfcafa4fb8d6723a37e0b8ac556c0702b. equipped-FEET-vox & on-equipped-FEET-vox state taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/31d6576ba8102135d058ef49c3cb6ecbe8db8a79/icons/mob/species/vox/shoes.dmi, and equipped-FEET-vox state modified to fix inconsistencies. ERT recolor by davyei for SS14, resprited by @mishutka09(discord:1152277579206774854)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET-vox.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET-vox.png index d7d694fb92..dcabc77e8a 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET-vox.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET-vox.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET.png index 1ae542d086..9516b128d9 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-inhand-left.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-inhand-left.png index a5d0ba43f9..20f60ab603 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-inhand-left.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-inhand-left.png differ diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-inhand-right.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-inhand-right.png index 8c1b0024bf..98118a87a4 100644 Binary files a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-inhand-right.png and b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-inhand-right.png differ diff --git a/Resources/Textures/Effects/HeatBlur/perlin_noise.png b/Resources/Textures/Effects/HeatBlur/perlin_noise.png new file mode 100644 index 0000000000..f1422d52a8 Binary files /dev/null and b/Resources/Textures/Effects/HeatBlur/perlin_noise.png differ diff --git a/Resources/Textures/Effects/HeatBlur/soft_circle.png b/Resources/Textures/Effects/HeatBlur/soft_circle.png new file mode 100644 index 0000000000..f3ef97531c Binary files /dev/null and b/Resources/Textures/Effects/HeatBlur/soft_circle.png differ diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json index cbfcdf45d0..073e279f0c 100644 --- a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json @@ -134,6 +134,9 @@ { "name":"xenoborg-basic-module" }, + { + "name":"xenoborg-jump-module" + }, { "name":"xenoborg-camera-computer" }, @@ -179,6 +182,9 @@ { "name":"xenoborg-sword2-module" }, + { + "name":"xenoborg-tile-module" + }, { "name":"xenoborg-tool-module" }, diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-jump-module.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-jump-module.png new file mode 100644 index 0000000000..bb09b413da Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-jump-module.png differ diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-tile-module.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-tile-module.png new file mode 100644 index 0000000000..75ac1819cc Binary files /dev/null and b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-tile-module.png differ diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing.png b/Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing.png new file mode 100644 index 0000000000..7b593f3449 Binary files /dev/null and b/Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing.png differ diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing_alt.png b/Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing_alt.png new file mode 100644 index 0000000000..45eb0e3854 Binary files /dev/null and b/Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing_alt.png differ diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json b/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json new file mode 100644 index 0000000000..a9c507fe65 --- /dev/null +++ b/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark.", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "flesh_clothing" + }, + { + "name": "flesh_clothing_alt" + } + ] +} diff --git a/Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-off.png b/Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-off.png new file mode 100644 index 0000000000..47991017ab Binary files /dev/null and b/Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-off.png differ diff --git a/Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-on.png b/Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-on.png new file mode 100644 index 0000000000..fffbd61b5c Binary files /dev/null and b/Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-on.png differ diff --git a/Resources/Textures/Interface/Alerts/changeling.rsi/meta.json b/Resources/Textures/Interface/Alerts/changeling.rsi/meta.json new file mode 100644 index 0000000000..78137bbfcc --- /dev/null +++ b/Resources/Textures/Interface/Alerts/changeling.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit).", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "flesh-clothing-off" + }, + { + "name": "flesh-clothing-on" + } + ] +} diff --git a/Resources/Textures/LobbyScreens/attributions.yml b/Resources/Textures/LobbyScreens/attributions.yml index 1df5da9a2b..dead0a28fe 100644 --- a/Resources/Textures/LobbyScreens/attributions.yml +++ b/Resources/Textures/LobbyScreens/attributions.yml @@ -48,6 +48,11 @@ copyright: "plantyfern on discord" source: "https://github.com/space-wizards/space-station-14" +- files: ["invisiblewall.webp"] + license: "CC-BY-SA-3.0" + copyright: "vanderslootassgiraffe on discord" + source: "https://github.com/space-wizards/space-station-14" + - files: ["janishootout.webp"] license: "CC0-1.0" copyright: "psychpsyo on Github/Twitter" @@ -56,4 +61,4 @@ - files: ["reclaimer-nuke.webp"] license: "CC-BY-NC-SA-3.0" copyright: "GetOutMarutak,aka.Snicket on Discord/github/Cara/Ko-fi" - source: "https://github.com/space-wizards/space-station-14" \ No newline at end of file + source: "https://github.com/space-wizards/space-station-14" diff --git a/Resources/Textures/LobbyScreens/invisiblewall.webp b/Resources/Textures/LobbyScreens/invisiblewall.webp new file mode 100644 index 0000000000..ac38cdbac0 Binary files /dev/null and b/Resources/Textures/LobbyScreens/invisiblewall.webp differ diff --git a/Resources/Textures/LobbyScreens/invisiblewall.webp.yml b/Resources/Textures/LobbyScreens/invisiblewall.webp.yml new file mode 100644 index 0000000000..5c43e23305 --- /dev/null +++ b/Resources/Textures/LobbyScreens/invisiblewall.webp.yml @@ -0,0 +1,2 @@ +sample: + filter: true diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/xenoborg_engi.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/xenoborg_engi.png index ac544fb5bd..28f2c33515 100644 Binary files a/Resources/Textures/Mobs/Silicon/chassis.rsi/xenoborg_engi.png and b/Resources/Textures/Mobs/Silicon/chassis.rsi/xenoborg_engi.png differ diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/broken.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/broken.png new file mode 100644 index 0000000000..02fc551ed7 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/station_ai.rsi/broken.png differ diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json b/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json index 6409354fa8..93fa4f1f55 100644 --- a/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json +++ b/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json @@ -254,6 +254,9 @@ }, { "name": "frame_4" + }, + { + "name": "broken" } ] } diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/full.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/full.png deleted file mode 100644 index 44e3df3e91..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/full.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/head_f.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/head_f.png deleted file mode 100644 index dada5727bf..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/head_f.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/head_m.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/head_m.png deleted file mode 100644 index dada5727bf..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/head_m.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_arm.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_arm.png deleted file mode 100644 index bb7425405c..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_arm.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_foot.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_foot.png deleted file mode 100644 index 8e0b3f1507..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_foot.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_hand.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_hand.png deleted file mode 100644 index cf93432a5e..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_hand.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_leg.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_leg.png deleted file mode 100644 index d693b3696d..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_leg.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/meta.json b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/meta.json deleted file mode 100644 index 688877a32d..0000000000 --- a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/meta.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Created by ps3moira#9488 (discord) for SS14.", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "full" - }, - { - "name": "head_f", - "directions": 4 - }, - { - "name": "head_m", - "directions": 4 - }, - { - "name": "l_arm", - "directions": 4 - }, - { - "name": "l_foot", - "directions": 4 - }, - { - "name": "l_hand", - "directions": 4 - }, - { - "name": "l_leg", - "directions": 4 - }, - { - "name": "r_arm", - "directions": 4 - }, - { - "name": "r_foot", - "directions": 4 - }, - { - "name": "r_hand", - "directions": 4 - }, - { - "name": "r_leg", - "directions": 4 - }, - { - "name": "skull_icon", - "directions": 1 - }, - { - "name": "torso_f", - "directions": 4 - }, - { - "name": "torso_m", - "directions": 4 - } - ] -} diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_arm.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_arm.png deleted file mode 100644 index 51f05a4773..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_arm.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_foot.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_foot.png deleted file mode 100644 index 19ac240da3..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_foot.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_hand.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_hand.png deleted file mode 100644 index 6cd2eb37cc..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_hand.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_leg.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_leg.png deleted file mode 100644 index e1ea113ca3..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_leg.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/skull_icon.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/skull_icon.png deleted file mode 100644 index 6d0ea66780..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/skull_icon.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/torso_f.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/torso_f.png deleted file mode 100644 index 2bcb3cc9eb..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/torso_f.png and /dev/null differ diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/torso_m.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/torso_m.png deleted file mode 100644 index 2bcb3cc9eb..0000000000 Binary files a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/torso_m.png and /dev/null differ diff --git a/Resources/Textures/Objects/Devices/pda.rsi/equipped-BELT.png b/Resources/Textures/Objects/Devices/pda.rsi/equipped-BELT.png deleted file mode 100644 index 6901e6c33b..0000000000 Binary files a/Resources/Textures/Objects/Devices/pda.rsi/equipped-BELT.png and /dev/null differ diff --git a/Resources/Textures/Objects/Devices/pda.rsi/equipped-IDCARD.png b/Resources/Textures/Objects/Devices/pda.rsi/equipped-IDCARD.png deleted file mode 100644 index 6901e6c33b..0000000000 Binary files a/Resources/Textures/Objects/Devices/pda.rsi/equipped-IDCARD.png and /dev/null differ diff --git a/Resources/Textures/Objects/Devices/pda.rsi/meta.json b/Resources/Textures/Objects/Devices/pda.rsi/meta.json index e5833360bd..984030280f 100644 --- a/Resources/Textures/Objects/Devices/pda.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/pda.rsi/meta.json @@ -13,10 +13,6 @@ { "name": "id_overlay_wide" }, - { - "name": "equipped-BELT", - "directions": 4 - }, { "name": "inhand-left", "directions": 4 @@ -254,10 +250,6 @@ 0.3 ] ] - }, - { - "name": "equipped-IDCARD", - "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Devices/travel_camera.rsi/equipped-NECK.png b/Resources/Textures/Objects/Devices/travel_camera.rsi/equipped-NECK.png new file mode 100644 index 0000000000..03ae539d6f Binary files /dev/null and b/Resources/Textures/Objects/Devices/travel_camera.rsi/equipped-NECK.png differ diff --git a/Resources/Textures/Objects/Devices/travel_camera.rsi/icon.png b/Resources/Textures/Objects/Devices/travel_camera.rsi/icon.png new file mode 100644 index 0000000000..2e13f71cfa Binary files /dev/null and b/Resources/Textures/Objects/Devices/travel_camera.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Devices/travel_camera.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/travel_camera.rsi/inhand-left.png new file mode 100644 index 0000000000..c475dea254 Binary files /dev/null and b/Resources/Textures/Objects/Devices/travel_camera.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Devices/travel_camera.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/travel_camera.rsi/inhand-right.png new file mode 100644 index 0000000000..6df91ff4a2 Binary files /dev/null and b/Resources/Textures/Objects/Devices/travel_camera.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Devices/travel_camera.rsi/meta.json b/Resources/Textures/Objects/Devices/travel_camera.rsi/meta.json new file mode 100644 index 0000000000..acd1d82967 --- /dev/null +++ b/Resources/Textures/Objects/Devices/travel_camera.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation, edited by SlamBamActionman", + "size": { + "x": 32, + "y": 32 + }, + + "states": [ + { + "name": "equipped-NECK", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/black.png b/Resources/Textures/Objects/Misc/photograph.rsi/black.png new file mode 100644 index 0000000000..2e1284ebeb Binary files /dev/null and b/Resources/Textures/Objects/Misc/photograph.rsi/black.png differ diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/blue.png b/Resources/Textures/Objects/Misc/photograph.rsi/blue.png new file mode 100644 index 0000000000..d32bb7534c Binary files /dev/null and b/Resources/Textures/Objects/Misc/photograph.rsi/blue.png differ diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/green.png b/Resources/Textures/Objects/Misc/photograph.rsi/green.png new file mode 100644 index 0000000000..6635d990ec Binary files /dev/null and b/Resources/Textures/Objects/Misc/photograph.rsi/green.png differ diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/meta.json b/Resources/Textures/Objects/Misc/photograph.rsi/meta.json new file mode 100644 index 0000000000..a76a07d169 --- /dev/null +++ b/Resources/Textures/Objects/Misc/photograph.rsi/meta.json @@ -0,0 +1,32 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by ketufaispikinut (Github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "black" + }, + { + "name": "red" + }, + { + "name": "blue" + }, + { + "name": "green" + }, + { + "name": "yellow" + }, + { + "name": "purple" + }, + { + "name": "rainbow" + } + ] +} diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/purple.png b/Resources/Textures/Objects/Misc/photograph.rsi/purple.png new file mode 100644 index 0000000000..6bf8b7b252 Binary files /dev/null and b/Resources/Textures/Objects/Misc/photograph.rsi/purple.png differ diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/rainbow.png b/Resources/Textures/Objects/Misc/photograph.rsi/rainbow.png new file mode 100644 index 0000000000..5aaef8a35b Binary files /dev/null and b/Resources/Textures/Objects/Misc/photograph.rsi/rainbow.png differ diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/red.png b/Resources/Textures/Objects/Misc/photograph.rsi/red.png new file mode 100644 index 0000000000..9df1683989 Binary files /dev/null and b/Resources/Textures/Objects/Misc/photograph.rsi/red.png differ diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/yellow.png b/Resources/Textures/Objects/Misc/photograph.rsi/yellow.png new file mode 100644 index 0000000000..6cfc8c71ed Binary files /dev/null and b/Resources/Textures/Objects/Misc/photograph.rsi/yellow.png differ diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_garbage_blue.png b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_garbage_blue.png new file mode 100644 index 0000000000..b764b37fe4 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_garbage_blue.png differ diff --git a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/meta.json b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/meta.json index 708d514397..7ae9fa1628 100644 --- a/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428, cart_goldenplunger modified from cart_plunger by TiniestShark (github) and tweaked by Prole0 (github)", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428, cart_goldenplunger modified from cart_plunger by TiniestShark (github) and tweaked by Prole0 (github), cart_garbage_blue modified from cart_garbage by AndrewFenriz (github)", "copyright": "Taken from TauCetiStation at commit https://github.com/TauCetiStation/TauCetiClassic/pull/970/commits/6f0a020e35a78b997420ca8ffff2c1de4bad3428, cart_goldenplunger modified from cart_plunger by TiniestShark (github)", "states": [ { @@ -16,6 +16,10 @@ "name": "cart_garbage", "directions": 4 }, + { + "name": "cart_garbage_blue", + "directions": 4 + }, { "name": "cart_mop", "directions": 4 diff --git a/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/blue-equipped-BELT.png b/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/blue-equipped-BELT.png new file mode 100644 index 0000000000..0b2ba41f2f Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/blue-equipped-BELT.png differ diff --git a/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/equipped-BELT.png b/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/equipped-BELT.png new file mode 100644 index 0000000000..cec43f8bd7 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/equipped-BELT.png differ diff --git a/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/meta.json b/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/meta.json index 6c11b86921..5ae1530e5d 100644 --- a/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/meta.json @@ -46,6 +46,14 @@ { "name": "blue-inhand-right", "directions": 4 + }, + { + "name": "equipped-BELT", + "directions": 4 + }, + { + "name": "blue-equipped-BELT", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-jump.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-jump.png new file mode 100644 index 0000000000..e667754481 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-jump.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-tile.png b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-tile.png new file mode 100644 index 0000000000..5fc8309c43 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-tile.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json index 345a622f3f..59c4c75ec8 100644 --- a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json @@ -124,6 +124,9 @@ { "name": "icon-xenoborg-cloak" }, + { + "name": "icon-xenoborg-jump" + }, { "name": "icon-xenoborg-cloak2" }, @@ -154,6 +157,9 @@ { "name": "icon-xenoborg-sword2" }, + { + "name": "icon-xenoborg-tile" + }, { "name": "icon-xenoborg-tools" }, diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..14c04d0ddd Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..e00c0e7a55 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..aeae5c35d9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..443e1e7ae1 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..116628a3ed Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/mask.png b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/mask.png new file mode 100644 index 0000000000..2e2d20d397 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/meta.json index 99437cf31d..ecd1f06fee 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455. Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -54,6 +54,24 @@ 0.2 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..14c04d0ddd Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..e00c0e7a55 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..aeae5c35d9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..443e1e7ae1 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..ad78a82d19 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/mask.png b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/mask.png new file mode 100644 index 0000000000..979cb6add8 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/meta.json index 99437cf31d..ecd1f06fee 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455. Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -54,6 +54,24 @@ 0.2 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..14c04d0ddd Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..e00c0e7a55 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..aeae5c35d9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..443e1e7ae1 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..ad78a82d19 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/mask.png b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/mask.png new file mode 100644 index 0000000000..908cd70ce9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/meta.json index bc260937b3..79fea60158 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455. Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -80,6 +80,24 @@ 0.2 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..4171a36f1e Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..96fef17086 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..05118ceae9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..b6f305736a Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..07fb9994ba Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/mask.png b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/mask.png new file mode 100644 index 0000000000..d8f296bd8b Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/meta.json index b2f8b619d5..756d7257dd 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, inhand-left and inhand-right by SlamBamActionman (Github).", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, inhand-left and inhand-right by SlamBamActionman (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -89,6 +89,24 @@ 0.1 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..14c04d0ddd Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..e00c0e7a55 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..aeae5c35d9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..443e1e7ae1 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..ad78a82d19 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/mask.png b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/mask.png new file mode 100644 index 0000000000..64e7863087 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/meta.json index bc260937b3..79fea60158 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455. Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -80,6 +80,24 @@ 0.2 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..c81e4b9868 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..52220c8954 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c42dce067d Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..cd0d10099f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING-vox.png b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-4.png similarity index 50% rename from Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING-vox.png rename to Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-4.png index c075eb9a7b..931132780c 100644 Binary files a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING-vox.png and b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/mask.png b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/mask.png new file mode 100644 index 0000000000..a7d8b59507 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/meta.json index a8ce0b5a28..0f47b7ebe8 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Drawn by Ubaser.", + "copyright": "Drawn by Ubaser. Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -44,6 +44,24 @@ { "name": "on-equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..ff4e662e73 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..63df9585b9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..8ecb49826f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..b8d7b7f809 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..87df57230a Binary files /dev/null and b/Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/mask.png b/Resources/Textures/Objects/Tanks/anesthetic.rsi/mask.png new file mode 100644 index 0000000000..7814561c83 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/anesthetic.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json b/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json index a9dff4eaf1..d47e1e011d 100644 --- a/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json @@ -1,75 +1,93 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Integrity stages by Princess-Cheeseballs (Github)", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-BACKPACK", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE-dog", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-puppy", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-fox", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-cat", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-sloth", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-hamster", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-kangaroo", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-possum", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-pig", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE-dog", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-puppy", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-fox", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-cat", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-sloth", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-hamster", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-kangaroo", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-possum", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-pig", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" + } + ] } diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..f40c14ac47 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..278f80945f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c06374e80f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..54c1753e48 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..a65b568dcc Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/mask.png b/Resources/Textures/Objects/Tanks/emergency.rsi/mask.png new file mode 100644 index 0000000000..1062dad6d5 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency.rsi/meta.json index 8c67ffd7c9..53f656df6a 100644 --- a/Resources/Textures/Objects/Tanks/emergency.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage and icon edits by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -22,11 +22,6 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, - { - "name": "equipped-SUITSTORAGE-kangaroo", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, { "name": "equipped-SUITSTORAGE-puppy", "directions": 4, @@ -47,6 +42,16 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, + { + "name": "equipped-SUITSTORAGE-hamster", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-kangaroo", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, { "name": "equipped-SUITSTORAGE-possum", "directions": 4, @@ -57,11 +62,6 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, - { - "name": "equipped-SUITSTORAGE-hamster", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, { "name": "inhand-left", "directions": 4 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..8b05d65cb1 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..637c1f007c Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c675dfeb0e Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..5425aea0d9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..1a64b21a63 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/mask.png b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/mask.png new file mode 100644 index 0000000000..d7fd9a572e Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/meta.json index 760edc5a67..e32c18d479 100644 --- a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/ff8df99906ab1909674680b9973bd3c909080360. Storage by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..4a3a086d62 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..dd289f8c52 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c406019ccb Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..c97800296b Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..0006eb20b3 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/mask.png b/Resources/Textures/Objects/Tanks/emergency_double.rsi/mask.png new file mode 100644 index 0000000000..247117f21e Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json index f4db0728d2..e32c18d479 100644 --- a/Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..4a3a086d62 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..dd289f8c52 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c406019ccb Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..c97800296b Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..0006eb20b3 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/mask.png b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/mask.png new file mode 100644 index 0000000000..ce27f54c82 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/meta.json index f8ac073188..e32c18d479 100644 --- a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Adapted by DangerRevolution from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage and icon edits by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..8b05d65cb1 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..637c1f007c Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c675dfeb0e Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..5425aea0d9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..1a64b21a63 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/mask.png b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/mask.png new file mode 100644 index 0000000000..b528543af3 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/meta.json index f4db0728d2..e32c18d479 100644 --- a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..8b05d65cb1 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..637c1f007c Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c675dfeb0e Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..5425aea0d9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..1a64b21a63 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/mask.png b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/mask.png new file mode 100644 index 0000000000..afc9525d81 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/meta.json index dff0f2dd0e..e32c18d479 100644 --- a/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Adapted by DangerRevolution from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..f40c14ac47 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..278f80945f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c06374e80f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..54c1753e48 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..a65b568dcc Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_red.rsi/mask.png b/Resources/Textures/Objects/Tanks/emergency_red.rsi/mask.png new file mode 100644 index 0000000000..1532b8a6ba Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_red.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_red.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_red.rsi/meta.json index 5990e60d2b..53f656df6a 100644 --- a/Resources/Textures/Objects/Tanks/emergency_red.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_red.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Adapted by DangerRevolution from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage and icon edits by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -22,11 +22,6 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, - { - "name": "equipped-SUITSTORAGE-kangaroo", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, { "name": "equipped-SUITSTORAGE-puppy", "directions": 4, @@ -47,6 +42,16 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, + { + "name": "equipped-SUITSTORAGE-hamster", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-kangaroo", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, { "name": "equipped-SUITSTORAGE-possum", "directions": 4, @@ -57,11 +62,6 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, - { - "name": "equipped-SUITSTORAGE-hamster", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, { "name": "inhand-left", "directions": 4 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..f40c14ac47 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..278f80945f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..c06374e80f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..54c1753e48 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..a65b568dcc Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/mask.png b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/mask.png new file mode 100644 index 0000000000..0d1e43896f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/meta.json index 1cfa40a9f9..53f656df6a 100644 --- a/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_yellow.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage and icon edits by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage and icon edits by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..ff4e662e73 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..63df9585b9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..8ecb49826f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..b8d7b7f809 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..87df57230a Binary files /dev/null and b/Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/mask.png b/Resources/Textures/Objects/Tanks/generic.rsi/mask.png new file mode 100644 index 0000000000..5d7acb4d1c Binary files /dev/null and b/Resources/Textures/Objects/Tanks/generic.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/generic.rsi/meta.json b/Resources/Textures/Objects/Tanks/generic.rsi/meta.json index 62f7319206..2b323dee74 100644 --- a/Resources/Textures/Objects/Tanks/generic.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/generic.rsi/meta.json @@ -1,75 +1,93 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Suit and back storages drawn by Ubaser.", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Suit and back storages drawn by Ubaser. Integrity stages by Princess-Cheeseballs (Github)", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-BACKPACK", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE-dog", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-puppy", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-fox", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-cat", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-sloth", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-hamster", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-kangaroo", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-possum", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-pig", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE-dog", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-puppy", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-fox", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-cat", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-sloth", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-hamster", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-kangaroo", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-possum", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-pig", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" + } + ] } diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..ff4e662e73 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..63df9585b9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..8ecb49826f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..b8d7b7f809 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..87df57230a Binary files /dev/null and b/Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/mask.png b/Resources/Textures/Objects/Tanks/oxygen.rsi/mask.png new file mode 100644 index 0000000000..8dc354095e Binary files /dev/null and b/Resources/Textures/Objects/Tanks/oxygen.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/oxygen.rsi/meta.json b/Resources/Textures/Objects/Tanks/oxygen.rsi/meta.json index 62f7319206..2b323dee74 100644 --- a/Resources/Textures/Objects/Tanks/oxygen.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/oxygen.rsi/meta.json @@ -1,75 +1,93 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Suit and back storages drawn by Ubaser.", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Suit and back storages drawn by Ubaser. Integrity stages by Princess-Cheeseballs (Github)", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-BACKPACK", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE-dog", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-puppy", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-fox", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-cat", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-sloth", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-hamster", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-kangaroo", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-possum", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-pig", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE-dog", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-puppy", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-fox", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-cat", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-sloth", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-hamster", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-kangaroo", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-possum", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-pig", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" + } + ] } diff --git a/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..0e8b3d4bd2 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..aaf9fc78f1 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..69ab788a11 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..a000c08a62 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..866db3b674 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/plasma.rsi/mask.png b/Resources/Textures/Objects/Tanks/plasma.rsi/mask.png new file mode 100644 index 0000000000..77d9a4a379 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/plasma.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/plasma.rsi/meta.json b/Resources/Textures/Objects/Tanks/plasma.rsi/meta.json index 2d3199f75c..f7ddcc6235 100644 --- a/Resources/Textures/Objects/Tanks/plasma.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/plasma.rsi/meta.json @@ -1,30 +1,48 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Integrity stages by Princess-Cheeseballs (Github)", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-BELT", - "directions": 4 - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE", - "directions": 4 - } - ] + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BELT", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE", + "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" + } + ] } diff --git a/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..ff4e662e73 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-1.png b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..63df9585b9 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-2.png b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..8ecb49826f Binary files /dev/null and b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-3.png b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..b8d7b7f809 Binary files /dev/null and b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-4.png b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..87df57230a Binary files /dev/null and b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Objects/Tanks/red.rsi/mask.png b/Resources/Textures/Objects/Tanks/red.rsi/mask.png new file mode 100644 index 0000000000..33638d35ee Binary files /dev/null and b/Resources/Textures/Objects/Tanks/red.rsi/mask.png differ diff --git a/Resources/Textures/Objects/Tanks/red.rsi/meta.json b/Resources/Textures/Objects/Tanks/red.rsi/meta.json index 62f7319206..2b323dee74 100644 --- a/Resources/Textures/Objects/Tanks/red.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/red.rsi/meta.json @@ -1,75 +1,93 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Suit and back storages drawn by Ubaser.", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Suit and back storages drawn by Ubaser. Integrity stages by Princess-Cheeseballs (Github)", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-BACKPACK", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE-dog", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-puppy", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-fox", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-cat", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-sloth", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-hamster", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-kangaroo", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-possum", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-pig", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE-dog", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-puppy", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-fox", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-cat", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-sloth", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-hamster", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-kangaroo", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-possum", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-pig", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" + } + ] } diff --git a/Resources/Textures/Objects/Tools/hijack_beacon.rsi/extraction_point.png b/Resources/Textures/Objects/Tools/hijack_beacon.rsi/extraction_point.png new file mode 100644 index 0000000000..7b8dd4c3af Binary files /dev/null and b/Resources/Textures/Objects/Tools/hijack_beacon.rsi/extraction_point.png differ diff --git a/Resources/Textures/Objects/Tools/hijack_beacon.rsi/meta.json b/Resources/Textures/Objects/Tools/hijack_beacon.rsi/meta.json new file mode 100644 index 0000000000..5177acf42f --- /dev/null +++ b/Resources/Textures/Objects/Tools/hijack_beacon.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/austation/austation/commit/e2a4fefd01e702f48d3d4cc8d6a2686d54d104fa and edited by TheShuEd. Recolored by alexalexmax", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "extraction_point", + "delays": [ + [ + 0.5, + 0.5 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/icon.png b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/icon.png new file mode 100644 index 0000000000..5e345c10e7 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-1.png b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-1.png new file mode 100644 index 0000000000..171ad18162 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-1.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-2.png b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-2.png new file mode 100644 index 0000000000..c51709c6ad Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-2.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-3.png b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-3.png new file mode 100644 index 0000000000..bdf4d8ab35 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-3.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-4.png b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-4.png new file mode 100644 index 0000000000..8c8fdb1e0c Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-4.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-5.png b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-5.png new file mode 100644 index 0000000000..d3bf22c77d Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-5.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-6.png b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-6.png new file mode 100644 index 0000000000..23c81a161a Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-6.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/meta.json new file mode 100644 index 0000000000..a9711a6798 --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/meta.json @@ -0,0 +1,40 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "created by Samuka-C (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "mag-1" + }, + { + "name": "mag-2" + }, + { + "name": "mag-3" + }, + { + "name": "mag-4" + }, + { + "name": "mag-5" + }, + { + "name": "mag-6" + } + ] +} diff --git a/Resources/Textures/Shaders/displacement_unshaded.swsl b/Resources/Textures/Shaders/displacement_unshaded.swsl new file mode 100644 index 0000000000..cc389b9a9b --- /dev/null +++ b/Resources/Textures/Shaders/displacement_unshaded.swsl @@ -0,0 +1,20 @@ +light_mode unshaded; + +uniform sampler2D displacementMap; +uniform highp float displacementSize; +uniform highp vec4 displacementUV; + +varying highp vec2 displacementUVOut; + +void vertex() { + displacementUVOut = mix(displacementUV.xy, displacementUV.zw, tCoord2); +} + +void fragment() { + highp vec4 displacementSample = texture2D(displacementMap, displacementUVOut); + highp vec2 displacementValue = (displacementSample.xy - vec2(128.0 / 255.0)) / (1.0 - 128.0 / 255.0); + COLOR = zTexture(UV + displacementValue * TEXTURE_PIXEL_SIZE * displacementSize * vec2(1.0, -1.0)); + COLOR.a *= displacementSample.a; +} + + diff --git a/Resources/Textures/Shaders/drunk.swsl b/Resources/Textures/Shaders/drunk.swsl index c2657c18a5..1410baa823 100644 --- a/Resources/Textures/Shaders/drunk.swsl +++ b/Resources/Textures/Shaders/drunk.swsl @@ -1,22 +1,29 @@ uniform sampler2D SCREEN_TEXTURE; uniform highp float boozePower; -const highp float TimeScale = 0.5; -const highp float DistortionScale = 0.01; +// How fast to do the rotating motion. +// 1 for normal effect. +// 0 for for no motion. +uniform highp float timeScale; +// Starting phase for the rotation effect. +// Needed so it doesn't always look the same for 0 motion. +uniform highp float phase; +// Multiplier for the amplitude of the offset. 1 for normal strength. +uniform highp float distortionScale; void fragment() { - highp float mod = mix(0.0, DistortionScale, boozePower); + highp float amplitudeMod = mix(0.0, distortionScale * 0.01, boozePower); highp vec2 coord = FRAGCOORD.xy * SCREEN_PIXEL_SIZE.xy; - highp float time = TIME * TimeScale; + highp float time = TIME * timeScale * 0.5 + phase; - highp vec2 offset = vec2((mod * 1.5) * sin(time * 1.5), (mod * 2.0) * cos(time * 1.5 - 0.2)); + highp vec2 offset = vec2((amplitudeMod * 1.5) * sin(time * 1.5), (amplitudeMod * 2.0) * cos(time * 1.5 - 0.2)); highp vec4 tex1 = zTextureSpec(SCREEN_TEXTURE, coord + offset); - - if (boozePower > 0.5) { - offset = vec2((mod * 2.0 - DistortionScale) * sin(time * 0.333 - 0.2), (mod * 2.0 - DistortionScale) * cos(time * 0.333)); - tex1 = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.3, boozePower*2.0-1.0)); + + if (boozePower > 0.25) { + offset = vec2((amplitudeMod * 2.0) * sin(time * 0.333 - 0.2), (amplitudeMod * 2.0) * cos(time * 0.333)); + tex1 = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.3, boozePower * 2.0 - 0.5)); } - - offset = vec2((mod * 1.0) * sin(time * 1.0 + 0.1), (mod * 1.0) * cos(time * 1.0)); + + offset = vec2(amplitudeMod * sin(time * 1.0 + 0.1), amplitudeMod * cos(time * 1.0)); COLOR = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.5, boozePower)); } diff --git a/Resources/Textures/Shaders/heatBlur.swsl b/Resources/Textures/Shaders/heatBlur.swsl new file mode 100644 index 0000000000..0f7d2400e3 --- /dev/null +++ b/Resources/Textures/Shaders/heatBlur.swsl @@ -0,0 +1,34 @@ +uniform sampler2D SCREEN_TEXTURE; +uniform sampler2D NOISE_TEXTURE; // The pre-generated noise + +// Those 3 are overwritten at SetReducedMotion +uniform highp float spatial_scale; // Makes more waves +uniform highp float strength_scale; // Makes waves stronger +uniform highp float speed_scale; // Makes waves run faster + +void fragment() +{ + // Calculate scrolling UVs for the noise + highp vec2 flow1 = vec2(0.0, TIME * speed_scale); + highp vec2 flow2 = vec2(TIME * 0.5 * 0.1, TIME * speed_scale * 1.2); + + // Sample the pre-calculated noise + highp float noiseVal = texture2D(NOISE_TEXTURE, fract((UV * spatial_scale) - flow1)).r; + highp float noiseVal2 = texture2D(NOISE_TEXTURE, fract((UV * spatial_scale) - flow2 + vec2(0.43, 0.12))).r; + + // Create distortion vector + highp vec2 distortion = vec2( + noiseVal - 0.5, + noiseVal2 - 0.5 + ); + + highp float heatStrength = texture2D(TEXTURE, UV).r; + + // Non-linear curve to make the heat look more "intense" in the center + heatStrength = clamp(-heatStrength * heatStrength + 2.0 * heatStrength, 0.0, 1.0); + + // Apply Distortion to Screen + highp vec2 distortedUV = UV + (distortion * strength_scale * heatStrength); + + COLOR = zTextureSpec(SCREEN_TEXTURE, distortedUV); +} diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner-blue.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner-blue.png index 7a1e37693e..c7b4de2553 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner-blue.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner-blue.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner-green.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner-green.png index a06a48885b..30e26f0ed4 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner-green.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner-green.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner-red.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner-red.png index 50e9b054af..a860f5e15e 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner-red.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner-red.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner-yellow.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner-yellow.png index 5c582fe755..3c0b66c909 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner-yellow.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner-yellow.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner.png index 8daa67ae23..0b7ff8bd30 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_cargo.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_cargo.png index ff808bbc8b..4a6f70cea1 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner_cargo.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner_cargo.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_engineering.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_engineering.png index da76b044c1..0780db8b74 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner_engineering.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner_engineering.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_medical.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_medical.png index 5bbd100773..eea1060c57 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner_medical.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner_medical.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_revolution.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_revolution.png index fd97a4b39d..5e5cd4c8d7 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner_revolution.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner_revolution.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_science.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_science.png index 8ae607cdcd..699a1155d4 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner_science.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner_science.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_security.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_security.png index 42114c515b..0fc82598d5 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner_security.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner_security.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_syndicate.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_syndicate.png index c8424057d0..5f7c2cfeef 100644 Binary files a/Resources/Textures/Structures/Decoration/banner.rsi/banner_syndicate.png and b/Resources/Textures/Structures/Decoration/banner.rsi/banner_syndicate.png differ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/meta.json b/Resources/Textures/Structures/Decoration/banner.rsi/meta.json index 69066fc803..b2421af6d0 100644 --- a/Resources/Textures/Structures/Decoration/banner.rsi/meta.json +++ b/Resources/Textures/Structures/Decoration/banner.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/tgstation/tgstation/commit/fa9e44d937026d5a2ba72615afccf2f18a87c485 | banner_syndicate sprited by mureixlol (Discord)| color banners repsrited by lzk228", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/pull/94997/commits/ffa07f506823033fe3f35f45defb6f16380527df, NT, Cargo, Syndicate, blue, green and yellow banners modified by _Svist_. (Discord) ", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/firelock.rsi/assembly.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/firelock.rsi/assembly.png index 0b14b77aec..3cc26d19ff 100644 Binary files a/Resources/Textures/Structures/Doors/Airlocks/Glass/firelock.rsi/assembly.png and b/Resources/Textures/Structures/Doors/Airlocks/Glass/firelock.rsi/assembly.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/meta.json b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/meta.json index e0ec34938b..509c9e55a3 100644 --- a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/meta.json +++ b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from TauCetiClassic at commit https://github.com/TauCetiStation/TauCetiClassic/commit/fd5cfd76acdf5bda9e46413c11006a6e825d51a9", + "copyright": "Taken from TauCetiClassic at commit https://github.com/TauCetiStation/TauCetiClassic/commit/fd5cfd76acdf5bda9e46413c11006a6e825d51a9, panel_open fixed by @mishutka09(discord:1152277579206774854)", "size": { "x": 32, "y": 32 @@ -34,9 +34,6 @@ { "name": "closed-glass" }, - { - "name": "panel_closed" - }, { "name": "closing", "delays": [ @@ -90,7 +87,7 @@ ] }, { - "name": "closing-panel", + "name": "panel_closing", "delays": [ [ 0.1, @@ -174,7 +171,7 @@ ] }, { - "name": "opening-panel", + "name": "panel_opening", "delays": [ [ 0.1, diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_closed.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_closed.png deleted file mode 100644 index 4c59d3a28c..0000000000 Binary files a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_closed.png and /dev/null differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/closing-panel.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_closing.png similarity index 100% rename from Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/closing-panel.png rename to Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_closing.png diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_open.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_open.png index c41e1484ee..89db9a5bff 100644 Binary files a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_open.png and b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_open.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/opening-panel.png b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_opening.png similarity index 100% rename from Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/opening-panel.png rename to Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_opening.png diff --git a/Resources/Textures/Structures/Power/cell_recharger.rsi/inhand-left.png b/Resources/Textures/Structures/Power/cell_recharger.rsi/inhand-left.png new file mode 100644 index 0000000000..12d689adf9 Binary files /dev/null and b/Resources/Textures/Structures/Power/cell_recharger.rsi/inhand-left.png differ diff --git a/Resources/Textures/Structures/Power/cell_recharger.rsi/inhand-right.png b/Resources/Textures/Structures/Power/cell_recharger.rsi/inhand-right.png new file mode 100644 index 0000000000..4133de5276 Binary files /dev/null and b/Resources/Textures/Structures/Power/cell_recharger.rsi/inhand-right.png differ diff --git a/Resources/Textures/Structures/Power/cell_recharger.rsi/meta.json b/Resources/Textures/Structures/Power/cell_recharger.rsi/meta.json index 42ad3ab990..4a59465e53 100644 --- a/Resources/Textures/Structures/Power/cell_recharger.rsi/meta.json +++ b/Resources/Textures/Structures/Power/cell_recharger.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "Taken from Goonstation at commit https://github.com/goonstation/goonstation/commit/4f88b9314336631929c9cdddb1567fc08f83bf9e and modified by potato1234x (github), then again by EmoGarbage404 (github) for ss14", + "copyright": "Taken from Goonstation at commit https://github.com/goonstation/goonstation/commit/4f88b9314336631929c9cdddb1567fc08f83bf9e and modified by potato1234x (github), then again by EmoGarbage404 (github) for ss14, in-hands by spanky-spanky (GitHub)", "states": [ { "name": "light-off" @@ -47,6 +47,14 @@ 0.8 ] ] + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 } ] } diff --git a/Resources/Textures/Structures/Power/recharger.rsi/inhand-left.png b/Resources/Textures/Structures/Power/recharger.rsi/inhand-left.png new file mode 100644 index 0000000000..e9d698667d Binary files /dev/null and b/Resources/Textures/Structures/Power/recharger.rsi/inhand-left.png differ diff --git a/Resources/Textures/Structures/Power/recharger.rsi/inhand-right.png b/Resources/Textures/Structures/Power/recharger.rsi/inhand-right.png new file mode 100644 index 0000000000..e9d698667d Binary files /dev/null and b/Resources/Textures/Structures/Power/recharger.rsi/inhand-right.png differ diff --git a/Resources/Textures/Structures/Power/recharger.rsi/meta.json b/Resources/Textures/Structures/Power/recharger.rsi/meta.json index 1aee0099ef..4ca570e071 100644 --- a/Resources/Textures/Structures/Power/recharger.rsi/meta.json +++ b/Resources/Textures/Structures/Power/recharger.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/discordia-space/CEV-Eris/raw/9ea3eccbe22e18d24653949067f3d7dd12194ea9/icons/obj/stationobjs.dmi", + "copyright": "https://github.com/discordia-space/CEV-Eris/raw/9ea3eccbe22e18d24653949067f3d7dd12194ea9/icons/obj/stationobjs.dmi , in-hands by spanky-spanky (GitHub)", "states": [ { "name": "empty" @@ -55,6 +55,14 @@ 0.1 ] ] + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 } ] } diff --git a/Resources/Textures/Structures/Power/turbo_recharger.rsi/inhand-left.png b/Resources/Textures/Structures/Power/turbo_recharger.rsi/inhand-left.png new file mode 100644 index 0000000000..40ab6caf12 Binary files /dev/null and b/Resources/Textures/Structures/Power/turbo_recharger.rsi/inhand-left.png differ diff --git a/Resources/Textures/Structures/Power/turbo_recharger.rsi/inhand-right.png b/Resources/Textures/Structures/Power/turbo_recharger.rsi/inhand-right.png new file mode 100644 index 0000000000..40ab6caf12 Binary files /dev/null and b/Resources/Textures/Structures/Power/turbo_recharger.rsi/inhand-right.png differ diff --git a/Resources/Textures/Structures/Power/turbo_recharger.rsi/meta.json b/Resources/Textures/Structures/Power/turbo_recharger.rsi/meta.json index 50271d1e33..0483760670 100644 --- a/Resources/Textures/Structures/Power/turbo_recharger.rsi/meta.json +++ b/Resources/Textures/Structures/Power/turbo_recharger.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "adapted from https://github.com/discordia-space/CEV-Eris/raw/9ea3eccbe22e18d24653949067f3d7dd12194ea9/icons/obj/stationobjs.dmi by EmoGarbage404 (github)", + "copyright": "adapted from https://github.com/discordia-space/CEV-Eris/raw/9ea3eccbe22e18d24653949067f3d7dd12194ea9/icons/obj/stationobjs.dmi by EmoGarbage404 (github), in-hands by spanky-spanky (GitHub)", "states": [ { "name": "empty" @@ -57,6 +57,14 @@ 0.05 ] ] + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 } ] } diff --git a/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-0.png b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000..ee5e372ef4 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-0.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-1.png b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-1.png new file mode 100644 index 0000000000..f457d016c3 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-1.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-2.png b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-2.png new file mode 100644 index 0000000000..6791d3b813 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-2.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-3.png b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-3.png new file mode 100644 index 0000000000..30e76da9fa Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-3.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-4.png b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-4.png new file mode 100644 index 0000000000..7c1bb333d3 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-4.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-black.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-black.png new file mode 100644 index 0000000000..e44e4191e0 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-black.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-blue.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-blue.png new file mode 100644 index 0000000000..d3bc54f284 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-blue.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-darkblue.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-darkblue.png new file mode 100644 index 0000000000..938cc9c161 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-darkblue.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-frezon.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-frezon.png new file mode 100644 index 0000000000..48da90174e Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-frezon.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-green.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-green.png new file mode 100644 index 0000000000..aafe16a4f5 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-green.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-greenys.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-greenys.png new file mode 100644 index 0000000000..87e7dd783c Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-greenys.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-grey.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-grey.png new file mode 100644 index 0000000000..d94929d4cb Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-grey.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-orange.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-orange.png new file mode 100644 index 0000000000..a7ce5a73a9 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-orange.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-red.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-red.png new file mode 100644 index 0000000000..88a6f988db Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-red.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-redws.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-redws.png new file mode 100644 index 0000000000..b564c69866 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-redws.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-water_vapor.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-water_vapor.png new file mode 100644 index 0000000000..ed23006740 Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-water_vapor.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-yellow.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-yellow.png new file mode 100644 index 0000000000..038a8aeaea Binary files /dev/null and b/Resources/Textures/Structures/Storage/canister.rsi/mask-yellow.png differ diff --git a/Resources/Textures/Structures/Storage/canister.rsi/meta.json b/Resources/Textures/Structures/Storage/canister.rsi/meta.json index 8f976a2461..3c09ca4cf9 100644 --- a/Resources/Textures/Structures/Storage/canister.rsi/meta.json +++ b/Resources/Textures/Structures/Storage/canister.rsi/meta.json @@ -2,7 +2,7 @@ "version": 1, "license": "CC-BY-SA-3.0", "copyright": "Frezon canister modified from tgstation, the rest are taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f8581a636acfc1517611a680b7711a74fc7ef335. Paper Sprites by Vermidia.", - "copyright": "Taken from TauCetiClassic at commit https://github.com/TauCetiStation/TauCetiClassic/tree/13b5c47145804dd24507b75f250de5e87d34e194", + "copyright": "Taken from TauCetiClassic at commit https://github.com/TauCetiStation/TauCetiClassic/tree/13b5c47145804dd24507b75f250de5e87d34e194, integrity & masks fixed by @mishutka09(discord:1152277579206774854)", "size": { "x": 32, "y": 32 @@ -14,12 +14,18 @@ { "name": "black-1" }, + { + "name": "mask-black" + }, { "name": "blue" }, { "name": "blue-1" }, + { + "name": "mask-blue" + }, { "name": "can-connector" }, @@ -59,60 +65,90 @@ { "name": "grey-1" }, + { + "name": "mask-grey" + }, { "name": "orange" }, { "name": "orange-1" }, + { + "name": "mask-orange" + }, { "name": "red" }, { "name": "red-1" }, + { + "name": "mask-red" + }, { "name": "redws" }, { "name": "redws-1" }, + { + "name": "mask-redws" + }, { "name": "yellow" }, { "name": "yellow-1" }, + { + "name": "mask-yellow" + }, { "name": "green" }, { "name": "green-1" }, + { + "name": "mask-green" + }, { "name": "greenys" }, { "name": "greenys-1" }, + { + "name": "mask-greenys" + }, { "name": "darkblue" }, { "name": "darkblue-1" }, + { + "name": "mask-darkblue" + }, { "name": "frezon" }, { "name": "frezon-1" }, + { + "name": "mask-frezon" + }, { "name": "water_vapor" }, { "name": "water_vapor-1" }, + { + "name": "mask-water_vapor" + }, { "name": "scrubber-connector" }, @@ -136,6 +172,21 @@ }, { "name": "redsur-1" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/RobustToolbox b/RobustToolbox index dad56301e1..3136118b53 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit dad56301e115f79f03852e3a8dfe485f0db667c3 +Subproject commit 3136118b5338ef2d9580178caf5c723e65eb76e7 diff --git a/Tools/generate_heat_distortion_textures.py b/Tools/generate_heat_distortion_textures.py new file mode 100644 index 0000000000..2c8c3dd106 --- /dev/null +++ b/Tools/generate_heat_distortion_textures.py @@ -0,0 +1,54 @@ +#This is script that was used to generate textures for heatdistortion + +from pyfastnoiselite.pyfastnoiselite import FastNoiseLite, NoiseType, FractalType +from PIL import Image +import math + +def generate_noise_image(output_filename="perlin_noise.png"): + width = 512 + height = 512 + + noise = FastNoiseLite() + + noise.noise_type = NoiseType.NoiseType_Perlin + noise.fractal_type = FractalType.FractalType_FBm + noise.fractal_octaves = 4 + noise.frequency = 0.01 + + image = Image.new("RGBA", (width, height)) + pixels = image.load() + + for x in range(width): + for y in range(height): + value = (noise.get_noise(x, y) + 1.0) / 2.0 + color_val = int(value * 255) + color_val = max(0, min(255, color_val)) + + pixels[x, y] = (color_val, color_val, color_val, 255) + image.save(output_filename) + print(f"Success! Image exported to: {output_filename}") + +def generate_soft_circle_texture(output_filename="soft_circle.png"): + width = 64 + height = 64 + + image = Image.new("RGBA", (width, height)) + pixels = image.load() + + center_x = width / 2.0 + center_y = height / 2.0 + max_dist = width / 2.0 + + for x in range(width): + for y in range(height): + dist = math.sqrt((x - center_x)**2 + (y - center_y)**2) + fade = 1.0 - max(0.0, min(1.0, dist / max_dist)) + alpha_val = fade * fade * (3.0 - 2.0 * fade) + alpha_byte = int(alpha_val * 255) + pixels[x, y] = (255, 255, 255, alpha_byte) + image.save(output_filename) + print(f"Success! Image exported to: {output_filename}") + +if __name__ == "__main__": + generate_noise_image() + generate_soft_circle_texture()