diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 094a5b1557c..2c74388c0c7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,4 +8,4 @@ /Resources/Maps/** @Morb0 @DIMMoon1 @kvant8 # 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 e40e102e442..e1237cdeb35 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 @@ -98,10 +130,20 @@ jobs: cookiejar="$(mktemp)" trap 'rm -f "$cookiejar"' EXIT - login_token=$(curl -sS -c "$cookiejar" \ + curl_api() { + curl --silent --show-error \ + --retry 3 \ + --retry-delay 5 \ + --retry-all-errors \ + --connect-timeout 20 \ + --max-time 180 \ + "$@" + } + + login_token=$(curl_api -c "$cookiejar" \ --data "action=query&meta=tokens&type=login&format=json" "$API" | jq -r '.query.tokens.logintoken') - curl -sS -c "$cookiejar" -b "$cookiejar" \ + curl_api -c "$cookiejar" -b "$cookiejar" \ --data-urlencode "action=login" \ --data-urlencode "lgname=$USER" \ --data-urlencode "lgpassword=$PASS" \ @@ -109,7 +151,7 @@ jobs: --data-urlencode "format=json" \ "$API" > /dev/null - token=$(curl -sS -b "$cookiejar" \ + token=$(curl_api -b "$cookiejar" \ --data "action=query&meta=tokens&format=json" "$API" | jq -r '.query.tokens.csrftoken') edit_page_file() { @@ -118,7 +160,7 @@ jobs: local textfile="$3" local token="$4" - curl -sS -b "$cookiejar" \ + curl_api -b "$cookiejar" \ --data-urlencode "action=edit" \ --data-urlencode "title=$title" \ --data-urlencode "summary=$summary" \ @@ -136,7 +178,7 @@ jobs: local text="$3" local token="$4" - curl -sS -b "$cookiejar" \ + curl_api -b "$cookiejar" \ --data-urlencode "action=edit" \ --data-urlencode "title=$title" \ --data-urlencode "summary=$summary" \ @@ -160,7 +202,7 @@ jobs: titles="${titles%|}" local resp - resp=$(curl -sS -b "$cookiejar" \ + resp=$(curl_api -b "$cookiejar" \ --data-urlencode "action=query" \ --data-urlencode "format=json" \ --data-urlencode "formatversion=2" \ @@ -183,16 +225,12 @@ jobs: ) } - count_lines() { - awk 'END { print NR }' - } - files=() while IFS= read -r -d '' file; do files+=("$file") done < <(find "$BASE" -type f -name '*.json' -print0) - batch_size=50 + batch_size=25 for ((i=0; i<${#files[@]}; i+=batch_size)); do batch_files=("${files[@]:i:batch_size}") diff --git a/Content.Benchmarks/ComponentQueryBenchmark.cs b/Content.Benchmarks/ComponentQueryBenchmark.cs index bfe367790a6..c9aebf7ba36 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 void Setup() 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 8d4929c47ff..dac79e03760 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 async Task SetupAsync() { 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 aa759c35fc6..58c834c0aef 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 async Task SetupAsync() { 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 bb2a22312ea..fcde4feb646 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 async Task SetupAsync() { 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 9ed30373d1c..f2988693663 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 async Task SetupAsync() { 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 cef5bc10c7f..18366306d7a 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 async Task SetupAsync() { 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 261d408aacf..9ea95c84b64 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 void Setup() 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 51a013539e0..af1ec8d9efc 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 void Setup() #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 e3d377ccb3e..99e032d0b58 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 void Setup() { 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 9b878eac40c..b6d5427480c 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 async Task SetupAsync() { 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 81b1c087d88..31f65dc78da 100644 --- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs +++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs @@ -65,7 +65,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _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 bb44ae26155..2169277f0d3 100644 --- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs +++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs @@ -216,7 +216,7 @@ private void SubmitData() 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 00000000000..e71f212786c --- /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 f0e4b13356c..e192a66fccb 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 AtmosAlarmEntryContainer(NetEntity uid, EntityCoordinates? coordinates) _entManager = IoCManager.Resolve(); _cache = IoCManager.Resolve(); + _atmosphere = _entManager.System(); NetEntity = uid; Coordinates = coordinates; @@ -149,7 +152,7 @@ public void UpdateEntry(AtmosAlertsComputerEntry entry, bool isFocus, AtmosAlert 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 0ce0c9c880a..cf9a89d1a56 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 void UpdateEntry(AtmosMonitoringConsoleEntry updatedData, bool isFocus) 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 d950108922c..24f3ffaa261 100644 --- a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -37,6 +37,20 @@ public override bool IsMixtureOxidizer(GasMixture mixture, float epsilon = Atmos 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 @@ protected override float GetHeatCapacityCalculation(float[] moles, bool space) // 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 696e7939f60..bae70ea2bdc 100644 --- a/Content.Client/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Client/Atmos/EntitySystems/GasTankSystem.cs @@ -11,6 +11,12 @@ public override void Initialize() 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 00000000000..a80208620be --- /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 00000000000..72eff2b4090 --- /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 00000000000..d8493198757 --- /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 37298b95fd8..4d909c9fc87 100644 --- a/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs +++ b/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs @@ -72,17 +72,7 @@ public GasTileVisibleGasOverlay() { 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 cae184edbb7..13513e2a957 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 override void Initialize() 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 3a5df3f9a88..c8f2091d133 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) + { + } + + protected override void Open() { - [ViewVariables] - private GasAnalyzerWindow? _window; - - public GasAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - } - - protected override void Open() - { - base.Open(); - - _window = this.CreateWindowCenteredLeft(); - _window.OnClose += Close; - } - - protected override void ReceiveMessage(BoundUserInterfaceMessage message) - { - if (_window == null) - return; - if (message is not GasAnalyzerUserMessage cast) - return; - _window.Populate(cast); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - _window?.Dispose(); - } + base.Open(); + + _window = this.CreateWindowCenteredLeft(); + _window.OnClose += Close; + } + + protected override void ReceiveMessage(BoundUserInterfaceMessage message) + { + if (_window == null) + return; + + if (message is not GasAnalyzerUserMessage cast) + return; + + _window.Populate(cast); } } diff --git a/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs b/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs index 63b4e6b0c6f..94a9f237453 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.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 @@ private void GenerateGasDisplay(GasMixEntry gasMix, Control parent) 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 0456426b1fc..56be898a01d 100644 --- a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs @@ -74,7 +74,7 @@ protected override void UpdateState(BoundUserInterfaceState state) _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 9929751b22d..92bfc677eb3 100644 --- a/Content.Client/Audio/AmbientSoundSystem.cs +++ b/Content.Client/Audio/AmbientSoundSystem.cs @@ -235,8 +235,6 @@ private static bool Callback( /// 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 @@ private void ProcessNearbyAmbience(TransformComponent playerXform) 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 @@ private void ProcessNearbyAmbience(TransformComponent playerXform) 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 7a9439c9df1..28443744525 100644 --- a/Content.Client/Audio/AmbientSoundTreeSystem.cs +++ b/Content.Client/Audio/AmbientSoundTreeSystem.cs @@ -23,8 +23,7 @@ protected override Box2 ExtractAabb(in ComponentTreeEntry var pos = XformSystem.GetRelativePosition( entry.Transform, - entry.Component.TreeUid.Value, - GetEntityQuery()); + 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 fba936ee58a..cf1e09bf306 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 @@ private IEnumerable AllMarkings(Entity en } } - 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 @@ private void ApplyMarkings(Entity ent, EntityUid t 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 @@ private void ApplyMarkings(Entity ent, EntityUid t 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 @@ private void ApplyMarkings(Entity ent, EntityUid t _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 @@ private void ApplyMarkings(Entity ent, EntityUid t 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 @@ private void RemoveMarkings(Entity ent, EntityUid 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 ecebe167277..a179f14a3b6 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 @@ private void OnBoxEffect(PlayBoxEffectMessage msg) 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 @@ private void OnBoxEffect(PlayBoxEffectMessage msg) 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 64d809c0c5f..25cfb1ae82f 100644 --- a/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs +++ b/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs @@ -4,6 +4,7 @@ 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 @@ protected override void Open() _menu.OpenOverMouseScreenPosition(); } - public override void Update() { if (_menu == null) @@ -32,7 +34,7 @@ public override void Update() 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 @@ private IEnumerable ConvertToButtons( ) { 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 @@ private void SendIdentitySelect(NetEntity identityId) { 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 8834f023f41..ae3487dc87a 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 1a6db7a1b65..9c374766764 100644 --- a/Content.Client/Clothing/ClientClothingSystem.cs +++ b/Content.Client/Clothing/ClientClothingSystem.cs @@ -223,7 +223,7 @@ protected override void OnGotEquipped(EntityUid uid, ClothingComponent component { 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 b64a9626046..de427a1e43d 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 @@ private async Task RunAsync() 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 @@ private async Task RunAsync() try { - entity = _entityManager.SpawnEntity(proto.ID, new EntityCoordinates(previewGrid.Owner, default)); - - await WaitForEntityAppearanceAsync(entity); - ApplyPrototypeAppearance(entity, proto); - await WaitForEntityAppearanceAsync(entity, 1); + 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); + } + else + { + entity = SpawnIconEntity(proto); + } await _renderService.Export(entity, Direction.South, outputDir / $"{proto.ID}.png"); exported++; @@ -241,4 +255,128 @@ private void ApplyPrototypeAppearance(EntityUid entity, EntityPrototype prototyp 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 848279b9dbe..cd055484692 100644 --- a/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs +++ b/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs @@ -3,8 +3,8 @@ 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 void Shutdown() _rsiStateImageCache.Clear(); + foreach (var image in _textureImageCache.Values) + { + image.Dispose(); + } + + _textureImageCache.Clear(); + if (_control == null) return; @@ -360,7 +368,7 @@ private static float GetNextBoundaryDelta( 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 @@ private bool TryExportFrameDirect( 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 @@ private bool TryExportFrameDirect( 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 @@ private bool TryExportFrameDirect( private static void BlitImage( Image sourceImage, - Rectangle sourceRect, + PixelRect sourceRect, Color modulation, Span destination, Vector2i destinationSize, @@ -519,14 +529,17 @@ private bool TryGetLayerImage( 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 @@ private bool TryGetLayerImage( 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 6986e1c8682..14075caba31 100644 --- a/Content.Client/DisplacementMap/DisplacementMapSystem.cs +++ b/Content.Client/DisplacementMap/DisplacementMapSystem.cs @@ -2,14 +2,18 @@ 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 bool TryAddDisplacement( 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 5802059537f..407cf3e7c1a 100644 --- a/Content.Client/DoAfter/DoAfterSystem.cs +++ b/Content.Client/DoAfter/DoAfterSystem.cs @@ -50,9 +50,7 @@ public override void Update(float frameTime) 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 a445378d98d..53c5061f2a1 100644 --- a/Content.Client/Doors/AirlockSystem.cs +++ b/Content.Client/Doors/AirlockSystem.cs @@ -61,6 +61,11 @@ private void OnComponentStartup(EntityUid uid, AirlockComponent comp, ComponentS 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 0f6537eeadd..cba9ba1d49b 100644 --- a/Content.Client/Doors/DoorSystem.cs +++ b/Content.Client/Doors/DoorSystem.cs @@ -18,13 +18,14 @@ public override void Initialize() { 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 @@ protected override void OnComponentInit(Entity ent, ref Component }; } + 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 @@ private void OnAppearanceChange(Entity entity, ref AppearanceChan 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 @@ private void UpdateAppearanceForDoorState(Entity entity, SpriteCo 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 @@ private void UpdateAppearanceForDoorState(Entity entity, SpriteCo 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; + + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey)) return; - _animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.AnimationKey); + _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 692232776aa..44d82f3794b 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 @@ protected override void FrameUpdate(FrameEventArgs args) 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 @@ protected override void Draw(in OverlayDrawArgs args) 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 c5fab75e7dd..2e1f8157aa4 100644 --- a/Content.Client/Drunk/DrunkSystem.cs +++ b/Content.Client/Drunk/DrunkSystem.cs @@ -3,6 +3,7 @@ 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 override void Initialize() 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 @@ private void OnStatusRemoved(Entity entity, ref Stat 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 8226b3eb05c..aa1dfaebe22 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -123,7 +123,6 @@ public override void Init() _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 dd390cc2d4f..5b2b52b1b74 100644 --- a/Content.Client/FeedbackPopup/ClientFeedbackManager.cs +++ b/Content.Client/FeedbackPopup/ClientFeedbackManager.cs @@ -31,7 +31,7 @@ public void Open() /// public override void Display(List>? prototypes) { - if (prototypes == null || !NetManager.IsClient) + if (prototypes == null) return; var count = _displayedPopups.Count; @@ -42,9 +42,6 @@ public override void Display(List>? prototypes) /// 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 25982b901c2..cb618484e22 100644 --- a/Content.Client/Holopad/HolopadWindow.xaml.cs +++ b/Content.Client/Holopad/HolopadWindow.xaml.cs @@ -173,9 +173,9 @@ public void UpdateState(Dictionary holopads) 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 a1c47ca6bed..4b8ec7be794 100644 --- a/Content.Client/IconSmoothing/IconSmoothSystem.cs +++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs @@ -18,6 +18,8 @@ public sealed partial class IconSmoothSystem : EntitySystem { [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 @@ public override void FrameUpdate(float frameTime) { 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 @@ public override void FrameUpdate(float frameTime) continue; } - DirtyNeighbours(uid, comp: null, xform, smoothQuery); + DirtyNeighbours(uid, comp: null, xform); } // Next, update actual sprites. @@ -132,19 +131,16 @@ public override void FrameUpdate(float frameTime) _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 @@ private void OnAnchorChanged(EntityUid uid, IconSmoothComponent component, ref A _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 @@ private void CalculateNewSprite(EntityUid uid, // 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 @@ private void CalculateNewSprite(EntityUid uid, { 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 @@ private void CalculateNewSprite(EntityUid uid, 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 @@ private void CalculateNewSprite(EntityUid uid, 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 @@ private void CalculateNewSprite(EntityUid uid, 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 @@ private void CalculateNewSprite(EntityUid uid, } private void CalculateNewSpriteDiagonal(Entity? gridEntity, IconSmoothComponent smooth, - Entity sprite, TransformComponent xform, EntityQuery smoothQuery) + Entity sprite, TransformComponent xform) { if (gridEntity == null) { @@ -320,7 +312,7 @@ private void CalculateNewSpriteDiagonal(Entity? gridEntity, Ic 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 @@ private void CalculateNewSpriteDiagonal(Entity? gridEntity, Ic } } - 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 @@ private void CalculateNewSpriteCardinal(Entity? gridEntity, Ic 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 @@ private void CalculateNewSpriteCardinal(Entity? gridEntity, Ic 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 @@ private bool MatchingEntity(IconSmoothComponent smooth, AnchoredEntitiesEnumerat 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 @@ private void CalculateNewSpriteCorners(Entity? gridEntity, Ico 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 a1af9f9e202..7ff8dd150f6 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 @@ private void HighlightTargets() 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 73f837d0d89..8cbb33d165e 100644 --- a/Content.Client/Inventory/ClientInventorySystem.cs +++ b/Content.Client/Inventory/ClientInventorySystem.cs @@ -73,8 +73,8 @@ public override void Update(float frameTime) 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 @@ private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent arg 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 a5c8f6a85cb..84f1d7835a9 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 f6688a63af4..adb115d3520 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.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 @@ 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 LatheMenu() 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 4919a5e8f2e..0f583898dec 100644 --- a/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs +++ b/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs @@ -58,7 +58,7 @@ private void OnMachineExamined(Entity ent, ref Client 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 51ff5c1a00e..211225119ba 100644 --- a/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs +++ b/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs @@ -2,9 +2,8 @@ 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 CryoPodWindow() { 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 void Populate(CryoPodUserMessage msg) { 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 eb60e4fbb6d..c806ae5aed8 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 6520d98ac93..3e1084bebb1 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 00000000000..96a63cc694c --- /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 24632b71fa2..00000000000 --- 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 73d45a17a98..23752fbb9b3 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 @@ private void HighlightTargets() 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 1319a3d74ef..f0cc9b2f595 100644 --- a/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs +++ b/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs @@ -4,7 +4,6 @@ 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 f4f109adc7c..f719b7b5b88 100644 --- a/Content.Client/Radiation/Systems/RadiationSystem.cs +++ b/Content.Client/Radiation/Systems/RadiationSystem.cs @@ -5,7 +5,7 @@ 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 e7d01713e59..3408a2af26b 100644 --- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs +++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs @@ -74,8 +74,7 @@ public override void FrameUpdate(float frameTime) 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 52398921d48..6b946f9cf8d 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 517027d0821..09e6e387f78 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 39b0758bf17..fd888fb6ddb 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 @@ public sealed class SingularityOverlay : Overlay, IEntityEventSubscriber [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 @@ public sealed class SingularityOverlay : Overlay, IEntityEventSubscriber private readonly ShaderInstance _shader; + private bool _reducedMotion; + public SingularityOverlay() { IoCManager.InjectDependencies(this); @@ -35,6 +40,8 @@ public SingularityOverlay() _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 @@ public SingularityOverlay() 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 @@ protected override void Draw(in OverlayDrawArgs args) /// 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 b3474110409..bdc49bd25f8 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 override void Initialize() { 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 85c6b8e0660..4f58919d780 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 00000000000..cb6e42a0d72 --- /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 d8236604bff..36eec4a6713 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 @@ protected override void Open() 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 df190154f83..097d54d581d 100644 --- a/Content.Client/Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs +++ b/Content.Client/Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs @@ -2,7 +2,7 @@ namespace Content.Client.Stylesheets.Stylesheets; -public sealed partial class NanotrasenStylesheet +public partial class NanotrasenStylesheet { public override ColorPalette PrimaryPalette => Palettes.Navy; public override ColorPalette SecondaryPalette => Palettes.Slate; diff --git a/Content.Client/SubFloor/TrayScannerSystem.cs b/Content.Client/SubFloor/TrayScannerSystem.cs index 4c67890f6a5..a23d67a98b5 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 override void Update(float frameTime) // 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 override void Update(float frameTime) { 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 override void Update(float frameTime) 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 override void Update(float frameTime) } 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 330cb51dcc8..a25d89543d3 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 959a60ef4f8..0cc207dd89d 100644 --- a/Content.Client/UserInterface/Controls/RadialMenu.cs +++ b/Content.Client/UserInterface/Controls/RadialMenu.cs @@ -228,7 +228,6 @@ public void ReturnToPreviousLayer() /// 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 2b43f2397de..d55b4acf55e 100644 --- a/Content.Client/UserInterface/Controls/SlotControl.cs +++ b/Content.Client/UserInterface/Controls/SlotControl.cs @@ -9,7 +9,6 @@ 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 19e578fda38..52951b8eb6b 100644 --- a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs +++ b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs @@ -46,7 +46,7 @@ protected override void UpdateState(BoundUserInterfaceState state) 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 8094a77b6be..cd18c3e6967 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 77b8531888f..e0bf99d1cb3 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -34,6 +34,7 @@ public sealed class VerbSystem : SharedVerbSystem [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 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true 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 89715089695..c792910fcf8 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs @@ -32,7 +32,7 @@ public override void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vect 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 override void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vect } _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 420e18748f0..5ecbaf63b8c 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 @@ protected override void DoDamageEffect(List targets, EntityUid? user, 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 4b63c059913..a7d8343fb29 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 00000000000..50da744ecfd --- /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 00000000000..70cd904f3fe --- /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 00000000000..e47b99b1463 --- /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 00000000000..d1975399973 --- /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 00000000000..75a22e93341 --- /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 00000000000..06f1e90acb1 --- /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 00000000000..00fe7a952f8 --- /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 00000000000..4acbe8e87b5 --- /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 00000000000..6ecde6fb6df --- /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 00000000000..c6525192c57 --- /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 00000000000..2afdeec8ef5 --- /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 00000000000..8d3a88bb7cf --- /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 00000000000..55903cf4a27 --- /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 00000000000..c474d0c2d55 --- /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 00000000000..1586c52e03f --- /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 00000000000..eaff1eae8ad --- /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 00000000000..f7f810eda41 --- /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 00000000000..0539a6b54ef --- /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 00000000000..c52bcac37e7 --- /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 00000000000..bf039c597ce --- /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 00000000000..878748949fd --- /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 b457d4a40bf..189e37fa39c 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -25,7 +25,6 @@ public static readonly (string cvar, string value)[] TestCvars = (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 a0c8c775b1a..02796739100 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 @@ public sealed class AccessReaderTest - 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); - }); + 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 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 + _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 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 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); + // 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); }); - await pair.CleanReturnAsync(); + _system.TryClearAccesses(reader); + _system.ClearDenyTags(reader); } } diff --git a/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs b/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs index 45addff00bf..9680e48939e 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 @@ 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 async Task TestActionDetach() 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 0dbec0c83ab..1fa005496e1 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 async Task TestCombatActionsAdded() // 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 772af337a1a..71cb0fd7ed0 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 async Task AddAndGetSingleLog() 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 @@ await PoolManager.WaitUntil(server, async () => 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 @@ await PoolManager.WaitUntil(server, async () => 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 @@ await PoolManager.WaitUntil(server, async () => 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,42 +191,26 @@ await PoolManager.WaitUntil(server, async () => Assert.That(logs.First().Players, Does.Contain(playerGuid)); return true; }); - await pair.CleanReturnAsync(); } [Test] - public async Task PreRoundAddAndGetSingle() + public async Task DuplicatePlayerDoesNotThrowTest() { - 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(); - var sSystems = server.ResolveDependency(); - + var sPlayers = server.ResolveDependency(); var sAdminLogSystem = server.ResolveDependency(); - var sGamerTicker = sSystems.GetEntitySystem(); var guid = Guid.NewGuid(); await server.WaitPost(() => { - sAdminLogSystem.Add(LogType.Unknown, $"test log: {guid}"); - }); + var player = sPlayers.Sessions.Single(); - await server.WaitPost(() => - { - sGamerTicker.StartRound(true); + sAdminLogSystem.Add(LogType.Unknown, $"{player} {player} test log: {guid}"); }); - SharedAdminLog log = default; - await PoolManager.WaitUntil(server, async () => { var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter @@ -243,35 +223,18 @@ await PoolManager.WaitUntil(server, async () => return false; } - log = logs.First(); return true; }); - - var filter = new LogFilter - { - Round = sGamerTicker.RoundId, - Search = log.Message, - Types = new HashSet { log.Type }, - }; - - await foreach (var json in sDatabase.GetAdminLogsJson(filter)) - { - var root = json.RootElement; - - Assert.That(root.TryGetProperty("guid", out _), Is.True); - - json.Dispose(); - } - await pair.CleanReturnAsync(); } [Test] - public async Task DuplicatePlayerDoesNotThrowTest() + public async Task DuplicatePlayerIdDoesNotThrowTest() { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sPlayers = server.ResolveDependency(); + var sAdminLogSystem = server.ResolveDependency(); var guid = Guid.NewGuid(); @@ -280,7 +243,7 @@ await server.WaitPost(() => { var player = sPlayers.Sessions.Single(); - sAdminLogSystem.Add(LogType.Unknown, $"{player} {player} test log: {guid}"); + sAdminLogSystem.Add(LogType.Unknown, $"{player:first} {player:second} test log: {guid}"); }); await PoolManager.WaitUntil(server, async () => @@ -297,30 +260,44 @@ await PoolManager.WaitUntil(server, async () => return true; }); - - await pair.CleanReturnAsync(); - Assert.Pass(); } +} + +public sealed class PreRoundAddTests : GameTest +{ + public override PoolSettings PoolSettings => new PoolSettings + { + Dirty = true, + InLobby = true, + AdminLogsEnabled = true + }; [Test] - public async Task DuplicatePlayerIdDoesNotThrowTest() + public async Task PreRoundAddAndGetSingle() { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; - var sPlayers = server.ResolveDependency(); + var sDatabase = server.ResolveDependency(); + var sSystems = server.ResolveDependency(); var sAdminLogSystem = server.ResolveDependency(); + var sGamerTicker = sSystems.GetEntitySystem(); var guid = Guid.NewGuid(); await server.WaitPost(() => { - var player = sPlayers.Sessions.Single(); + sAdminLogSystem.Add(LogType.Unknown, $"test log: {guid}"); + }); - sAdminLogSystem.Add(LogType.Unknown, $"{player:first} {player:second} test log: {guid}"); + await server.WaitPost(() => + { + sGamerTicker.StartRound(true); }); + SharedAdminLog log = default; + await PoolManager.WaitUntil(server, async () => { var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter @@ -333,10 +310,25 @@ await PoolManager.WaitUntil(server, async () => return false; } + log = logs.First(); return true; }); - await pair.CleanReturnAsync(); - Assert.Pass(); + var filter = new LogFilter + { + Round = sGamerTicker.RoundId, + Search = log.Message, + Types = new HashSet { log.Type }, + }; + + await foreach (var json in sDatabase.GetAdminLogsJson(filter)) + { + var root = json.RootElement; + + Assert.That(root.TryGetProperty("guid", out _), Is.True); + + json.Dispose(); + } } + } diff --git a/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs index 6f907f425e3..2517defe96e 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 @@ await PoolManager.WaitUntil(server, async () => 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 5a58757d531..55b36ebae1c 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 @@ await PoolManager.WaitUntil(server, async () => return false; }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs index b74c35ba111..2eaa0738199 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 @@ public sealed class AlarmThresholdTest [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 @@ await server.WaitAssertion(() => 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 6481e377c95..d44bfe7ae44 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 @@ 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 @@ await server.WaitPost(() => // 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 @@ await server.WaitPost(() => } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs b/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs index 07caf447bd9..eda9061281c 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 @@ await server.WaitAssertion(() => } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs b/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs index 1cb8fd8b6f2..70bbd0ac0cd 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 @@ await server.WaitAssertion(() => Assert.That(a.GetMoles(Gas.Oxygen), Is.EqualTo(50)); }); }); - - await pair.CleanReturnAsync(); } [Test] @@ -69,7 +68,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => 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 45ccddfad98..9b43e85396a 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 @@ 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 @@ await server.WaitPost(() => // 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 ac80f4a1050..7269b1b4739 100644 --- a/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs @@ -37,7 +37,7 @@ public async Task SetUp() { 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 async Task GasSpecificHeats_Agree() 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 a7274879404..a3f3c1bcc37 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 @@ await server.WaitAssertion(() => 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 560dfbf64a1..3d885186f18 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 @@ await server.WaitAssertion(() => 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 d9cce764ab7..444bd159f38 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 b42f42922a4..9c12da3fe8a 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 @@ public sealed partial class BuckleTest [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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => Assert.That(buckle.Buckled); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs index df85e61550a..0c8fc2f0fb4 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 @@ 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 @@ await server.WaitAssertion(() => } }); }); - - 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => } } }); - - await pair.CleanReturnAsync(); } /// @@ -144,7 +139,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => } mapSystem.DeleteMap(mapId); }); - - await pair.CleanReturnAsync(); } [TestPrototypes] @@ -233,7 +226,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => 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 @@ await pair.Server.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs b/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs index 59948c8b175..9ac45549a29 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 async Task ReagentDataIsSerializable() 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 5b5829d386e..f188fd0f66e 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 @@ await server.WaitAssertion(() => Is.EqualTo((FixedPoint2) 30)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs index 6f50f54103e..ffaca012a34 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 @@ await server.WaitAssertion(() => Assert.That(oil, Is.EqualTo(oilQuantity)); }); }); - - await pair.CleanReturnAsync(); } // This test mimics current behavior @@ -97,7 +96,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => .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 @@ await server.WaitAssertion(() => 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 0037670556f..e720387425e 100644 --- a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs @@ -5,6 +5,7 @@ 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 @@ public sealed class TryAllReactionsTest [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 @@ await server.WaitAssertion(() => server.EntMan.DeleteEntity(beaker); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs b/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs index e2bff03501a..037224fec55 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++) - { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true - }); - var server = pair.Server; + var pair = Pair; + var server = pair.Server; - var sPlayerManager = server.ResolveDependency(); - var eui = server.ResolveDependency(); + 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 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 aaac421ed1a..fb20cd90388 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 @@ 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 @@ public sealed class ClickableTest [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 @@ await server.WaitPost(() => }); // Let client sync up. - await pair.RunTicksSync(5); + await RunUntilSynced(); var hit = false; var clientEnt = clientEntManager.GetEntity(serverEntManager.GetNetEntity(serverEnt)); @@ -89,8 +90,6 @@ await server.WaitPost(() => 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 9edc115eee2..c21bcf922ae 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 @@ await server.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs index 3fa7e64f1a4..566edd47cae 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 @@ 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 @@ await server.WaitAssertion(() => // 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 d430325e31b..eb18d8e1ae0 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 @@ 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 @@ await server.WaitPost(() => 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 5f77af1b104..2d84eebf962 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 @@ public async Task PardonTest() 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 308f2797c44..15d93c40aed 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 @@ public sealed class RejuvenateTest [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 @@ await server.WaitAssertion(() => 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 72a05b5246f..bcacc8011bc 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 @@ await server.WaitAssertion(() => 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 32f1f6128e3..f78bd4949ea 100644 --- a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs +++ b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -21,7 +22,7 @@ namespace Content.IntegrationTests.Tests.Commands; [TestFixture] -public sealed class SuicideCommandTests +public sealed class SuicideCommandTests : GameTest { [TestPrototypes] @@ -57,6 +58,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 @@ -64,12 +72,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(); @@ -104,8 +107,6 @@ await server.WaitAssertion(() => !ghostComp.CanReturnToBody); }); }); - - await pair.CleanReturnAsync(); } /// @@ -115,12 +116,7 @@ await server.WaitAssertion(() => [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(); @@ -166,8 +162,6 @@ await server.WaitAssertion(() => Assert.That(damageableSystem.GetTotalDamage(player), Is.EqualTo(lethalDamageThreshold)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -177,12 +171,7 @@ await server.WaitAssertion(() => [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(); @@ -217,8 +206,6 @@ await server.WaitAssertion(() => !ghostComp.CanReturnToBody); }); }); - - await pair.CleanReturnAsync(); } @@ -228,12 +215,7 @@ await server.WaitAssertion(() => [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(); @@ -292,8 +274,6 @@ await server.WaitAssertion(() => Assert.That(damageableSystem.GetAllDamage((player, damageableComp)).DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -303,12 +283,7 @@ await server.WaitAssertion(() => [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(); @@ -367,7 +342,5 @@ await server.WaitAssertion(() => 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 ebeea7f391d..224a5677c05 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 @@ 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 @@ await server.WaitPost(() => 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 c59b42d6389..8c68e5b1926 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 @@ 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 @@ private bool IsValid(IGraphAction action, IPrototypeManager protoMan, out string [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 @@ await server.WaitPost(() => }); 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 @@ await server.WaitPost(() => }); 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 9441443b225..49cc76e9458 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 @@ 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 @@ public sealed class ConstructionPrototypeTest [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 @@ await server.WaitAssertion(() => $"Invalid deconstruction node \"{target}\" on graph \"{graph.ID}\" for construction entity \"{proto.ID}\"!"); } }); - - await pair.CleanReturnAsync(); } [Test] @@ -59,7 +58,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => $"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 37c4b0c9b57..cb7e4016018 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 @@ namespace Content.IntegrationTests.Tests { - public sealed class ContainerOcclusionTest + public sealed class ContainerOcclusionTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -34,7 +35,7 @@ public sealed class ContainerOcclusionTest [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 @@ await client.WaitAssertion(() => 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 @@ await client.WaitAssertion(() => 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 @@ await client.WaitAssertion(() => Assert.That(light.ContainerOccluded); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/ContrabandTest.cs b/Content.IntegrationTests/Tests/ContrabandTest.cs index c52ef293e1e..9f0fb1d4995 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 @@ 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 @@ await client.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs index 9777054625f..758bbe0a50b 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 @@ public sealed class DamageableTest [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 @@ await server.WaitAssertion(() => 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 b359f055690..c4179135cdf 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 async Task ValidateMobThresholds(string protoKey) 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 09be373a4c1..b4a278529da 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 @@ await server.WaitAssertion(() => #pragma warning restore NUnit2041 } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/DeleteInventoryTest.cs b/Content.IntegrationTests/Tests/DeleteInventoryTest.cs index 49d54bbecfc..bbaf11a0524 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 @@ 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 @@ await server.WaitAssertion(() => // 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 f5010eefdc1..a83891b8fcc 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 @@ await server.WaitAssertion(() => // 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 70baaea95a1..47f44ad4969 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 @@ await server.WaitAssertion(() => // 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 df98294ee96..0aa0449682d 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 @@ 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 @@ await server.WaitAssertion(() => 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 4460affedf0..da56a43a4ac 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 @@ await server.WaitAssertion(() => 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 c62b46ab6d4..7a2a880bae5 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 @@ 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 @@ await server.WaitAssertion(() => } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs index fdc0e1a4d4a..6ecdffc58c9 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 @@ public sealed class DeviceNetworkTest [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 @@ await server.WaitAssertion(() => { 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 @@ await server.WaitAssertion(() => { 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 @@ await server.WaitAssertion(() => { 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 52b669b09da..f7b20819c24 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 @@ private static void Flush(EntityUid unitEntity, DisposalUnitComponent unit, bool [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 @@ await server.WaitAssertion(() => // 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 32f8b58542b..d9619076e0f 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 @@ public override DoAfterEvent Clone() [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 @@ await server.WaitPost(() => } }); }); - - 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 @@ await server.WaitPost(() => 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 @@ await server.WaitPost(() => await server.WaitRunTicks(3); Assert.That(ev.Cancelled); - - await pair.CleanReturnAsync(); } /// @@ -124,7 +119,7 @@ await server.WaitPost(() => [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 @@ await server.WaitPost(() => 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 69fe66039b8..af03de169f5 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 @@ public sealed class AirlockTest [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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => { 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 62197bb3197..dcb5d315dcf 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 @@ 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 @@ await client.WaitAssertion(() => proto.ID); } }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 9b0e7729f5b..cd45c4df323 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -2,6 +2,8 @@ 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 @@ await server.WaitPost(() => 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 @@ await server.WaitPost(() => Assert.That(entityMan.EntityCount, Is.Zero); }); - - await pair.CleanReturnAsync(); } /// @@ -145,10 +153,7 @@ await server.WaitPost(() => [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 @@ await server.WaitPost(() => } }); - 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 @@ await server.WaitPost(() => Assert.That(sEntMan.EntityCount, Is.Zero); }); - - await pair.CleanReturnAsync(); } /// @@ -230,8 +233,7 @@ await server.WaitPost(() => [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; @@ -246,6 +248,17 @@ public async Task SpawnAndDeleteEntityCountTest() "AnnounceOnSpawn", }; + var excludedPrototypeIds = new HashSet(StringComparer.Ordinal) + { + "MaxCap", + "MaxCapBluespace", + "MaxCapCanister", + "MaxCapSilly", + "MaxCapSmall", + "MaxCapSmallDouble", + "MaxCapSmallEx", + }; + Assert.That(server.CfgMan.GetCVar(CVars.NetPVS), Is.False); var protoIds = server.ProtoMan @@ -253,6 +266,7 @@ public async Task SpawnAndDeleteEntityCountTest() .Where(p => !p.Abstract) .Where(p => !pair.IsTestPrototype(p)) .Where(p => !excluded.Any(p.Components.ContainsKey)) + .Where(p => !excludedPrototypeIds.Contains(p.ID)) .Where(p => p.Categories.All(x => x.ID != SpawnerCategory)) .Select(p => p.ID) .ToList(); @@ -317,8 +331,6 @@ await Assert.MultipleAsync(async () => BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan)); } }); - - await pair.CleanReturnAsync(); } private static string BuildDiffString(IEnumerable oldEnts, IEnumerable newEnts, IEntityManager entMan) @@ -385,14 +397,11 @@ public async Task AllComponentsOneToOneDeleteTest() "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 +454,6 @@ await server.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs b/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs index 01f52f7d838..7a486e68394 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 async Task Validate(string protoKey) 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 99354e16c1d..142ec21ddbe 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 @@ await client.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs b/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs index 1afed38966f..3d9ff61a2d3 100644 --- a/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs @@ -7,12 +7,13 @@ 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 record TestSolutionCase( [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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 d6f9bf35986..c073020e9c5 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 @@ await server.WaitAssertion(() => } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs index ee2d0cb1f7a..71fbefa241f 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 f4447426c77..464b3306f20 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 @@ 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 @@ await server.WaitPost(() => 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 dae3203f9ff..97681ad4695 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 @@ public sealed class HandCuffTest [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 @@ await server.WaitAssertion(() => 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 ef94cf0f003..3473f1bc9c2 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs @@ -2,6 +2,7 @@ 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 @@ public async Task PrototypesHaveKnownComponents() 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 @@ public async Task PrototypesHaveKnownComponents() [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 @@ public async Task IgnoredComponentsExistInTheCorrectPlaces() } } 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 1e307e9447f..3db194eb9e8 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 @@ await client.WaitAssertion(() => 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 b215584c57a..4a059eb1cca 100644 --- a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs @@ -1,6 +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.GameTicking; @@ -14,17 +15,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; @@ -71,6 +74,5 @@ public async Task TestLobbyPlayersValid() Assert.That(pool.Count, Is.EqualTo(0)); 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 b9a02339fba..b8efb64b8bc 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 @@ 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 async Task FailAndStartTest() 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 53165adca07..f9074f7dc60 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 @@ 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; @@ -260,6 +264,5 @@ await server.WaitAssertion(() => }); ticker.SetGamePreset((GamePresetPrototype?) null); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs index 5298403c6bc..ba8b48e6cbc 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); @@ -65,8 +68,6 @@ await server.WaitAssertion(() => { 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 5d7ae8efbf4..521b043c68d 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 @@ await server.WaitAssertion(() => // 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 ecd3edaf6f7..1eef1ab0a12 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,21 +8,21 @@ 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() { - // TSF edit start - var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false - }); - // TSF edit end + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); var gameTicker = server.ResolveDependency().GetEntitySystem(); @@ -49,7 +50,5 @@ await server.WaitAssertion(() => 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 97fe1c87627..318c4b6b425 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 @@ 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 @@ await server.WaitPost(() => $"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 00000000000..34a6d6dd9c0 --- /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 00000000000..5ce2b486fd5 --- /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 00000000000..d71c56c3135 --- /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 00000000000..942254807fb --- /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 00000000000..3d7afccd57e --- /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 00000000000..080c143b8a8 --- /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 00000000000..1a3de265144 --- /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 ee0f7a742df..23be8f028ab 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 async Task TestGib() 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 0951e7e2608..0ef52a12a66 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 @@ public sealed class WeightlessStatusTests [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 @@ await server.WaitAssertion(() => }); await pair.RunTicksSync(10); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/GravityGridTest.cs b/Content.IntegrationTests/Tests/GravityGridTest.cs index 047ec0259a5..10804ea201b 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 @@ public sealed class GravityGridTest [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 @@ await server.WaitAssertion(() => 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 dec2c40c0ac..98b7056d00f 100644 --- a/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs +++ b/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs @@ -3,6 +3,7 @@ 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 @@ some text with a nested control [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 @@ await client.WaitPost(() => 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 13d0ad1497e..016b06abe8f 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 @@ await client.WaitAssertion(() => 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 d5cf75c4634..95f053a1cb7 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 @@ 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 @@ await server.WaitPost(() => 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 @@ await server.WaitPost(() => 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 929a2311599..accdd4667c0 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 @@ public sealed class HumanInventoryUniformSlotsTest [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 @@ await server.WaitAssertion(() => 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 d95992bda2f..4ee65c9c32e 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 @@ 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 async Task NoOrgansWithoutClothing() { 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 async Task NoClothingWithoutOrgans() { 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 1e4a094b797..48eb521c5e7 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 @@ await server.WaitAssertion(() => 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 6ac40e92a1e..e4bd4615c58 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 @@ public sealed class InteractionSystemTests [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 @@ await server.WaitAssertion(() => }); 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 @@ await server.WaitAssertion(() => }); 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 @@ await server.WaitAssertion(() => }); 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 @@ await server.WaitAssertion(() => }); 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 @@ await server.WaitAssertion(() => }); 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 801433ae72b..966f13675d5 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 @@ public sealed class InRangeUnobstructed [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 @@ await server.WaitAssertion(() => 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 62a03b0abee..5231055c694 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 d1535368736..d2f67e98308 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => server.EntMan.DeleteEntity(dummy); }); - - await pair.CleanReturnAsync(); } [TestPrototypes] diff --git a/Content.IntegrationTests/Tests/InventoryHelpersTest.cs b/Content.IntegrationTests/Tests/InventoryHelpersTest.cs index 39761ad0898..c841e233656 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 @@ namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class InventoryHelpersTest + public sealed class InventoryHelpersTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -39,7 +40,7 @@ public sealed class InventoryHelpersTest [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 @@ await server.WaitAssertion(() => #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 c335f8d6c8a..f0a5c042372 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 @@ 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 @@ await server.WaitAssertion(() => } }); }); - - 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 async Task AllLatheRecipesValidTest() 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 b75b81ab3ca..bd35c1f08e4 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 async Task TestStaticFieldValidation() 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 2fa3c9961c4..fb48797616d 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 @@ await client.WaitAssertion(() => 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 3dc2075887a..e59bc269ad7 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 @@ await server.WaitAssertion(() => //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 69d44fd08b3..1b8fa165432 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 async Task TestNoManualEntityLocStrings() } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs b/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs index 05f98c3d198..14aeaf648cc 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 @@ 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 async Task ValidProtoIdsTest() 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 e1533bbb8d7..9554357eced 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 @@ 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 @@ await server.WaitAssertion(() => }); } }); - - await pair.CleanReturnAsync(); } /// @@ -71,7 +70,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => }); } }); - - await pair.CleanReturnAsync(); } /// @@ -111,7 +108,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => }); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs b/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs index 6d48a668a55..43fea6d6c0c 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 @@ await client.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs index be8bad229b4..0bf637cf194 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 @@ 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 @@ await server.WaitPost(() => 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 bd930aef9ee..987fe7ad735 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 @@ await client.WaitPost(() => state.RequestStateChange(); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs b/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs index c81ff6a6980..af5c6c20183 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 f69d3356c27..026d2ecf3b0 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 3c6c372b75e..460146357ca 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 @@ await Assert.MultipleAsync(async () => }); 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 a177869e7f3..43c7dab2b0b 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 @@ await server.WaitAssertion(() => mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs index 32fcf9c1ade..7369fece356 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 @@ 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 async Task TakeRoleAndReturn(bool adminGhost) { 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 @@ await server.WaitPost(() => if (!adminGhost) { // End of the normal player ghost role test - await pair.CleanReturnAsync(); return; } @@ -242,7 +244,5 @@ await server.WaitPost(() => // 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 3a860267e55..26991c08add 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 @@ namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed class GhostTests +public sealed class GhostTests : GameTest { private struct GhostTestData { @@ -45,17 +46,20 @@ public GhostTestData() } } + // 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 async Task TestGridGhostOnDelete() // 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 async Task TestGridGhostOnQueueDelete() // 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 async Task TestGhostGridNotTerminating() // 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 ad4ddc26125..c207e7b4894 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 async Task DeleteAllThenGhost() 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 513049bcad1..cf87b3ea76a 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 @@ await server.WaitAssertion(() => // 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 @@ await server.WaitAssertion(() => Assert.That(transform.MapID, Is.Not.EqualTo(testMap.MapId)); #pragma warning restore NUnit2045 }); - - await pair.CleanReturnAsync(); } /// @@ -130,7 +126,7 @@ await server.WaitAssertion(() => 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 async Task TestGhostOnDelete() await pair.RunTicksSync(5); Assert.That(entMan.HasComponent(player.AttachedEntity), "Player did not become a ghost"); - - await pair.CleanReturnAsync(); } /// @@ -162,7 +156,7 @@ public async Task TestGhostOnDelete() 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 @@ await server.WaitAssertion(() => Assert.That(mind.Comp.VisitingEntity, Is.Null); Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(ghost)); }); - - await pair.CleanReturnAsync(); } /// @@ -220,7 +212,7 @@ await server.WaitAssertion(() => [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 async Task TestGhostToAghost() var mind = entMan.GetComponent(mindId!.Value); Assert.That(mind.VisitingEntity, Is.Null); - - await pair.CleanReturnAsync(); } /// @@ -264,7 +254,7 @@ public async Task TestGhostToAghost() 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 @@ await server.WaitAssertion(() => 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 cebbed8ee8f..e22f6c1e4df 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 db87797553f..fbe79fe044d 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 async Task TestGhostsCanReconnect() 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 async Task TestGhostsCanReconnect() [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 async Task TestDeletedCanReconnect() 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 async Task TestDeletedCanReconnect() [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 async Task TestVisitingGhostReconnect() 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 async Task TestVisitingGhostReconnect() [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 @@ await pair.Server.WaitAssertion(() => 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 @@ await pair.Server.WaitAssertion(() => [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 async Task TestReconnect() 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 35069339baf..08cfbaed5e3 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 @@ 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => }); 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 f0a7268a3d7..3204dc1c2fd 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 @@ 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 async Task ValidateRolePrototypes() } }); - await pair.CleanReturnAsync(); } /// @@ -45,7 +45,7 @@ public async Task ValidateRolePrototypes() [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 async Task ValidateJobPrototypes() Assert.That(((MindRoleComponent)mindComp).JobPrototype, Is.Not.Null); } }); - - await pair.CleanReturnAsync(); } /// @@ -68,7 +66,7 @@ public async Task ValidateJobPrototypes() [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 async Task ValidateRolesHaveMindRoleComp() } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/NPC/NPCTest.cs b/Content.IntegrationTests/Tests/NPC/NPCTest.cs index 064fd6c5bf0..1568f360e7d 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 @@ 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 @@ await server.WaitAssertion(() => 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 c72be944fd3..7b4f048939b 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 @@ public async Task TestConnect() 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 b3955698489..786e14ae425 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 @@ 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 async Task TestPvsCommands() 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 4539ca81a51..26e76376ce3 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 @@ public async Task Test() 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 3e494998451..15c7373b63a 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 @@ await client.WaitPost(() => } 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 a65e7d1fd67..f65bd324a00 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 @@ 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 @@ await pair.Server.WaitAssertion(() => 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 e0588f789c3..54735959636 100644 --- a/Content.IntegrationTests/Tests/PostMapInitTest.cs +++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs @@ -2,6 +2,8 @@ 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 @@ 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 @@ public sealed class PostMapInitTest /// 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 @@ await server.WaitPost(() => mapSystem.DeleteMap(mapId); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -131,16 +136,16 @@ await server.WaitPost(() => /// [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 @@ await server.WaitPost(() => 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 @@ public async Task NoSavedPostMapInitTest(ResPath map) // ReSharper disable once RedundantLogicalConditionalExpressionOperand if (SkipTestMaps && rootedPath.ToString().StartsWith(TestMapsPath, StringComparison.Ordinal)) { - await pair.CleanReturnAsync(); return; // We just pass immediately. } @@ -241,8 +241,6 @@ public async Task NoSavedPostMapInitTest(ResPath map) 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 @@ private bool IsPreInit(ResPath map, } [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 @@ public async Task GameMapsLoadableTest(string mapProto) 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 @@ await server.WaitPost(() => throw new Exception($"Failed to delete map {mapProto}", ex); } }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } @@ -473,37 +465,18 @@ private static int GetCountLateSpawn(List gridUids, IEntityManager 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 @@ public async Task NonGameMapsLoadableTest(ResPath mapPath) { // 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 @@ public async Task NonGameMapsLoadableTest(ResPath mapPath) if (SkipTestMaps && rootedPath.ToString().StartsWith(TestMapsPath, StringComparison.Ordinal)) { - await pair.CleanReturnAsync(); return; } @@ -563,9 +534,6 @@ await server.WaitPost(() => } }); }); - - await server.WaitRunTicks(1); - await pair.CleanReturnAsync(); } /// diff --git a/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs b/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs index 288e976e9b6..e991801330c 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 @@ 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 @@ await server.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Power/PowerStateTest.cs b/Content.IntegrationTests/Tests/Power/PowerStateTest.cs index edec6f3d211..a265de532fd 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 @@ 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 @@ await server.WaitAssertion(() => Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -75,7 +74,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -128,7 +125,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => 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 a28f646ef80..db6c0b7c894 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 @@ namespace Content.IntegrationTests.Tests.Power { [TestFixture] - public sealed class PowerTest + public sealed class PowerTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -167,7 +168,7 @@ public sealed class PowerTest [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 @@ await server.WaitAssertion(() => Assert.That(supplier.CurrentSupply, Is.EqualTo(loadPower * 2).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } @@ -229,7 +228,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => Assert.That(currentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] @@ -481,7 +474,7 @@ public async Task TestNoDemandRampdown() { // 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -899,7 +882,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => Assert.That(netBattery2.SupplyRampPosition, Is.EqualTo(500).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -998,7 +979,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => Assert.That(netBattery.CurrentSupply, Is.GreaterThan(0)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -1171,7 +1148,7 @@ await server.WaitAssertion(() => [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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 9d79abf4809..763cec328e0 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,7 +14,7 @@ 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. @@ -36,14 +37,16 @@ public sealed class StationPowerTests "Exo", ]; + public override PoolSettings PoolSettings => new () + { + Dirty = true, + }; + [Explicit] [Test, TestCaseSource(nameof(GameMaps))] 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; @@ -96,17 +99,12 @@ await server.WaitAssertion(() => 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; @@ -145,7 +143,5 @@ await server.WaitAssertion(() => } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs b/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs index 267b3637e0a..62c9f1a460c 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 @@ 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 @@ await server.WaitAssertion(() => entManager.DeleteEntity(tester); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs index 8209e6c61e2..b62f5627dba 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 @@ namespace Content.IntegrationTests.Tests.Preferences { [TestFixture] - public sealed class ServerDbSqliteTests + public sealed class ServerDbSqliteTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -75,12 +76,10 @@ private static ServerDbSqlite GetDb(RobustIntegrationTest.ServerIntegrationInsta [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 @@ await pair.Server.WaitAssertion(() => [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 @@ public async Task TestInitPrefs() 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 @@ public async Task TestDeleteCharacter() 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 @@ private static NetUserId NewUserId() [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 @@ await server.WaitAssertion(() => 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 6bede9660a9..d3afcc72534 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 @@ 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 @@ await pair.Server.WaitAssertion(() => } } }); - - 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 @@ await pair.Server.WaitAssertion(() => } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index 9ff8ca29009..5977e7deee2 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 @@ await server.WaitAssertion(() => } }); }); - 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 440d9e636ec..3b4bed882d4 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 @@ 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 async Task TestAllServerPrototypesAreSerializable() [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 async Task SaveThenValidatePrototype(RobustIntegrationTest.IntegrationIns [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 async Task ServerPrototypeSaveLoadSaveTest() [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 c641cd9bd1c..ac7bdcd24d5 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 @@ 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 @@ await pair.Client.WaitPost(() => 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 a4fde86dbfb..541417354a6 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 @@ await server.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Replays/ReplayTests.cs b/Content.IntegrationTests/Tests/Replays/ReplayTests.cs index dcff6c3b458..4bd5f62e78c 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 @@ 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 async Task AutoRecordReplayTest() 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 4661a1ea9e1..c95d68d9e25 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 @@ 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 @@ await server.WaitAssertion(() => } }); }); - - 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 @@ await server.WaitAssertion(() => } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs b/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs index 40457f54883..13bb3cfd74d 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 @@ public void Reset(RoundRestartCleanupEvent ev) } } + 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 @@ await server.WaitAssertion(() => Assert.That(system.HasBeenReset); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Respirator/LungTest.cs b/Content.IntegrationTests/Tests/Respirator/LungTest.cs index ae6b50ff0f4..097fe5e3a01 100644 --- a/Content.IntegrationTests/Tests/Respirator/LungTest.cs +++ b/Content.IntegrationTests/Tests/Respirator/LungTest.cs @@ -6,6 +6,7 @@ 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 @@ await server.WaitAssertion(() => "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 @@ await server.WaitAssertion(() => $"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 69c9a7dedf1..1ed43b0951b 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 @@ await server.WaitPost(() => 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 de89f16be76..e4f80eebb73 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 @@ 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 @@ await server.WaitAssertion(() => mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Round/JobTest.cs b/Content.IntegrationTests/Tests/Round/JobTest.cs index 215890791da..927f34a9eff 100644 --- a/Content.IntegrationTests/Tests/Round/JobTest.cs +++ b/Content.IntegrationTests/Tests/Round/JobTest.cs @@ -1,6 +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; @@ -16,7 +17,7 @@ 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"; @@ -44,6 +45,13 @@ public sealed class JobTest {Captain}: [ 1, 1 ] "; + 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(); @@ -71,12 +79,7 @@ private void AssertJob(TestPair pair, ProtoId job, NetUserId? user [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(); @@ -95,7 +98,6 @@ public async Task StartRoundTest() AssertJob(pair, Passenger); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -104,12 +106,7 @@ public async Task StartRoundTest() [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(); @@ -133,7 +130,6 @@ public async Task JobPreferenceTest() AssertJob(pair, Passenger); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -143,12 +139,7 @@ public async Task JobPreferenceTest() [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(); @@ -169,7 +160,6 @@ public async Task JobWeightTest() AssertJob(pair, Captain); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -178,12 +168,7 @@ public async Task JobWeightTest() [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(CCVars.GameMap, _map); var ticker = pair.Server.System(); @@ -217,6 +202,5 @@ public async Task JobPriorityTest() }); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/RoundEndTest.cs b/Content.IntegrationTests/Tests/RoundEndTest.cs index 5de6de381de..16c460c3da1 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 @@ namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class RoundEndTest + public sealed class RoundEndTest : GameTest { private sealed class RoundEndTestSystem : EntitySystem { @@ -25,15 +26,18 @@ private void OnRoundEnd(RoundEndSystemChangedEvent ev) } } + + 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 @@ await server.WaitAssertion(() => 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 0059db6292b..68bdf4726c1 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 @@ 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 async Task AllSalvageMapsLoadableTest() 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 @@ await server.WaitPost(() => } } }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); + await RunUntilSynced(); } } diff --git a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs index eb3dc147208..914a1e21c29 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 @@ 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 @@ public async Task SaveLoadMultiGridMap() 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 @@ await server.WaitAssertion(() => }); } }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs index b41aa0bf2f3..9339d612707 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 @@ await server.WaitPost(() => } }); 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 @@ await server.WaitPost(() => [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 @@ await server.WaitPost(() => testSystem.Enabled = false; await server.WaitPost(() => mapSys.DeleteMap(mapId)); - await pair.CleanReturnAsync(); } /// @@ -183,7 +182,7 @@ await server.WaitPost(() => [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 @@ public async Task LoadTickLoadBagel() 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 339420362c1..daf14e42a0c 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 @@ 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 async Task SerializeGenericEnums() 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 ab82a3d2f91..927f9734722 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 @@ namespace Content.IntegrationTests.Tests.Shuttle; -public sealed class DockTest : ContentUnitTest +public sealed class DockTest : GameTest { private static IEnumerable TestSource() { @@ -26,7 +27,7 @@ private static IEnumerable TestSource() [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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 da5b82d91e7..1c447db8715 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 @@ 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 @@ await server.WaitAssertion(() => { 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 da7e1e8e9b0..ca667f2e7af 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 @@ await pair.Client.WaitPost(() => 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 e2bf5e8ff1c..223f4676b04 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 @@ public sealed class StartTest [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 @@ public async Task TestClientStart() 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 02552669f7a..8da97cbab74 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 async Task EmergencyEvacTest() 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 5172049a991..0961db1c67b 100644 --- a/Content.IntegrationTests/Tests/Station/JobTests.cs +++ b/Content.IntegrationTests/Tests/Station/JobTests.cs @@ -2,12 +2,13 @@ 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 @@ await server.WaitAssertion(() => } } }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs index 4abd32bda0d..c0f0b19b195 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs b/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs index f80cc089de4..6b100f89fdf 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 @@ 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 @@ await server.WaitPost(() => 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 95d94906bb4..c0ef6a3691a 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 @@ 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 @@ await server.WaitAssertion(() => $"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 @@ await server.WaitAssertion(() => } }); }); - 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 @@ await server.WaitPost(() => } } }); - - 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 @@ await server.WaitAssertion(() => $"{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 @@ private int GetFillSize(StorageFillComponent fill, bool getCount, IPrototypeMana [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 async Task NoMultipleContainerFillsTest() 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 39df0fc8cc3..5a63dfa5633 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.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 async Task StoreDiscountAndRefund() EntityUid pda = default; var uplinkSystem = entManager.System(); + var ringerSystem = entManager.System(); var listingPrototypes = prototypeManager.EnumeratePrototypes() .ToArray(); @@ -78,10 +91,13 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Tag/TagTest.cs b/Content.IntegrationTests/Tests/Tag/TagTest.cs index e6cd2accaf5..4632e3ea2eb 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 @@ public sealed class TagTest [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 @@ await server.WaitAssertion(() => 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 52c5b032656..d1c26e9bec9 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 @@ 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 async Task TestBaseTurfRecursion() (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 5efa009ca7e..2aea2986ec9 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 @@ 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 @@ await pair.Client.WaitAssertion(() => 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 d460fd354f0..6ab5ca16918 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 @@ public sealed class EntitySystemExtensionsTest [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 @@ await server.WaitAssertion(() => 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 19b25816fa3..d7e1239603d 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 @@ public sealed class EntityWhitelistTest [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 @@ await server.WaitAssertion(() => 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 7058cfab6a1..dfaaae05ad7 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 @@ public sealed class VendingMachineRestockTest : EntitySystem [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 @@ await server.WaitAssertion(() => $"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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 @@ await server.WaitAssertion(() => 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 920dc088186..d9801275b90 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 @@ await server.WaitAssertion(() => 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 69ca794baf2..6d93d290f11 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 async Task MicrowaveRecipesFreezeTest() { 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 ac4c58c52cc..35b03470e9a 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 @@ 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 @@ await server.WaitPost(() => 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 @@ await server.WaitPost(() => [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 @@ await server.WaitPost(() => 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 @@ await server.WaitPost(() => [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 @@ await server.WaitPost(() => 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 @@ await server.WaitPost(() => [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 @@ await server.WaitPost(() => Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -315,7 +304,7 @@ await server.WaitPost(() => [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 @@ await server.WaitPost(() => 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 @@ await server.WaitPost(() => 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 00000000000..c8ed338a829 --- /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 90f97a57869..668e04f63d1 100644 --- a/Content.MapRenderer/Program.cs +++ b/Content.MapRenderer/Program.cs @@ -145,7 +145,7 @@ internal static async Task Main(string[] args) { 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 0586c33adae..6e7cd6a406a 100644 --- a/Content.Server/Access/Systems/AgentIDCardSystem.cs +++ b/Content.Server/Access/Systems/AgentIDCardSystem.cs @@ -1,22 +1,22 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Access.Components; +using Content.Server.Clothing.Systems; +using Content.Server.Implants; 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.Clothing.Systems; -using Content.Server.Implants; -using Content.Server.VoiceMask; +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 @@ private void OnChameleonControllerOutfitChangedItem(Entity 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 0ea55eee911..1e4a0449019 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -135,7 +135,7 @@ private void TryWriteToTargetId(EntityUid uid, string newFullName, string newJobTitle, List> newAccessList, - ProtoId newJobProto, + ProtoId? newJobProto, EntityUid player, IdCardConsoleComponent? component = null) { @@ -158,7 +158,7 @@ private void TryWriteToTargetId(EntityUid uid, _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 1990de8a6f0..34b41e0ec19 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -340,7 +340,7 @@ await RespondError( var bans = await _db.GetBansAsync(userId: located.UserId, address: null, hwId: null, - modernHWIds: located.LastModernHWIds, + modernHWIds: null, includeUnbanned: false); if (bans.Count > 0) { @@ -370,6 +370,11 @@ await RespondError( { 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 540bd7d0895..63b4b5646b1 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -279,7 +279,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) 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 02cd0aafc62..e053377d579 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 override void Initialize() 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 e68bf7b2ec3..7bb2c0c89c4 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.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 override void AdjustTileMixture(Entity entity, Gas g 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 bool IsDeltaPressureEntityInList(Entity grid, E 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 9457c5689c5..59ccbbb8c8a 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs @@ -200,71 +200,6 @@ This code takes advantage of ArrayPool so we can super easily reuse memory per t } } - /// - /// 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 639a71ec608..db89d53feab 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -28,6 +28,20 @@ public override void InitializeGases() 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 @@ protected override float GetHeatCapacityCalculation(float[] moles, bool space) } 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 @@ public void ScrubInto(GasMixture mixture, GasMixture destination, IReadOnlyColle 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 ad19770bfe6..d4a70dfbc0f 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -117,8 +117,11 @@ private void ProcessCell( 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 @@ public float GetHeatCapacityArchived(TileAtmosphere tile) } /// - /// 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 @@ public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int a 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 @@ public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int a } } - 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 @@ public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int a // 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 @@ public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int a } } - 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 6eb46b19463..6f881a32e70 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -64,17 +64,17 @@ private void OnUpdateResistance(EntityUid uid, PressureProtectionComponent press 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 a3fe2400d0d..9dcb4d13a22 100644 --- a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs @@ -8,7 +8,6 @@ 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 @@ private void DisableAnalyzer(Entity entity, EntityUid? use _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 @@ private bool UpdateAnalyzer(EntityUid uid, GasAnalyzerComponent? component = nul 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 @@ private GasEntry[] GenerateGasEntryArray(GasMixture? mixture) { 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 420db3f7325..fba6c145ae1 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 -{ - [UsedImplicitly] - public sealed class GasTankSystem : SharedGasTankSystem - { - [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!; - - private const float TimerDelay = 0.5f; - private float _timer = 0f; - private const float MinimumSoundValvePressure = 10.0f; - private float _maxExplosionRange; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnParentChange); - SubscribeLocalEvent(OnAnalyzed); - SubscribeLocalEvent(OnGasTankPrice); - Subs.CVar(_cfg, CCVars.AtmosTankFragment, UpdateMaxRange, true); - } +namespace Content.Server.Atmos.EntitySystems; - private void UpdateMaxRange(float value) - { - _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; - } +[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!; - public override void Update(float frameTime) - { - base.Update(frameTime); + private const float MinimumSoundValvePressure = 21.3f; // Arbitrary number - _timer += frameTime; + private const float ReleaseArea = 0.0005f; // About 5cm^2 - if (_timer < TimerDelay) - return; + // 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); - _timer -= TimerDelay; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnParentChange); + SubscribeLocalEvent(OnAnalyzed); + SubscribeLocalEvent(OnGasTankPrice); + } - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) - { - 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); - } - } + 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) + { + DisconnectFromInternals(entity); + ReleaseGas(entity, args.dt); } - - private void ReleaseGas(Entity gasTank) + else if (entity.Comp.CheckUser) { - var removed = RemoveAirVolume(gasTank, gasTank.Comp.ValveOutputRate * TimerDelay); - var environment = _atmosphereSystem.GetContainingMixture(gasTank.Owner, false, true); - if (environment != null) + entity.Comp.CheckUser = false; + if (Transform(entity).ParentUid != entity.Comp.User) { - _atmosphereSystem.Merge(environment, removed); + DisconnectFromInternals(entity); } - 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); } - public GasMixture? RemoveAir(Entity gasTank, float amount) - { - var gas = gasTank.Comp.Air?.Remove(amount); - CheckStatus(gasTank); - return gas; - } + Atmos.React(entity.Comp.Air, entity.Comp); - public GasMixture RemoveAirVolume(Entity gasTank, float volume) - { - var component = gasTank.Comp; - if (component.Air == null) - return new GasMixture(volume); + 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 + { + TankPressure = component.Air.Pressure + }); + } - var molesNeeded = component.OutputPressure * volume / (Atmospherics.R * component.Air.Temperature); + 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; + } - var air = RemoveAir(gasTank, molesNeeded); + /// + /// 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); - if (air != null) - air.Volume = volume; - else - return new GasMixture(volume); + var deltaP = environment == null + ? entity.Comp.Air.Pressure + : entity.Comp.Air.Pressure - environment.Pressure; - return air; - } + // Cap deltaP by the maximum output pressure of the tank. + if (deltaP < entity.Comp.SafetyPressure) + deltaP = Math.Min(entity.Comp.ReleasePressure, deltaP); - public void AssumeAir(Entity ent, GasMixture giver) - { - _atmosphereSystem.Merge(ent.Comp.Air, giver); - CheckStatus(ent); - } + var removed = _atmosphereSystem.FlowGas(entity.Comp.Air, deltaP, dt, ReleaseArea); - public void CheckStatus(Entity ent) - { - var (owner, component) = ent; - if (component.Air == null) - return; + if (removed == null) + return; - var pressure = component.Air.Pressure; + if (environment != null) + _atmosphereSystem.Merge(environment, removed); - 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); - } + // If we wouldn't produce a sound, don't throw or play a sound. + if (removed.Pressure < MinimumSoundValvePressure) + return; - pressure = component.Air.Pressure; - var range = MathF.Sqrt((pressure - component.TankFragmentPressure) / component.TankFragmentScale); + Audio.PlayPvs(entity.Comp.ReleaseSound, entity); - // Let's cap the explosion, yeah? - // !1984 - range = Math.Min(Math.Min(range, GasTankComponent.MaxExplosionRange), _maxExplosionRange); + var strength = Atmos.GetOverPressure(removed) * Atmospherics.kPaToKg_m2; - _explosions.TriggerExplosive(owner, radius: range); + if (strength <= 0) + return; - return; - } + // 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); - if (pressure > component.TankRupturePressure) - { - if (component.Integrity <= 0) - { - var environment = _atmosphereSystem.GetContainingMixture(owner, false, true); - if (environment != null) - _atmosphereSystem.Merge(environment, component.Air); + // We bias by angle to make sure it doesn't rotate too much and flies relatively straight. + _physics.ApplyAngularImpulse(entity, (float)(strength * ang)); - _audioSys.PlayPvs(component.RuptureSound, Transform(owner).Coordinates, AudioParams.Default.WithVariation(0.125f)); + // 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); + } - QueueDel(owner); - return; - } + 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; + } - component.Integrity--; - return; - } + public GasMixture RemoveAir(Entity gasTank, float amount) + { + return gasTank.Comp.Air.Remove(amount); + } - if (pressure > component.TankLeakPressure) - { - if (component.Integrity <= 0) - { - var environment = _atmosphereSystem.GetContainingMixture(owner, false, true); - if (environment == null) - return; - - var leakedGas = component.Air.RemoveRatio(0.25f); - _atmosphereSystem.Merge(environment, leakedGas); - } - else - { - component.Integrity--; - } - - return; - } + protected override void SafetyMeasures(Entity entity) + { + if (entity.Comp.ReleaseValveOpen) + return; - if (component.Integrity < 3) - component.Integrity++; - } + ToggleValve(entity); + if (entity.Comp.SafetyAlert != null) + _popup.PopupEntity(Loc.GetString(entity.Comp.SafetyAlert), entity, PopupType.LargeCaution); - /// - /// 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)); - } + Dirty(entity); + } - private void OnGasTankPrice(EntityUid uid, GasTankComponent component, ref PriceCalculationEvent args) - { - args.Price += _atmosphereSystem.GetPrice(component.Air); - } + /// + /// 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); } } diff --git a/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs b/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs index 57d48b64e95..1e3ffc869ea 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 b3da10513e1..abbddd72fe7 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.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!; + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; - public override void Initialize() - { - base.Initialize(); + public override void Initialize() + { + base.Initialize(); - SubscribeLocalEvent(OnPassiveGateUpdated); - SubscribeLocalEvent(OnExamined); - } + SubscribeLocalEvent(OnPassiveGateUpdated); + SubscribeLocalEvent(OnExamined); + } - private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, ref AtmosDeviceUpdateEvent args) + 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) { - if (!_nodeContainer.TryGetNodes(uid, gate.InletName, gate.OutletName, out PipeNode? inlet, out PipeNode? outlet)) - return; - - 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; - - float dt = args.dt; - float dV = 0; - var denom = (T1*V2 + T2*V1); + var transferFrac = _atmosphereSystem.FractionToEqualizePressure(inlet.Air, outlet.Air); + dV = transferFrac * 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 + // Actually transfer the gas. + _atmosphereSystem.Merge(outlet.Air, inlet.Air.RemoveRatio(transferFrac)); } - private void OnExamined(Entity gate, ref ExaminedEvent args) - { - if (!Comp(gate).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. - return; + gate.FlowRate = AtmosphereSystem.ExponentialMovingAverage(dV, gate.FlowRate, dt); + } - var str = Loc.GetString("gas-passive-gate-examined", ("flowRate", $"{gate.Comp.FlowRate:0.#}")); - args.PushMarkup(str); - } + private void OnExamined(Entity gate, ref ExaminedEvent args) + { + if (!Transform(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); } } diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs index 38f3ca8a470..d9128f68ae9 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.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) + float control = (controlNode.Air.Pressure - outletNode.Air.Pressure) - comp.Threshold; + float transferRate; + if (control < 0) { - 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); + comp.Enabled = false; + transferRate = 0; + } + else + { + comp.Enabled = true; + transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate * _atmosphereSystem.PumpSpeedup()); } + UpdateAppearance(uid, comp); - private void OnFilterLeaveAtmosphere(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceDisabledEvent args) + // 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) { - comp.Enabled = false; - UpdateAppearance(uid, comp); _ambientSoundSystem.SetAmbience(uid, false); + return; } - private void UpdateAppearance(EntityUid uid, PressureControlledValveComponent? comp = null, AppearanceComponent? appearance = null) - { - if (!Resolve(uid, ref comp, ref appearance, 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); - _appearance.SetData(uid, FilterVisuals.Enabled, comp.Enabled, appearance); - } + _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 06807640a83..60e6f5a20d5 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.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 @@ protected override void DirtyUI(EntityUid uid, GasCanisterComponent? canister = 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) + { + if (entity.Comp.SafetyValveOpen) + return; + + 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(canister.Air, canister); + _atmos.React(entity.Comp.Air, entity.Comp); - if (!TryComp(uid, out var nodeContainer) - || !TryComp(uid, out var appearance)) + if (!TryComp(entity, out var nodeContainer) + || !TryComp(entity, out var appearance)) return; - if (!_nodeContainer.TryGetNode(nodeContainer, canister.PortName, out PortablePipeNode? portNode)) + 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 c470ae3f0d7..da7ac33479b 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 @@ private void OnInhaleLocation(Entity ent, ref InhaleLocation 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 5fb24611334..2a1fb1a3ea8 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -429,6 +429,7 @@ public void Update(EntityUid uid, PlantHolderComponent? component = null) && 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 00000000000..3ece8334e6c --- /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 0422470cc90..00000000000 --- 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 351451123ce..635eadea1db 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.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 9f3a4d5bf3a..a35f1e7ea3e 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -7,6 +7,7 @@ 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 0101b913d6a..bbf81760353 100644 --- a/Content.Server/Cargo/Systems/PricingSystem.cs +++ b/Content.Server/Cargo/Systems/PricingSystem.cs @@ -8,13 +8,14 @@ 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 @@ private void CalculateMobPrice(EntityUid uid, MobPriceComponent component, ref P 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 b1b23b2732d..e7687ec3df9 100644 --- a/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs +++ b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs @@ -3,6 +3,7 @@ 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 @@ private void UpdateUiState(EntityUid uid, EntityUid loaderUid, CrewManifestCartr 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 7252a59f605..e4dc4fb8f94 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs @@ -8,6 +8,7 @@ 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 @@ public override void Initialize() 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 a05c7069f0c..995f3e32eb4 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 override void Initialize() 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 @@ private void OnCloneItemStore(Entity ent, ref CloningItemEvent a } } + 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 @@ private void OnCloneInventory(Entity ent, ref CloningEvent a _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 ed0e3c85d1f..abff99a4072 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; @@ -38,12 +37,13 @@ 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 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 IdentitySystem _identity = default!; - /// - /// 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)) @@ -55,10 +55,13 @@ public bool TryCloning(EntityUid original, MapCoordinates? coords, ProtoId settings) + public override void CloneComponents( + EntityUid original, + EntityUid clone, + ProtoId settings) { if (!_prototype.Resolve(settings, out var proto)) return; @@ -99,7 +106,10 @@ public override void CloneComponents(EntityUid original, EntityUid clone, ProtoI 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; @@ -145,11 +155,12 @@ public override void CloneComponents(EntityUid original, EntityUid clone, Clonin RaiseLocalEvent(original, ref cloningEv); // used for datafields that cannot be directly copied using CopyComp } - /// - /// 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; @@ -167,15 +178,11 @@ public void CopyEquipment(Entity original, Entity - /// 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)) @@ -211,12 +218,11 @@ public void CopyEquipment(Entity original, Entity - /// 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; @@ -235,17 +241,12 @@ public void CopyStorage(Entity original, Entity - /// 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! @@ -272,35 +273,5 @@ public void CopyImplants(Entity original, EntityUid target, 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 a645a108905..a18ccd2e711 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 @@ private void OnMapInit(Entity ent, ref MapInitEvent 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 645b47d1802..8a654b4ab93 100644 --- a/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs +++ b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs @@ -22,7 +22,7 @@ public override void Initialize() 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 @@ private void UpdateUi(EntityUid uid, ChameleonClothingComponent? component = nul 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 override void SetSelectedPrototype(EntityUid uid, string? protoId, bool f // 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 b1efbffd60c..c1187657fd6 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.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 bool SetOutfit(EntityUid target, string gear, Action(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 00000000000..914c9025a05 --- /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/ComponentJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/ComponentJsonGenerator.cs index c1817382ef5..c465d018581 100644 --- a/Content.Server/Corvax/GuideGenerator/ComponentJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/ComponentJsonGenerator.cs @@ -10,6 +10,12 @@ namespace Content.Server.Corvax.GuideGenerator; public static class ComponentJsonGenerator { + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + public static void PublishAll(IResourceManager res, ResPath destRoot) { var proto = IoCManager.Resolve(); @@ -95,17 +101,10 @@ public static void PublishAll(IResourceManager res, ResPath destRoot) ["id"] = map }; - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - res.UserData.CreateDir(destRoot); var fileName = TextTools.DecapitalizeString(compName) + ".json"; - var file = res.UserData.OpenWriteText(destRoot / fileName); - file.Write(JsonSerializer.Serialize(outObj, serializeOptions)); - file.Flush(); + using var stream = res.UserData.OpenWrite(destRoot / fileName); + JsonSerializer.Serialize(stream, outObj, SerializeOptions); } } } diff --git a/Content.Server/Corvax/GuideGenerator/ComponentListGenerator.cs b/Content.Server/Corvax/GuideGenerator/ComponentListGenerator.cs index 6f414d01d31..53e687afd52 100644 --- a/Content.Server/Corvax/GuideGenerator/ComponentListGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/ComponentListGenerator.cs @@ -7,7 +7,13 @@ namespace Content.Server.Corvax.GuideGenerator; public static class ComponentListGenerator { - public static void PublishJson(StreamWriter file) + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + public static void PublishJson(Stream stream) { var proto = IoCManager.Resolve(); @@ -32,12 +38,6 @@ public static void PublishJson(StreamWriter file) if (output.Count == 0) return; - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - file.Write(JsonSerializer.Serialize(output, serializeOptions)); + JsonSerializer.Serialize(stream, output, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/ComponentStoreGenerator.cs b/Content.Server/Corvax/GuideGenerator/ComponentStoreGenerator.cs index f8471ee8791..813a93bb40f 100644 --- a/Content.Server/Corvax/GuideGenerator/ComponentStoreGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/ComponentStoreGenerator.cs @@ -11,7 +11,13 @@ namespace Content.Server.Corvax.GuideGenerator; public static class ComponentStoreGenerator { - public static void PublishJson(StreamWriter file) + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + public static void PublishJson(Stream stream) { var proto = IoCManager.Resolve(); var compFactory = IoCManager.Resolve(); @@ -92,12 +98,6 @@ public static void PublishJson(StreamWriter file) normalized[refEntId] = compMap; } - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - file.Write(JsonSerializer.Serialize(normalized, serializeOptions)); + JsonSerializer.Serialize(stream, normalized, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/EntityEntry.cs b/Content.Server/Corvax/GuideGenerator/EntityEntry.cs deleted file mode 100644 index 9ddd167328b..00000000000 --- 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 24c21cd2298..6328729287c 100644 --- a/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs @@ -1,27 +1,69 @@ 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 { - public static void PublishJson(StreamWriter file) + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + [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(Stream stream) { var prototype = IoCManager.Resolve(); var prototypes = 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, - }; - - file.Write(JsonSerializer.Serialize(prototypes, serializeOptions)); + JsonSerializer.Serialize(stream, prototypes, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs index 117936e32be..403605823c0 100644 --- a/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs @@ -2,19 +2,34 @@ 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 = "(способность)"; + + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + 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 +46,11 @@ private static string GetLabel(EntityPrototype proto) 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 +58,7 @@ private static Dictionary> GetDuplicatesName( bool duplicatesOnly) { var loc = IoCManager.Resolve(); + var compFactory = IoCManager.Resolve(); var allowedIds = EntityProjectGenerator.GetProjectEntityIds(); return prototypeManager .EnumeratePrototypes() @@ -50,24 +67,17 @@ private static Dictionary> GetDuplicatesName( { 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 => @@ -97,34 +107,22 @@ private static Dictionary> GetDuplicatesName( : [g.OrderBy(p => p.ID).First().ID]); } - public static void PublishNameJson(StreamWriter writer) + public static void PublishNameJson(Stream stream) { var prototypeManager = IoCManager.Resolve(); var nameToIds = GetDuplicatesName(prototypeManager, false); var nameToSingleId = nameToIds.ToDictionary(kv => kv.Key, kv => kv.Value[0]); - var options = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - writer.Write(JsonSerializer.Serialize(nameToSingleId, options)); + JsonSerializer.Serialize(stream, nameToSingleId, SerializeOptions); } - public static void PublishDuplicatesJson(StreamWriter writer) + public static void PublishDuplicatesJson(Stream stream) { var prototypeManager = IoCManager.Resolve(); var duplicatesName = GetDuplicatesName(prototypeManager, true); - var options = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - writer.Write(JsonSerializer.Serialize(duplicatesName, options)); + JsonSerializer.Serialize(stream, duplicatesName, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs new file mode 100644 index 00000000000..0555da8cb6b --- /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 +{ + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + [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(Stream stream) + { + var prototypeManager = IoCManager.Resolve(); + var prototypes = prototypeManager + .EnumeratePrototypes() + .Where(x => !x.Abstract) + .Select(x => new EntityParentJsonGenerator(x)) + .ToDictionary(x => x.Id, x => x.Parents); + + JsonSerializer.Serialize(stream, prototypes, SerializeOptions); + } +} diff --git a/Content.Server/Corvax/GuideGenerator/EntityProjectGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityProjectGenerator.cs index 90cd58d21c5..0b46a3e1aad 100644 --- a/Content.Server/Corvax/GuideGenerator/EntityProjectGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/EntityProjectGenerator.cs @@ -8,12 +8,18 @@ namespace Content.Server.Corvax.GuideGenerator; public static class EntityProjectGenerator { + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + public static HashSet GetProjectEntityIds() { return EntityProjectHelper.GetProjectEntityIds(); } - public static void PublishJson(StreamWriter file) + public static void PublishJson(Stream stream) { var ids = GetProjectEntityIds(); if (ids.Count == 0) @@ -22,12 +28,6 @@ public static void PublishJson(StreamWriter file) var sorted = ids.ToList(); sorted.Sort(StringComparer.Ordinal); - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - file.Write(JsonSerializer.Serialize(sorted, serializeOptions)); + JsonSerializer.Serialize(stream, sorted, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs index a2b65120bd9..d7a90bc6ad0 100644 --- a/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs @@ -11,6 +11,12 @@ namespace Content.Server.Corvax.GuideGenerator; public static class LocJsonGenerator { + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + }; + // Matches top-level message/term identifiers at start of line (no leading whitespace or comment). private static readonly Regex TopEntryRegex = new(@"(?m)^(?!\s|#)([^\s=]+)\s*=", RegexOptions.Compiled | RegexOptions.CultureInvariant); @@ -31,7 +37,7 @@ private static string GetStringSafe(string key) } } - public static void PublishJson(StreamWriter file) + public static void PublishJson(Stream stream) { var loc = IoCManager.Resolve(); var res = IoCManager.Resolve(); @@ -40,7 +46,7 @@ public static void PublishJson(StreamWriter file) var culture = loc.DefaultCulture ?? loc.GetFoundCultures().FirstOrDefault(); if (culture == null) { - file.Write("{}"); + JsonSerializer.Serialize(stream, new Dictionary(), SerializeOptions); return; } @@ -52,8 +58,8 @@ public static void PublishJson(StreamWriter file) foreach (var path in files) { - using var stream = res.ContentFileRead(path); - using var reader = new StreamReader(stream, Encoding.UTF8); + using var fileStream = res.ContentFileRead(path); + using var reader = new StreamReader(fileStream, Encoding.UTF8); var contents = reader.ReadToEnd(); // Normalize line endings to simplify indexing. contents = contents.Replace("\r\n", "\n"); @@ -106,12 +112,6 @@ public static void PublishJson(StreamWriter file) } } - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - }; - - file.Write(JsonSerializer.Serialize(output, serializeOptions)); + JsonSerializer.Serialize(stream, output, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/MetaLicenseGenerator.cs b/Content.Server/Corvax/GuideGenerator/MetaLicenseGenerator.cs index 015757760a2..6f0c9fbc635 100644 --- a/Content.Server/Corvax/GuideGenerator/MetaLicenseGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/MetaLicenseGenerator.cs @@ -6,7 +6,13 @@ namespace Content.Server.Corvax.GuideGenerator; public static class MetaLicenseGenerator { - public static void PublishJson(StreamWriter file) + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + public static void PublishJson(Stream stream) { var workingDir = Directory.GetCurrentDirectory(); var resourcesRoot = Path.Combine(workingDir, "Resources"); @@ -41,13 +47,7 @@ public static void PublishJson(StreamWriter file) if (output.Count == 0) return; - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - file.Write(JsonSerializer.Serialize(output, serializeOptions)); + JsonSerializer.Serialize(stream, output, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs index 2549b4c5aee..219c35dba4f 100644 --- a/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs @@ -3,9 +3,7 @@ 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; @@ -14,6 +12,12 @@ namespace Content.Server.Corvax.GuideGenerator; public static class PrototypeJsonGenerator { + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + public static void PublishAll(IResourceManager res, ResPath destRoot) { var proto = IoCManager.Resolve(); @@ -48,11 +52,19 @@ public static void PublishAll(IResourceManager res, ResPath destRoot) var instance = Activator.CreateInstance(kind); if (instance != null) { - FieldEntry.EnsureFieldsCollectionsInitialized(instance); - var defaultNode = ser.WriteValueAs(kind, instance, true); - defaultNode.Remove("id"); - FieldEntry.NormalizeFlagsToSequences(instance, defaultNode); - defaultObj = FieldEntry.DataNodeToObject(defaultNode); + try + { + FieldEntry.EnsureFieldsCollectionsInitialized(instance); + var defaultNode = ser.WriteValueAs(kind, instance, true); + defaultNode.Remove("id"); + FieldEntry.NormalizeFlagsToSequences(instance, defaultNode); + defaultObj = FieldEntry.DataNodeToObject(defaultNode); + } + finally + { + if (instance is IDisposable disposable) + disposable.Dispose(); + } } } catch @@ -66,20 +78,13 @@ public static void PublishAll(IResourceManager res, ResPath destRoot) ["id"] = map }; - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - res.UserData.CreateDir(destRoot); var kindName = proto.TryGetKindFrom(kind, out var actualKindName) ? actualKindName : kind.Name; var fileName = TextTools.DecapitalizeString(kindName) + ".json"; - var file = res.UserData.OpenWriteText(destRoot / fileName); - file.Write(JsonSerializer.Serialize(outObj, serializeOptions)); - file.Flush(); + using var stream = res.UserData.OpenWrite(destRoot / fileName); + JsonSerializer.Serialize(stream, outObj, SerializeOptions); } } @@ -154,8 +159,9 @@ private static bool IsUnsafeSerializedType(Type type, HashSet visited) } } - 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/PrototypeListGenerator.cs b/Content.Server/Corvax/GuideGenerator/PrototypeListGenerator.cs index 6e905368a09..c4c44377e64 100644 --- a/Content.Server/Corvax/GuideGenerator/PrototypeListGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/PrototypeListGenerator.cs @@ -7,7 +7,13 @@ namespace Content.Server.Corvax.GuideGenerator; public static class PrototypeListGenerator { - public static void PublishJson(StreamWriter file) + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + public static void PublishJson(Stream stream) { var proto = IoCManager.Resolve(); @@ -36,12 +42,6 @@ public static void PublishJson(StreamWriter file) if (output.Count == 0) return; - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - file.Write(JsonSerializer.Serialize(output, serializeOptions)); + JsonSerializer.Serialize(stream, output, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/PrototypeStoreGenerator.cs b/Content.Server/Corvax/GuideGenerator/PrototypeStoreGenerator.cs index 0c12646c812..b7cfeecb822 100644 --- a/Content.Server/Corvax/GuideGenerator/PrototypeStoreGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/PrototypeStoreGenerator.cs @@ -7,7 +7,13 @@ namespace Content.Server.Corvax.GuideGenerator; public static class PrototypeStoreGenerator { - public static void PublishJson(StreamWriter file) + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + public static void PublishJson(Stream stream) { var proto = IoCManager.Resolve(); @@ -54,13 +60,7 @@ public static void PublishJson(StreamWriter file) if (output.Count == 0) return; - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - file.Write(JsonSerializer.Serialize(output, serializeOptions)); + JsonSerializer.Serialize(stream, output, SerializeOptions); } } diff --git a/Content.Server/Corvax/GuideGenerator/TextTools.cs b/Content.Server/Corvax/GuideGenerator/TextTools.cs index 021261ebb49..e1da52f6233 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 static string GetDisplayName(EntityPrototype proto, IPrototypeManager pro 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/WikiEntityNameGenerator.cs b/Content.Server/Corvax/GuideGenerator/WikiEntityNameGenerator.cs index 799de417a89..d26b075d8b4 100644 --- a/Content.Server/Corvax/GuideGenerator/WikiEntityNameGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/WikiEntityNameGenerator.cs @@ -17,8 +17,13 @@ public static class WikiEntityNameGenerator private const string ApiEndpoint = "https://station14.ru/api.php"; private static readonly HttpClient HttpClient = new(); + private static readonly JsonSerializerOptions SerializeOptions = new() + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; - public static void PublishJson(StreamWriter writer, IResourceManager resourceManager, ResPath destRoot) + public static void PublishJson(Stream stream, IResourceManager resourceManager, ResPath destRoot) { var entityNamePath = destRoot.WithName("entity_name.json"); @@ -65,14 +70,7 @@ public static void PublishJson(StreamWriter writer, IResourceManager resourceMan missing.Add(title); } - var options = new JsonSerializerOptions - { - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - var outputJson = JsonSerializer.Serialize(missing, options); - writer.Write(outputJson); + JsonSerializer.Serialize(stream, missing, SerializeOptions); } private static HashSet FetchAllCategoryTitles() diff --git a/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs b/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs index b5578164b17..a8a75f825ac 100644 --- a/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs +++ b/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs @@ -36,7 +36,7 @@ public static bool TryGetRawMapping( { 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 7dfdebf3678..40ecdc21be8 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 80461e156af..c634836b5af 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 @@ public sealed partial class DragonComponent : Component /// [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 1c838939ec9..8bc5fd83b14 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.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 override void Update(float frameTime) 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 @@ private void OnZombified(Entity ent, ref EntityZombifiedEvent a _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 00000000000..f681afda5eb --- /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 42edebfd874..648ae3382f4 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -158,13 +158,13 @@ public override void PostInit() { var resPath = new ResPath(dest).ToRootedPath(); // Corvax-Wiki-Start - void WriteFile(string name, Action write) + void WriteFile(string name, Action write) { - using var file = _res.UserData.OpenWriteText(resPath.WithName(name)); - write(file); - file.Flush(); + using var stream = _res.UserData.OpenWrite(resPath.WithName(name)); + write(stream); } - WriteFile("entity_" + dest, EntityJsonGenerator.PublishJson); + 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 ab35f729dbc..ab65c04633c 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -494,7 +494,6 @@ private void ProcessEntity( dir, physics, xform, - _projectileQuery, throwForce); } } diff --git a/Content.Server/FeedbackSystem/ServerFeedbackManager.cs b/Content.Server/FeedbackSystem/ServerFeedbackManager.cs index 09edd8eefec..4ecb22bcc23 100644 --- a/Content.Server/FeedbackSystem/ServerFeedbackManager.cs +++ b/Content.Server/FeedbackSystem/ServerFeedbackManager.cs @@ -29,9 +29,6 @@ public override bool Send(EntityUid uid, List> p /// 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 override void SendToSession(ICommonSession session, List public override void SendToAllSessions(List> popupPrototypes, bool remove = false) { - if (!NetManager.IsServer) - return; - var msg = new FeedbackPopupMessage { FeedbackPrototypes = popupPrototypes, @@ -59,9 +53,6 @@ public override void SendToAllSessions(List> pop /// public override void OpenForSession(ICommonSession session) { - if (!NetManager.IsServer) - return; - var msg = new OpenFeedbackPopupMessage(); NetManager.ServerSendMessage(msg, session.Channel); } @@ -69,9 +60,6 @@ public override void OpenForSession(ICommonSession session) /// 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 c1d8e3b31e0..cf1d6eb1a89 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -309,7 +309,7 @@ public void ClearGameRules() } /// - /// 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 IEnumerable GetAddedGameRules() } } + /// + /// 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 IEnumerable GetActiveGameRules() } } + /// + /// 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 16699acfd53..7d370767e4e 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -352,17 +352,17 @@ public void DoSpawn( 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); _roles.MindAddJobRole(newMind, silent: silent, jobPrototype: jobId); diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 00169f53cd3..156a6d91f94 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -3,32 +3,46 @@ 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 @@ protected override void AppendRoundEndText(EntityUid uid, 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 @@ private void OnGetBriefing(Entity role, ref GetBriefingEve 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 ab8864caaa7..7eb076d0d52 100644 --- a/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs @@ -7,17 +7,20 @@ 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 @@ protected override void Started(EntityUid uid, ParadoxCloneRuleComponent compone base.Started(uid, component, gameRule, args); // check if we got enough potential cloning targets, otherwise cancel the gamerule so that the ghost role does not show up - var allHumans = _mind.GetAliveHumans(); + var allHumans = _target.GetAliveHumans(); if (allHumans.Count == 0) { @@ -59,7 +62,7 @@ private void OnAntagSelectEntity(Entity ent, ref Anta else { // get possible targets - var allAliveHumanoids = _mind.GetAliveHumans(); + var allAliveHumanoids = _target.GetAliveHumans(); // we already checked when starting the gamerule, but someone might have died since then. if (allAliveHumanoids.Count == 0) diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs index cf74513c874..f0cf3087ff7 100644 --- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs @@ -146,7 +146,8 @@ private void OnPostFlash(EntityUid uid, HeadRevolutionaryComponent comp, ref Aft !HasComp(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 046ecedad69..b520a3f6424 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 @@ protected override void Started(EntityUid uid, SurvivorRuleComponent component, { 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 3568f17306f..bc053c80bad 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Text; using Content.Server.Codewords; +using Robust.Shared.Map; namespace Content.Server.GameTicking.Rules; @@ -148,32 +149,22 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component) 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 bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component) 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 0b6a700d110..932774e9a17 100644 --- a/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs @@ -7,6 +7,7 @@ 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 @@ protected override void AppendRoundEndText(EntityUid uid, 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 @@ protected override void AppendRoundEndText(EntityUid uid, 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 118c0abacc7..11d1a243b5d 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -13,7 +13,6 @@ 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; @@ -39,7 +38,6 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Timing; namespace Content.Server.Ghost { @@ -49,7 +47,6 @@ public sealed class GhostSystem : SharedGhostSystem [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!; @@ -92,8 +89,6 @@ public override void Initialize() SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnGhostShutdown); - SubscribeLocalEvent(OnGhostExamine); - SubscribeLocalEvent(OnMindRemovedMessage); SubscribeLocalEvent(OnMindUnvisitedMessage); SubscribeLocalEvent(OnPlayerDetached); @@ -209,8 +204,10 @@ private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentSt } _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) @@ -241,16 +238,6 @@ private void OnMapInit(EntityUid uid, GhostComponent component, MapInitEvent arg _actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction); } - 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); - } - #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 e884e181eeb..3de51a49499 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 @@ private void GetJobEquipmentInformation( 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 582b9cb2ac4..08fe5b54cf7 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 e628ee6e014..2a7bcab9095 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 7fb78db29cf..18e67a78740 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -24,7 +24,6 @@ using Content.Server.ServerInfo; using Content.Server.ServerUpdates; using Content.Server.Voting.Managers; -using Content.Server.Worldgen.Tools; using Content.Corvax.Interfaces.Server; using Content.Corvax.Interfaces.Shared; using Content.Server.Corvax.DiscordAuth; @@ -71,7 +70,6 @@ public static void Register(IDependencyCollection deps) 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 04903241e36..7435a7706eb 100644 --- a/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs +++ b/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs @@ -75,7 +75,7 @@ public void ClearPartEntity(Entity ent, Enum part) 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 @@ private void OnComponentStartup(Entity ent, ref Compo { // 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 @@ private void OnMachineAnchorChanged(Entity ent, 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 @@ private void OnPartAnchorChanged(Entity ent, ref // 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 6fc031d7c13..975f4c19bdf 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 00000000000..431e68a55ba --- /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 00000000000..7872a66f021 --- /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 00000000000..dc9a9cbb7dd --- /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 00000000000..6dc8e494011 --- /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 00000000000..98cd2afd4de --- /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 00000000000..3048e043b55 --- /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 00000000000..6693c3c63c2 --- /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 00000000000..d07a9c29170 --- /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 00000000000..387c9e9809f --- /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 0c88cc2de6b..00000000000 --- 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 89b49cbbe9f..b30f813a66a 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -30,7 +30,10 @@ 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 @@ private float GetScore(NPCBlackboard blackboard, EntityUid targetUid, UtilityCon 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 @@ private float GetScore(NPCBlackboard blackboard, EntityUid targetUid, UtilityCon } 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 33b619732d2..202d03c6ea5 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 ae35fa48251..e9ecba3454a 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs @@ -106,7 +106,7 @@ private void OnSmokeableEquipEvent(Entity entity, ref GotEqui { 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 00000000000..c89c9aa9e97 --- /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 00000000000..416c4228379 --- /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 00000000000..850b7f2f13c --- /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 2c864a80d4d..16e539f320f 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 00000000000..038150d90c8 --- /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 00000000000..1cf9318b53c --- /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 00000000000..e5f88a28dda --- /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 00000000000..a82005fa57f --- /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 b3075b22747..5b48b25ad4b 100644 --- a/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs +++ b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs @@ -5,6 +5,7 @@ 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 @@ private void OnSpecificPersonAssigned(Entity ent, r 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 @@ private void OnRandomPersonAssigned(Entity ent, ref O 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 00000000000..93e649def25 --- /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 9122c969645..724c9dc4914 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.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; @@ -189,7 +191,7 @@ public override void UpdatePdaUi(EntityUid uid, PdaComponent? pda = null) 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); @@ -274,8 +276,19 @@ private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaShowUplinkMessage m 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) @@ -285,14 +298,24 @@ private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaLockUplinkMessage m 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 00000000000..f7d124adef6 --- /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 b47ca0fde3b..a86b44d3899 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 override void Initialize() /// 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,72 +79,212 @@ private void OnCurrencyInsert(Entity ent, ref CurrencyInsertAtt /// /// 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; - - if (!HasComp(uid)) - return false; + ReservedSerializedRingtones.Clear(); + } - // 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; - for (var i = 0; i < RingtoneLength; i++) + var maxPow = Math.Pow(notes.Length, RingtoneLength); + if (maxPow > int.MaxValue) { - ringtone[i] = _random.Pick(notes); + 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++) + { + 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 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 bf2d2e2817f..1e5fa74b7cf 100644 --- a/Content.Server/Pinpointer/StationMapSystem.cs +++ b/Content.Server/Pinpointer/StationMapSystem.cs @@ -22,9 +22,11 @@ public override void Initialize() 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 @@ private void OnMapInit(Entity ent, ref MapInitEvent args) 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 @@ private void OnStationMapOpened(EntityUid uid, StationMapComponent component, Bo comp.Map = uid; } - private void OnNukeopsStationSelected(Entity ent, ref NukeopsTargetStationSelectedEvent args) + private void OnNukeOpsStationMap(Entity entity, ref ChooseStationMapEvent args) { - if (args.TargetStation == null) - return; + // 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; - if (!TryComp(ent, out var stationMap) || !TryComp(args.RuleEntity, out var ruleGrids)) + args.TargetGrid = _station.GetLargestGrid((rule.Comp.TargetStation.Value, null)); return; + } + } - if (Transform(ent).MapID != ruleGrids.Map) + private void OnNukeopsStationSelected(ref NukeopsTargetStationSelectedEvent args) + { + if (args.TargetStation == null || !TryComp(args.RuleEntity, out var ruleGrids)) return; - stationMap.TargetGrid = _station.GetLargestGrid((args.TargetStation.Value, null)); - Dirty(ent); + var mapquery = EntityQueryEnumerator(); + while (mapquery.MoveNext(out var uid, out _, out var map)) + { + if (Transform(uid).MapID != ruleGrids.Map) + continue; + + 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 e5b0981b3db..0854fbb172d 100644 --- a/Content.Server/Procedural/DungeonSystem.Rooms.cs +++ b/Content.Server/Procedural/DungeonSystem.Rooms.cs @@ -7,6 +7,7 @@ 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 void SpawnRoom( MapGridComponent grid, Vector2i origin, DungeonRoomPrototype room, - Random random, + IRobustRandom random, HashSet? reservedTiles, bool clearExisting = false, bool rotation = false) @@ -96,7 +97,7 @@ public void SpawnRoom( 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 f4ccab23673..96adb8dec30 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 @@ private void OnRoomFillMapInit(EntityUid uid, RoomFillComponent component, MapIn 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 @@ private void OnRoomFillMapInit(EntityUid uid, RoomFillComponent component, MapIn 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 44b11af8347..00000000000 --- 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 77df6c09e21..eb69e2ccd8e 100644 --- a/Content.Server/Radiation/Systems/GeigerSystem.cs +++ b/Content.Server/Radiation/Systems/GeigerSystem.cs @@ -47,7 +47,7 @@ private void OnEquipped(Entity geiger, ref GotEquippedEvent arg { 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 a32fa810c97..00000000000 --- 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 4a4d0f8930c..3fbc7e0d28e 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 @@ 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 override void Initialize() 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 7d16687d5f2..13b331c7780 100644 --- a/Content.Server/Radio/EntitySystems/HeadsetSystem.cs +++ b/Content.Server/Radio/EntitySystems/HeadsetSystem.cs @@ -58,7 +58,7 @@ protected override void OnGotEquipped(EntityUid uid, HeadsetComponent component, 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 @@ protected override void OnGotUnequipped(EntityUid uid, HeadsetComponent componen { 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 c5080c0b067..19b6a155300 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 void MakeVisible(bool visible) } } + /// + /// 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 override void Update(float frameTime) { 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 00000000000..61acf26d5b7 --- /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 76b4131ca20..6307469b414 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -315,7 +315,7 @@ public void EndRound(TimeSpan? countdownTime = null) 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 d4fcfb81c66..cfb667e1f0a 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 d50eea53ee5..cbc48b3ccad 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs @@ -256,7 +256,7 @@ private void ThrowEntitiesOnGrid(EntityUid gridUid, TransformComponent xform, Ve 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 { @@ -414,7 +414,7 @@ private void ProcessTileBatch( 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 9d928b37a0c..b8bc955825a 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 cc2f7a4e2f2..b16c98f56b8 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 @@ private void OnChargeChanged(Entity entity, ref ChargeCh private void OnDamageChanged(Entity entity, ref DamageChangedEvent args) { - UpdateCoreIntegrityAlert(entity); + UpdateCoreIntegrityAlert(entity, args.DamageIncreased); UpdateDamagedAccent(entity); } @@ -285,7 +286,7 @@ private void UpdateBatteryAlert(Entity ent) } } - private void UpdateCoreIntegrityAlert(Entity ent) + private void UpdateCoreIntegrityAlert(Entity ent, bool damageIncreased = false) { if (!TryComp(ent, out var damageable)) return; @@ -303,6 +304,12 @@ private void UpdateCoreIntegrityAlert(Entity ent) 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 6905b38b436..837ba05df1f 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -50,7 +50,7 @@ public override void Initialize() 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 @@ private void OnSignalReceived(EntityUid uid, EmitterComponent component, ref Sig } } - 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 1720d67d024..38878b147fc 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 @@ private void HandleSpamEmitSoundMapInit(Entity entity, r 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 a0d923dd1ed..a6b75f953c2 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -21,7 +21,9 @@ public sealed class StackSystem : SharedStackSystem /// 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 cf58aea47a3..2e6e7fcb96c 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.Shared.Access.Components; using Content.Shared.Forensics.Components; @@ -9,6 +10,7 @@ using Content.Shared.StationRecords; using Robust.Shared.Enums; using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Server.StationRecords.Systems; @@ -37,6 +39,7 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem [Dependency] private readonly StationRecordKeyStorageSystem _keyStorage = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IdCardSystem _idCard = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -211,6 +214,27 @@ public bool RemoveRecord(StationRecordKey key, StationRecordsComponent? records 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 1c398016224..d5855388fa1 100644 --- a/Content.Server/Storage/StorageCommand.cs +++ b/Content.Server/Storage/StorageCommand.cs @@ -52,7 +52,7 @@ public IEnumerable StorageFastTake([PipedArgument] 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 cb92a42c10a..f4ecd870407 100644 --- a/Content.Server/Store/Systems/StoreSystem.Refund.cs +++ b/Content.Server/Store/Systems/StoreSystem.Refund.cs @@ -4,11 +4,14 @@ 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 6b67ff5d60e..713146856b1 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -7,16 +7,12 @@ 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 NpcFactionSystem _npcFaction = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly StackSystem _stack = default!; - [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; private void InitializeUi() { @@ -41,6 +36,16 @@ private void InitializeUi() 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 @@ private void OnRefundEntityDeleted(Entity ent, ref RefundEntityD 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) - { - if (!Resolve(storeEnt, ref component)) - 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) + private void RemoteStoreRelay(Entity entity, object ev) { - if (!Resolve(uid, ref component)) + if (entity.Comp.Store == null || !TryComp(entity.Comp.Store, out var store)) 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 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi 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 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi 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 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi _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 @@ private void OnRequestWithdraw(EntityUid uid, StoreComponent component, StoreReq 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 7c5c99b5b4f..b8e92f10bf6 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 @@ private void OnMapInit(EntityUid uid, StoreComponent component, MapInitEvent arg { 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 @@ private void OnStoreOpenAttempt(EntityUid uid, StoreComponent component, Activat 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 @@ private void OnStoreOpenAttempt(EntityUid uid, StoreComponent component, Activat 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) - 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))) + if (GetRemoteStore(entity.AsNullable()) is not { } 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 e8c53de9ebf..723ddbf0ffb 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs @@ -30,7 +30,7 @@ private void OnExpandRecipients(ExpandICChatRecipientsEvent ev) // 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 @@ private void OnExpandRecipients(ExpandICChatRecipientsEvent ev) 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 00000000000..4015a65cc99 --- /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 cdd032c79cf..09c2b39447f 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs @@ -13,15 +13,15 @@ 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 override void Initialize() SubscribeLocalEvent(OnPacketReceived); SubscribeLocalEvent(OnSetName); SubscribeLocalEvent(OnSetNetwork); + + InitializeCollide(); } private void OnPacketReceived(EntityUid uid, SurveillanceCameraComponent component, DeviceNetworkPacketEvent args) @@ -232,7 +234,7 @@ private void Deactivate(EntityUid camera, SurveillanceCameraComponent? component 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 @@ private void Deactivate(EntityUid camera, SurveillanceCameraComponent? component 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 void AddActiveViewer(EntityUid camera, EntityUid player, EntityUid? monit } _viewSubscriberSystem.AddViewSubscriber(camera, actor.PlayerSession); - component.ActiveViewers.Add(player); + + component.ActivePvsViewers.Add(player); if (monitor != null) { @@ -346,7 +368,7 @@ public void RemoveActiveViewer(EntityUid camera, EntityUid player, EntityUid? mo 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 @@ private void UpdateVisuals(EntityUid uid, SurveillanceCameraComponent? component 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 0314a116b9c..052fcd8871a 100644 --- a/Content.Server/Telephone/TelephoneSystem.cs +++ b/Content.Server/Telephone/TelephoneSystem.cs @@ -118,12 +118,12 @@ private void OnTelephoneMessageReceived(Entity entity, ref T // 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 6b605e98d36..839f7fa9ca6 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 8350c01f7aa..bf00f5ebd84 100644 --- a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs +++ b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs @@ -73,7 +73,13 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) } // 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 e8ed868dfbc..1daee849ddc 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.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; + + var ev = new GenerateUplinkCodeEvent(); + RaiseLocalEvent(storeEntity, ref ev); - EnsureComp(uplinkEntity.Value); + if (ev.Code == null) + { + QueueDel(storeEntity); + return false; + } - SetUplink(user, uplinkEntity.Value, balance, giveDiscounts); + code = ev.Code; - // TODO add BUI. Currently can't be done outside of yaml -_- - // ^ What does this even mean? + if (bindToPda) + { + var accessComp = EnsureComp(storeEntity); + _ringer.SetBoundUplinkEntity((storeEntity, accessComp), uplinkEntity.Value); + } + + SetUplink(user, storeEntity, balance, giveDiscounts); return true; } @@ -60,25 +134,25 @@ public bool AddUplink( /// /// 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 @@ private void SetUplink(EntityUid user, EntityUid uplink, FixedPoint2 balance, bo /// /// 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 @@ private bool ImplantUplink(EntityUid user, FixedPoint2 balance, bool giveDiscoun 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 @@ private bool ImplantUplink(EntityUid user, FixedPoint2 balance, bool giveDiscoun { 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 6ece6f0d959..d369ea86899 100644 --- a/Content.Server/Wagging/WaggingSystem.cs +++ b/Content.Server/Wagging/WaggingSystem.cs @@ -35,7 +35,13 @@ private void OnCloning(Entity ent, ref CloningEvent args) 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 58e1eb2b988..b3a0bea8822 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 c9fd429c479..e4d7861029f 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 @@ private void OnInteractUsing(EntityUid uid, WiresComponent component, InteractUs { 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 @@ private void OnPanelChanged(Entity ent, ref PanelChangedEvent ar 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 @@ private void UpdateUserInterface(EntityUid uid, WiresComponent? wires = null, Us 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 @@ private void UpdateUserInterface(EntityUid uid, WiresComponent? wires = null, Us 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 void SetWiresPanelSecurity(EntityUid uid, WiresPanelSecurityComponent com 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 08571cd588c..00000000000 --- 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 28724d20a49..00000000000 --- 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 a1317ae2edf..00000000000 --- 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 ae61f0581e3..00000000000 --- 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 af4ef7f1cf0..00000000000 --- 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 b2112779972..00000000000 --- 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 5db9bad9255..00000000000 --- 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 4865773bf34..00000000000 --- 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 d2743ad4ab3..00000000000 --- 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 1d37ab34c90..00000000000 --- 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 06d84d2f85f..00000000000 --- 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 3a91c007564..00000000000 --- 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 63580e7541a..00000000000 --- 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 e6bb7781e96..00000000000 --- 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 24b710626bd..00000000000 --- 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 75e4670e88d..00000000000 --- 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 41d89cbaa72..00000000000 --- 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 c9107fa2bd8..00000000000 --- 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 7a9e74375cb..00000000000 --- 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 1827f6deeda..00000000000 --- 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 1207d6f157e..00000000000 --- 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 ba0a3a7132e..00000000000 --- 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 b609f7e1f7e..00000000000 --- 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 a02ff81362c..00000000000 --- 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 dcb7b7fc8fc..00000000000 --- 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 fb02e8aa0bd..00000000000 --- 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 5a7e02c803a..00000000000 --- 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 19c777e1ad6..00000000000 --- 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 cc0ec627338..00000000000 --- 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 54805754270..00000000000 --- 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 cb7c5a14114..00000000000 --- 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 1ed20b9f1fa..00000000000 --- 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 c261c7decac..32662d78384 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 35af5972c35..89c24940ba3 100644 --- a/Content.Shared/Access/Components/IdCardConsoleComponent.cs +++ b/Content.Shared/Access/Components/IdCardConsoleComponent.cs @@ -26,9 +26,9 @@ public sealed class WriteToTargetIdMessage : BoundUserInterfaceMessage 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 f8c6d492441..19c1cb880b1 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -41,6 +41,7 @@ public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnLinkAttempt); @@ -52,13 +53,18 @@ public override void Initialize() 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 485dc895804..18c85c3aaa8 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -23,14 +23,7 @@ public sealed class ActionBlockerSystem : EntitySystem { [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 534f0d3ee74..642131a3655 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 642d942fb6d..b8bd20034ea 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -33,19 +33,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); @@ -927,7 +923,7 @@ private void OnDidEquip(Entity ent, ref DidEquipEvent args) 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 834a22b8deb..5c751b938fc 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 4c3cdb01465..0b2c4b7a16d 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 @@ private void OnAnomalyPulse(EntityUid uid, GravityAnomalyComponent component, re 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 @@ private void OnSupercritical(EntityUid uid, GravityAnomalyComponent component, r 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 b63b25fa5da..03aead97c5c 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 @@ private void SetScannerSupercritical(Entity anomalyEnt, bool v if (tilerefs.Count == 0) return null; - var physQuery = GetEntityQuery(); var resultList = new List(); while (resultList.Count < amount) { @@ -437,7 +438,7 @@ private void SetScannerSupercritical(Entity anomalyEnt, bool v 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 10d303cd1d4..ffd0daee5bd 100644 --- a/Content.Shared/Atmos/Atmospherics.cs +++ b/Content.Shared/Atmos/Atmospherics.cs @@ -104,6 +104,21 @@ public static class Atmospherics 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 @@ public static class Atmospherics /// 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 @@ public static class Atmospherics /// 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 @@ public static class Atmospherics /// /// 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 @@ public static class Atmospherics 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 115cb548929..cc3914fe677 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(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) +{ /// - /// Atmospheric data is gathered in the system and sent to the user + /// Name of the tab in the UI. /// - [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; - } - } - + public readonly string Name = Name; /// - /// Contains information on a gas mix entry, turns into a tab in the UI + /// Volume of this gas mixture. /// - [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; - } - } - + public readonly float Volume = Volume; /// - /// Individual gas entry data for populating the UI + /// Pressure of this gas mixture. /// - [Serializable, NetSerializable] - public struct GasEntry - { - public readonly string Name; - public readonly float Amount; - public readonly string Color; + 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; +} - public GasEntry(string name, float amount, string color) - { - Name = name; - Amount = amount; - Color = color; - } +/// +/// 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; +} - public override string ToString() - { - // e.g. "Plasma: 2000 mol" - return Loc.GetString( - "gas-entry-info", - ("gasName", Name), - ("gasAmount", 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 00000000000..573d3cbdf2e --- /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 7ca60985557..31fa3a0d5a9 100644 --- a/Content.Shared/Atmos/Components/GasTankComponent.cs +++ b/Content.Shared/Atmos/Components/GasTankComponent.cs @@ -5,18 +5,12 @@ 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 00000000000..e05d80c96ca --- /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 00000000000..31e6a4f6c42 --- /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 3852c30974c..5b953e6df4e 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs @@ -86,4 +86,19 @@ public bool TryGetExposedMixture(Entity entity, [NotNullWhe 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 c7267393dd5..240711f048a 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs @@ -16,10 +16,18 @@ due to sandboxing. */ /// - /// 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 virtual void InitializeGases() 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 @@ Most usages are going to want the scaled value anyway. 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 @@ protected float GetHeatCapacity(GasMixture mixture) 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 @@ protected float GetHeatCapacity(GasMixture mixture) /// 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 04f02219947..47e3219efc4 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 override void Initialize() { base.Initialize(); - _internalsQuery = GetEntityQuery(); - InitializeBreathTool(); InitializeGases(); InitializeCVars(); diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs index 4158c687bd0..ffb7d9f865a 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs @@ -6,16 +6,14 @@ 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 @@ private void OnGasTankToggleInternals(Entity ent, ref GasTankT 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 @@ private void OnExamined(EntityUid uid, GasTankComponent component, ExaminedEvent 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 @@ private void OnActionToggle(Entity gasTank, ref ToggleActionEv 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 bool ConnectToInternals(Entity ent, EntityUid? user = n 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 bool DisconnectFromInternals(Entity ent, EntityUid? use 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 23ea8fa8fa7..e9c697c53c9 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs @@ -31,8 +31,7 @@ public override void Initialize() 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 40d76684ee8..2c19638ee01 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,37 +20,17 @@ public sealed partial class GasCanisterComponent : Component, IGasMixtureHolder [DataField] public ItemSlot GasTankSlot = new(); - [DataField("gasMixture")] - public GasMixture Air { get; set; } = new(); - - /// - /// Last recorded pressure, for appearance-updating purposes. - /// - public float LastPressure = 0f; - /// - /// Minimum release pressure possible for the release valve. + /// The safety release valve on this gas canister. Automatically opens + /// when is reached. /// - [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; + [DataField] + public bool SafetyValveOpen; /// - /// Whether the release valve is open on the canister. + /// Last recorded pressure, for appearance-updating purposes. /// - [DataField, AutoNetworkedField] - public bool ReleaseValve = false; + public float LastPressure = 0f; [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 a7562689cab..8fed3f0eb7b 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 @@ 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 @@ private void OnHoldingTankEjectMessage(EntityUid uid, GasCanisterComponent canis 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 @@ private void OnCanisterChangeReleasePressure(EntityUid uid, GasCanisterComponent 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 @@ private void OnCanisterInsertAttempt(EntityUid uid, GasCanisterComponent compone 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 86304dfddaf..bb92d0307d3 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; - -namespace Content.Shared.Atmos.Prototypes +using Robust.Shared.Utility; + +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: Control gas amount necessary for overlay to appear - // TODO: Add interfaces for gas behaviours e.g. breathing, burning - - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// Specific heat for gas. - /// - [DataField("specificHeat")] - public float SpecificHeat { get; private set; } - - /// - /// Heat capacity ratio for gas - /// - [DataField("heatCapacityRatio")] - public float HeatCapacityRatio { get; private set; } = 1.4f; - - /// - /// Molar mass of gas - /// - [DataField("molarMass")] - public float MolarMass { get; set; } = 1f; - - - /// - /// Minimum amount of moles for this gas to be visible. - /// - [DataField("gasMolesVisible")] - public float GasMolesVisible { get; private set; } = 0.25f; - - /// - /// Visibility for this gas will be max after this value. - /// - public float GasMolesVisibleMax => GasMolesVisible * GasVisibilityFactor; - - [DataField("gasVisbilityFactor")] - 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; - - /// - /// 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; - - /// - /// State for the gas RSI overlay. - /// - [DataField("gasOverlaySprite")] - public string GasOverlaySprite { get; set; } = string.Empty; - - /// - /// Path to the tile overlay used when this gas appears visible. - /// - [DataField("overlayPath")] - public string OverlayPath { get; private set; } = string.Empty; - - /// - /// The reagent that this gas will turn into when inhaled. - /// - [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string? Reagent { get; private set; } = default!; - - [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; - } + // TODO: Add interfaces for gas behaviours e.g. breathing, burning + + /// + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// The name of the gas as shown to the player. + /// + [DataField(required: true)] + public LocId Name; + + /// + /// The abbreviation of the name. For example O₂ for Oxygen. + /// Used for UI purposes. + /// + [DataField(required: true)] + public LocId Abbreviation; + + /// + /// 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; + + /// + /// 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] + public float GasMolesVisible = 0.25f; + + /// + /// Visibility for this gas will be max after this value. + /// + [ViewVariables] + public float GasMolesVisibleMax => GasMolesVisible * GasVisibilityFactor; + + /// + /// Multiplier that decides when a gas will be at maximum visibility. + /// + [DataField] + public float GasVisibilityFactor = Atmospherics.FactorGasVisibleMax; + + /// + /// 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; + + /// + /// The reagent that this gas will turn into when inhaled or condensed. + /// + [DataField] + public ProtoId? Reagent; + + /// + /// The color of the gas used for UI purposes. + /// + [DataField] + public Color Color = Color.White; + + /// + /// 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; + + /// + /// 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; } diff --git a/Content.Shared/Audio/SharedAmbientSoundSystem.cs b/Content.Shared/Audio/SharedAmbientSoundSystem.cs index 0a52b7c58e2..fd5a474a70a 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 6f62d7b34eb..e8f8ffef29d 100644 --- a/Content.Shared/Bed/BedSystem.cs +++ b/Content.Shared/Bed/BedSystem.cs @@ -26,7 +26,7 @@ public sealed class BedSystem : EntitySystem [Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!; [Dependency] private readonly SleepingSystem _sleepingSystem = default!; - private EntityQuery _sleepingQuery; + [Dependency] private readonly EntityQuery _sleepingQuery = default!; public override void Initialize() { @@ -41,8 +41,6 @@ public override void Initialize() 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 2e17485bded..d0e3bef52bd 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 @@ private void OnToggleAction(EntityUid uid, BlockingComponent component, ToggleAc 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 @@ private void OnToggleAction(EntityUid uid, BlockingComponent component, ToggleAc 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 bool StartBlocking(EntityUid item, BlockingComponent component, EntityUid 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 @@ private void StopBlockingHelper(EntityUid uid, BlockingComponent component, Enti 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 4cd5a4e7924..3f01b4e2ba8 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 override void Initialize() 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 6273b0640bf..a7f51631104 100644 --- a/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs +++ b/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs @@ -82,7 +82,7 @@ public bool TryGatherMarkingsData(Entity ent, [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 705a931102d..8a7a00001a8 100644 --- a/Content.Shared/Body/Systems/LungSystem.cs +++ b/Content.Shared/Body/Systems/LungSystem.cs @@ -37,10 +37,10 @@ private void OnGotEquipped(Entity ent, ref GotEquippedEvent 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 ec0521b7b63..bde65b99fee 100644 --- a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs +++ b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs @@ -278,7 +278,8 @@ private void OnRejuvenate(Entity ent, ref RejuvenateEvent 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 bool TryRegulateBloodLevel(Entity ent, FixedPoint2 || 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 af79fd1830a..31c34a42f99 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 833938b765f..eccee011512 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 da165ce74a7..00000000000 --- 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 00000000000..ec0cba53866 --- /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 9d044d18506..291d3abbc31 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 override void Initialize() base.Initialize(); SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnHijackSuccess); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -25,6 +27,23 @@ private void OnMapInit(Entity ent, ref MapInitEvent 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 9940a607052..9a4ff4454ef 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 f2c5c82ca94..f0a7bb44ba3 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 00000000000..c235ea071a9 --- /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 00000000000..40df5b2bad2 --- /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 00000000000..affd1f3a8ff --- /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 8e74f835373..bef822b720f 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 385ed5c9e98..63a0a67f9b5 100644 --- a/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs @@ -209,14 +209,14 @@ public void Draw(Entity ent, EntityUid target, Entit 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 3406038e9c2..e0cb17f51fb 100644 --- a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs @@ -2,9 +2,7 @@ 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.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 override void Initialize() SubscribeLocalEvent(OnDevourAction); SubscribeLocalEvent(OnDevourWindup); SubscribeLocalEvent(OnDevourConsume); - SubscribeLocalEvent>(OnConsumeAttemptTick); SubscribeLocalEvent(OnShutdown); } @@ -64,81 +54,25 @@ private void OnShutdown(Entity ent, ref ComponentShut } } - //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) - { - - var curTime = _timing.CurTime; - - if (curTime < ent.Comp.NextTick) - return; - - ConsumeDamageTick(eventData.Event.Target, ent.Comp, eventData.Event.User); - ent.Comp.NextTick += ent.Comp.DamageTimeBetweenTicks; - Dirty(ent, ent.Comp); - } - - private void ConsumeDamageTick(EntityUid? target, ChangelingDevourComponent comp, EntityUid? user) - { - if (target == null) - return; - - _damageable.ChangeDamage(target.Value, comp.DamagePerTick, true, true, user); - } - - /// - /// Checkes if the targets outerclothing is beyond a DamageCoefficientThreshold to protect them from being devoured. - /// - /// The Targeted entity - /// Changelings Devour Component - /// Is the target Protected from the attack - private bool IsTargetProtected(EntityUid target, Entity ent) - { - var ev = new CoefficientQueryEvent(SlotFlags.OUTERCLOTHING); - - RaiseLocalEvent(target, ev); - - foreach (var compProtectiveDamageType in ent.Comp.ProtectiveDamageTypes) - { - if (!ev.DamageModifiers.Coefficients.TryGetValue(compProtectiveDamageType, out var coefficient)) - continue; - if (coefficient < 1f - ent.Comp.DevourPreventionPercentageThreshold) - return true; - } - - return false; - } - + // The action was used. + // Start the first doafter for the windup. private void OnDevourAction(Entity ent, ref ChangelingDevourActionEvent args) { - if (args.Handled || _whitelistSystem.IsWhitelistFailOrNull(ent.Comp.Whitelist, args.Target) - || !HasComp(ent)) + 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)) - { - _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-rotting"), args.Performer, args.Performer, PopupType.Medium); + if (!CanDevour(ent.AsNullable(), target)) return; - } - - if (IsTargetProtected(target, ent)) - { - _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-protected"), ent, ent, PopupType.Medium); - return; - } if (_net.IsServer) { - var pvsSound = _audio.PlayPvs(ent.Comp.DevourWindupNoise, ent); - if (pvsSound != null) - ent.Comp.CurrentDevourSound = pvsSound.Value.Entity; + 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}"); @@ -146,7 +80,7 @@ private void OnDevourAction(Entity ent, ref Changelin _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, ent, ent.Comp.DevourWindupTime, new ChangelingDevourWindupDoAfterEvent(), ent, target: target, used: ent) { BreakOnMove = true, - BlockDuplicate = true, + CancelDuplicate = true, DuplicateCondition = DuplicateConditions.None, }); @@ -160,110 +94,171 @@ private void OnDevourAction(Entity ent, ref Changelin PopupType.MediumCaution); } + // 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) { - var curTime = _timing.CurTime; args.Handled = true; - - if (!Exists(ent.Comp.CurrentDevourSound)) - _audio.Stop(ent.Comp.CurrentDevourSound!); + ent.Comp.CurrentDevourSound = _audio.Stop(ent.Comp.CurrentDevourSound); if (args.Cancelled) return; + 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, - args.User, - args.User, + ent.Owner, + ent.Owner, PopupType.LargeCaution); if (_net.IsServer) - { - var pvsSound = _audio.PlayPvs(ent.Comp.ConsumeNoise, ent); - - if (pvsSound != null) - ent.Comp.CurrentDevourSound = pvsSound.Value.Entity; - } + ent.Comp.CurrentDevourSound = _audio.PlayPvs(ent.Comp.ConsumeNoise, ent)?.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"); + _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: args.Target, + target: target, used: ent) { - AttemptFrequency = AttemptFrequency.EveryTick, BreakOnMove = true, - BlockDuplicate = 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; - var target = args.Target; + ent.Comp.CurrentDevourSound = _audio.Stop(ent.Comp.CurrentDevourSound); - if (target == null) + if (args.Cancelled) return; - if (Exists(ent.Comp.CurrentDevourSound)) - _audio.Stop(ent.Comp.CurrentDevourSound!); - - if (args.Cancelled) + if (args.Target is not { } target) return; - if (!_mobState.IsDead((EntityUid)target)) - { - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} unsuccessfully devoured {ToPrettyString(args.Target):player}'s identity"); - _popupSystem.PopupClient(Loc.GetString("changeling-devour-consume-failed-not-dead"), args.User, args.User, PopupType.Medium); + // 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(args.User, EntityManager))); - var othersMessage = Loc.GetString("changeling-devour-consume-complete-others", ("user", Identity.Entity(args.User, EntityManager))); + 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, - args.User, - args.User, + ent.Owner, + ent.Owner, 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(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); + } + + /// + /// 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)) { - _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 (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-cannot-devour"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } - if (_inventorySystem.TryGetSlotEntity(target.Value, "jumpsuit", out var item) - && TryComp(item, out var butcherable)) - RipClothing(target.Value, (item.Value, butcherable)); + 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; } - Dirty(ent); + 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; } - private void RipClothing(EntityUid victim, Entity item) + /// + /// Checks if the target's outerclothing is beyond a DamageCoefficientThreshold to protect them from being devoured. + /// + /// The Targeted entity + /// Changelings Devour Component + /// Is the target Protected from the attack + private bool IsTargetProtected(EntityUid target, Entity ent) { - var spawnEntities = EntitySpawnCollection.GetSpawns(item.Comp.SpawnedEntities, _robustRandom); + var ev = new CoefficientQueryEvent(SlotFlags.OUTERCLOTHING); + + RaiseLocalEvent(target, ev); - foreach (var proto in spawnEntities) + foreach (var compProtectiveDamageType in ent.Comp.ProtectiveDamageTypes) { - // TODO: once predictedRandom is in, make this a Coordinate offset of 0.25f from the victims position - PredictedSpawnNextToOrDrop(proto, victim); + if (!ev.DamageModifiers.Coefficients.TryGetValue(compProtectiveDamageType, out var coefficient)) + continue; + if (coefficient < 1f - ent.Comp.DevourPreventionPercentageThreshold) + return true; } - PredictedQueueDel(item.Owner); + return false; } } diff --git a/Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs b/Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs new file mode 100644 index 00000000000..8082c3bb87b --- /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 e5551473528..50f5ceb5470 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 9e4b1d4d03c..8ec2d36cd86 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs @@ -5,10 +5,11 @@ 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 override void Initialize() 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 @@ private void OnTransformAction(Entity ent, 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 void TransformInto(Entity ent, EntityUid t 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 void TransformInto(Entity ent, EntityUid t 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 void TransformInto(Entity ent, EntityUid t }); } - 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 @@ private void OnSuccessfulTransform(Entity ent, 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 @@ private void OnSuccessfulTransform(Entity ent, 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 a3b620d68ad..8e28cd48ad3 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 override void Initialize() SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); SubscribeLocalEvent(OnStoredRemove); + + SubscribeLocalEvent(OnDevouredShutdown); } private void OnPlayerAttached(Entity ent, ref PlayerAttachedEvent args) @@ -57,7 +53,32 @@ private void OnShutdown(Entity ent, ref ComponentSh { 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 void CleanupChangelingNullspaceIdentities(Entity + /// 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 void CleanupChangelingNullspaceIdentities(Entity /// 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 void CleanupChangelingNullspaceIdentities(EntityThe 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 void CleanupChangelingNullspaceIdentities(Entity + /// 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 @@ private void HandlePvsOverride(EntityUid uid, EntityUid identity) /// 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 void HandOverPvsOverride(Entity ent, ICommon { 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 8fe17368cc0..3928ff71790 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 override void Initialize() 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 9cc0a55ce1c..257817394eb 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 0b531561ca9..ffc4dd06ddc 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 e44264fb41e..ca21d686cf1 100644 --- a/Content.Shared/Cloning/SharedCloningSystem.cs +++ b/Content.Shared/Cloning/SharedCloningSystem.cs @@ -1,16 +1,42 @@ +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!; + + /// + /// Spawns a clone of the given humanoid mob at the specified location or in nullspace. + /// + public virtual bool TryCloning( + EntityUid original, + MapCoordinates? coords, + ProtoId settingsId, + [NotNullWhen(true)] out EntityUid? clone) + { + clone = null; + return false; + } + /// /// 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) + /// The clone settings prototype id containing the list of components to clone. + public virtual void CloneComponents( + EntityUid original, + EntityUid clone, + ProtoId settings) { } @@ -19,8 +45,93 @@ public virtual void CloneComponents(EntityUid original, EntityUid clone, Cloning /// /// 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) + /// 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 5caddda18be..4c61c67c6dd 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 00000000000..0790d476720 --- /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 6ebaa94e2e1..6c7b024b292 100644 --- a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs @@ -88,22 +88,22 @@ protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, 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 @@ private void OnItemStripped(Entity ent, ref BeforeItemStrippe #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 76b7b9aa66d..5c0e936bdf1 100644 --- a/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs @@ -22,8 +22,8 @@ public override void Initialize() 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 @@ private void OnUnequipped(Entity ent, ref GotUnequippe 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 00000000000..e9a61dd8b39 --- /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 49df7aee943..9890832ee79 100644 --- a/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs @@ -63,7 +63,7 @@ private void OnEquipped(Entity entity, ref GotEquipped 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 ab0c41c5c7b..e7227352677 100644 --- a/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs @@ -23,7 +23,7 @@ private void OnUnequip(Entity selfUnremovableC 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 07aae61f83d..ccd0e04953f 100644 --- a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs @@ -66,12 +66,20 @@ private void OnPrototypeReload(EntityUid uid, ChameleonClothingComponent compone 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 @@ private void OnVerb(Entity ent, ref GetVerbsEvent + /// 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 a2b7d016410..ce167e06f4e 100644 --- a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs @@ -160,7 +160,7 @@ private void OnToggleableUnequip(EntityUid uid, ToggleableClothingComponent comp // 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 3985bd30510..495dddfe693 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 override void Initialize() { base.Initialize(); - _physicsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnInteractUsing, before: new[] { typeof(ItemSlotsSystem) }, after: new[] { typeof(SharedConstructionSystem) }); SubscribeLocalEvent(OnAnchorComplete); @@ -235,12 +233,8 @@ private void TryAnchor(EntityUid uid, EntityUid userUid, EntityUid usingUid, // 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 @@ private bool Valid( 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 f8efa20afa3..577b7dfe2c9 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -795,14 +795,14 @@ private void CheckAct(EntityUid uid, CuffableComponent component, CancellableEnt 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 00000000000..51a80a12fea --- /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 99b055a6456..00000000000 --- 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 9a53d83eb26..820fc3c90bb 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 @@ private void OnEntityExit(EntityUid uid, DamageContactsComponent component, ref 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 00000000000..a47b239330a --- /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 bbb7bfda323..00000000000 --- 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 c9cf625cbc2..36f997c5a97 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.Events.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.Events.cs @@ -23,9 +23,6 @@ public override void Initialize() 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 2d707568f0e..fd8564cf3ca 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 ca030d5e5d1..1832eeee14c 100644 --- a/Content.Shared/Damage/Systems/SharedStaminaSystem.cs +++ b/Content.Shared/Damage/Systems/SharedStaminaSystem.cs @@ -45,6 +45,8 @@ public abstract partial class SharedStaminaSystem : EntitySystem [Dependency] private readonly StatusEffectsSystem _status = default!; [Dependency] protected readonly SharedStunSystem StunSystem = default!; + [Dependency] private readonly EntityQuery _stamQuery = default!; + /// /// How much of a buffer is there between the stun duration and when stuns can be re-applied. /// @@ -162,13 +164,12 @@ private void OnMeleeHit(EntityUid uid, StaminaDamageOnHitComponent component, Me 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)); @@ -351,14 +352,13 @@ public override void Update(float frameTime) { 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); @@ -397,8 +397,7 @@ private void EnterStamCrit(EntityUid uid, StaminaComponent? component = null) component.Critical = true; component.StaminaDamage = component.CritThreshold; - if (StunSystem.TryUpdateParalyzeDuration(uid, component.StunTime)) - StunSystem.TrySeeingStars(uid); + StunSystem.TryUpdateParalyzeDuration(uid, component.StunTime); // Give them buffer before being able to be re-stunned component.NextUpdate = Timing.CurTime + component.StunTime + StamCritBufferTime; diff --git a/Content.Shared/Destructible/SharedDestructibleSystem.cs b/Content.Shared/Destructible/SharedDestructibleSystem.cs index 4917ded3267..076125bc8af 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 74bbc259c6a..9e4e964f0aa 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 @@ private void OnSourceStartup(Entity source, ref Compo /// 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 79f89a1d253..8bc922708f7 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 14c2ae67d3e..5e4b742e10f 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 override void Update(float frameTime) 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 override void Update(float frameTime) try { - Update(uid, active, comp, time, xformQuery, handsQuery); + Update(uid, active, comp, time); } // ReSharper disable once RedundantCatchClause #if EXCEPTION_TOLERANCE @@ -87,9 +87,7 @@ protected void Update( EntityUid uid, ActiveDoAfterComponent active, DoAfterComponent comp, - TimeSpan time, - EntityQuery xformQuery, - EntityQuery handsQuery) + TimeSpan time) { var dirty = false; @@ -122,7 +120,7 @@ protected void Update( continue; } - if (ShouldCancel(doAfter, xformQuery, handsQuery)) + if (ShouldCancel(doAfter)) { InternalCancel(doAfter, comp); dirty = true; @@ -196,27 +194,24 @@ private void TryComplete(DoAfter doAfter, DoAfterComponent component) } } - 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 @@ private bool ShouldCancel(DoAfter doAfter, // 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 0b72692ea05..8841d8dd268 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -251,7 +251,7 @@ public bool TryStartDoAfter(DoAfterArgs args, [NotNullWhen(true)] out DoAfterId? 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 6b3fcfad7e4..136f49b4fc3 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 e66dff2611d..057fd5982df 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 5bbde04aedb..23c8c463e6b 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 override void Initialize() 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 @@ private void OnGetPryMod(EntityUid uid, AirlockComponent component, ref GetPryTi /// /// 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 void SetEmergencyAccess(Entity ent, bool value, EntityU 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 void SetSafety(AirlockComponent component, bool value) 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 e1ef05c8cec..0c902798b15 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 override void Initialize() 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 aa5132e5967..9614933247d 100644 --- a/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs +++ b/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs @@ -30,6 +30,6 @@ public override string EntityEffectGuidebookText(IPrototypeManager prototype, IE 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 00000000000..2ab68708cc9 --- /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 c54912191e4..9b3d731fcc3 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 e220d3ef773..7c231fe157f 100644 --- a/Content.Shared/Examine/ExamineSystemShared.Group.cs +++ b/Content.Shared/Examine/ExamineSystemShared.Group.cs @@ -13,8 +13,6 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent>(OnGroupExamineVerb); - - _ghostQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 7c063f6ce98..8af9a91e5c9 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -21,6 +21,8 @@ public abstract partial class ExamineSystemShared : EntitySystem [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 @@ public abstract partial class ExamineSystemShared : EntitySystem 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 a2ecb4d034c..ca0b8694a73 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs @@ -24,11 +24,11 @@ private void OnBlindfoldTrySee(Entity blindfold, ref Invento 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 099753d51e0..aa0a5a1574b 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs @@ -44,12 +44,12 @@ public void UpdateBlurMagnitude(Entity ent) 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 73b06cac9b5..4ae08ed95b5 100644 --- a/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs +++ b/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs @@ -46,10 +46,11 @@ private void OnCheckLockAccess(Entity ent, ref Check /// 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 bool IsAllowed(Entity target, EntityUid user 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 d1a8b882d9a..9484add5ef9 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 1c18ca16767..70a61765ffc 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 4b8ff3ab7b7..30f11efdeb6 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 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 override void Initialize() 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 @@ private void OnFlashMeleeHit(Entity ent, ref MeleeHitEvent args) if (!ent.Comp.FlashOnMelee || !args.IsHit || !args.HitEntities.Any() || - !UseFlash(ent, args.User)) + !TryUseFlashItem(ent.AsNullable(), args.User)) { return; } @@ -82,41 +81,66 @@ private void OnFlashMeleeHit(Entity ent, ref MeleeHitEvent args) { 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 @@ private bool UseFlash(Entity ent, EntityUid? user) 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 a6f4a8b31f9..07e2938585a 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 override void Initialize() 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 56ba5e84417..18cae762f8b 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 override void Initialize() 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 71c51c55f1e..71e269b98c2 100644 --- a/Content.Shared/Friction/TileFrictionController.cs +++ b/Content.Shared/Friction/TileFrictionController.cs @@ -28,14 +28,14 @@ public sealed class TileFrictionController : VirtualController [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 @@ public override void Initialize() 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 6e41f4bdea9..104a08c7b5a 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 3fc9c081cb8..37f5c9e6939 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 7d3561a79f9..726c20d31a6 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 @@ public override void Initialize() 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 41cf616cc4b..a1dbed382c2 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 d9a0a70d940..50eb6748174 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 override void Initialize() // 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 4b96f89d811..4146d4ec647 100644 --- a/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs +++ b/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs @@ -17,27 +17,27 @@ public override void Initialize() 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 c19de9629a4..17794807ed5 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs @@ -80,6 +80,10 @@ public bool TryPickup( 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 00000000000..b6f1d60c796 --- /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 00000000000..ed1d3ca74bf --- /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 00000000000..d3e5810fbab --- /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 82cc4924fb1..9622e814c52 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 void Draw(EntityUid implanter, EntityUid user, EntityUid target, Implante 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 void Draw(EntityUid implanter, EntityUid user, EntityUid target, Implante } } - 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 6595789977b..52e25a0b9a4 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 @@ private void RelayToImplantEvent(EntityUid uid, ImplantedComponent component, 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 @@ private void RelayToImplantEvent(EntityUid uid, ImplantedComponent component, /// 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 88d42b8a286..f5deabdf7e5 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -75,16 +75,16 @@ public abstract partial class SharedInteractionSystem : EntitySystem [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 @@ public abstract partial class SharedInteractionSystem : EntitySystem 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 00000000000..6ea408107b6 --- /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 00000000000..a084e1536ad --- /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 2914dd469ee..97babafe261 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 77fbe83e8af..a152a54b445 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 a74b3e2d7cc..4e557cc1579 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 4e1764a7d2d..3f553c95917 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 sealed class DidUnequipEvent : UnequippedEventBase -{ - public DidUnequipEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; } -public sealed class GotUnequippedEvent : UnequippedEventBase -{ - public GotUnequippedEvent(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); + +/// +/// 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 ca069b71cad..311761e1e2b 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -133,7 +133,7 @@ public bool TryEquip(EntityUid actor, EntityUid target, EntityUid itemUid, strin { 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 bool TryEquip(EntityUid actor, EntityUid target, EntityUid itemUid, strin 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 bool TryEquip(EntityUid actor, EntityUid target, EntityUid itemUid, strin 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 @@ private bool TryUnequip( 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 @@ private bool TryUnequip( 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 @@ private bool TryUnequip( 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 @@ private bool TryUnequip( 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 3f0bdf10d45..005f78eef7e 100644 --- a/Content.Shared/Inventory/InventorySystem.Slots.cs +++ b/Content.Shared/Inventory/InventorySystem.Slots.cs @@ -263,6 +263,10 @@ public InventorySlotEnumerator(SlotDefinition[] slots, ContainerSlot[] container _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 bool MoveNext([NotNullWhen(true)] out ContainerSlot? container) 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 2bd113e22b1..113457e564b 100644 --- a/Content.Shared/Inventory/SelfEquipOnlySystem.cs +++ b/Content.Shared/Inventory/SelfEquipOnlySystem.cs @@ -23,7 +23,7 @@ private void OnBeingEquipped(Entity ent, ref BeingEquipp 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 @@ private void OnBeingUnequipped(Entity ent, ref BeingUneq 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 c260529ded9..2315961d97c 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 @@ private void OnActivate(Entity ent, ref ActivateInWorldEven /// 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 bool TrySetActive(Entity ent, bool active, EntityUi /// 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 bool TryActivate(Entity ent, EntityUid? user = null /// 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 @@ private void TurnOnOnWielded(Entity ent, ref ItemWieldedEve 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 39be05a1480..00000000000 --- 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 c3b4bd73965..00000000000 --- 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 2de7c5591fd..00000000000 --- 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 7185d603052..ff0693a2575 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 82cae19ec10..9bdb39ecafd 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -15,6 +15,7 @@ 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 @@ private void WorldSpawnSpellHelper(List entityEntries, EntityC #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 void Knock(EntityUid performer, float range) 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 @@ protected virtual void OnRandomGlobalSpawnSpell(RandomGlobalSpawnSpellEvent ev) 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 f5477441d92..005b05b4d01 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 bool IsTileBlocked(EntityUid gridUid, 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 bool IsTileBlocked(EntityUid gridUid, 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 33168db1db3..ad7aeff7dd4 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 override void Initialize() 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 386d0989d97..6dbe11776d5 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 ac1a00d5798..f3b8024d6db 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 override void Initialize() 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 bb325ef7977..7767ab3ad04 100644 --- a/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs +++ b/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs @@ -47,7 +47,8 @@ public abstract class SharedSuitSensorSystem : EntitySystem [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly SharedBloodstreamSystem _bloodstream = default!; - private EntityQuery _sensorQuery; + [Dependency] private readonly EntityQuery _sensorQuery = default!; + public override void Initialize() { base.Initialize(); @@ -63,8 +64,6 @@ public override void Initialize() SubscribeLocalEvent(OnInsert); SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnSuitSensorDoAfter); - - _sensorQuery = GetEntityQuery(); } /// @@ -407,18 +406,17 @@ public void SetAllSensors(EntityUid target, SuitSensorMode mode, SlotFlags slots status.TotalDamageThreshold = totalDamageThreshold; ApplyTsfVitals(sensor.User.Value, status); 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 3e31fae1acb..af9ee038e2c 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 00000000000..88fd557bbff --- /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 c8e5c55bae8..1dfa2d2379a 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 d805138ac30..ff192698f5a 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 334539634fa..0e471b01877 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 e8098d72a24..8e76e5a47e0 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 a6565e4d334..d3bec4d8d69 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 c2daf3e3617..47c8c4178b0 100644 --- a/Content.Shared/Mind/Filters/MindFilter.cs +++ b/Content.Shared/Mind/Filters/MindFilter.cs @@ -3,25 +3,25 @@ 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 263d15d8128..0886e0716d3 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 5b64fc36c61..275f49872d6 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 a21642c4ecf..8571c35aa2c 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -14,6 +14,7 @@ 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 MindStringRepresentation MindOwnerLoggingString(MindComponent mind) 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 b0a6eb8c808..06f081f525a 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -182,14 +182,14 @@ private void CheckAct(EntityUid target, MobStateComponent component, Cancellable 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 0497f3cce0b..c55c4d1bd64 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 197d7cfd7c8..172ab449b49 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 0fcdf7ebed0..03e37ed3e70 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -614,4 +614,21 @@ public bool TryStopPull(EntityUid pullableUid, PullableComponent pullable, Entit 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 23060b77e17..0ee8316e8cd 100644 --- a/Content.Shared/Movement/Systems/MovementModStatusSystem.cs +++ b/Content.Shared/Movement/Systems/MovementModStatusSystem.cs @@ -53,7 +53,7 @@ private void OnRefreshRelay( 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) @@ -159,8 +159,7 @@ float sprintSpeedModifier /// 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 @@ -171,7 +170,8 @@ float sprintSpeedModifier 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 0d6a9858ff4..8485dc4386d 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 @@ private void OnMapInit(EntityUid uid, JetpackComponent component, MapInitEvent a 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 598e4b564a1..d55596ad7d0 100644 --- a/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs +++ b/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs @@ -99,13 +99,15 @@ private void OnClone(Entity ent, ref CloningEvent args) 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 fab9552271b..63a92fc4ab5 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 override void Initialize() }, 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 bda05da0195..12f23ddf094 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 override void Initialize() 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 e69f0c2f7ad..e52573ab032 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 @@ private void OnShutdown(Entity ent, ref ComponentShut { 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 @@ private void OnTrackerShutdown(Entity ent, ref { 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 void DeAggroEntity(Entity ent, EntityUid targ 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 c2425b92648..053e7aa8b4a 100644 --- a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs +++ b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs @@ -23,7 +23,7 @@ public override void Initialize() /// 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 01f860da125..e5325c55ba7 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs @@ -98,7 +98,7 @@ private void OnCreateStarAttempt(Entity ent, ref CreateItemA /// 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 62ddcecb81b..b25c1d79ac8 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 b6bd1240849..91ba3b3d6e3 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 6779fe36667..08c583ff7be 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; + + /// + /// 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; +} - [Serializable, NetSerializable] - public enum CreamPiedVisuals - { - Creamed, - } +/// +/// 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 931d47838ba..65bc5519ed5 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 1a1418b644c..9b30e5d4380 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 3e7c9da1222..a4d5d623912 100644 --- a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs +++ b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs @@ -43,9 +43,10 @@ private void OnUnremovableIngestion(Entity entity, ref In 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 8446cf5d0f5..3c70060b9ed 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 a0a82d63eff..84827ed0e39 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs @@ -1,72 +1,164 @@ +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; +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!; + + public override void Initialize() { - [Dependency] private SharedStunSystem _stunSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + base.Initialize(); - public override void Initialize() - { - base.Initialize(); + SubscribeLocalEvent(OnCreamPieHit); + SubscribeLocalEvent(OnCreamPieLand); + SubscribeLocalEvent(OnCreamPiedHitBy); + SubscribeLocalEvent(OnSlice); + SubscribeLocalEvent(OnRejuvenate); + } - SubscribeLocalEvent(OnCreamPieHit); - SubscribeLocalEvent(OnCreamPieLand); - SubscribeLocalEvent(OnCreamPiedHitBy); - } + /// + /// 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); - public void SplatCreamPie(Entity 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 { - // Already splatted! Do nothing. - if (creamPie.Comp.Splatted) - return; + var coordinates = Transform(creamPie).Coordinates; + _audio.PlayPvs(creamPie.Comp.Sound, coordinates); + } - creamPie.Comp.Splatted = true; + if (TryComp(creamPie, out var edibleComp)) + { + if (_solutions.TryGetSolution(creamPie.Owner, edibleComp.Solution, out _, out var solution)) + _puddle.TrySpillAt(creamPie.Owner, solution, out _, false); - SplattedCreamPie(creamPie); + _ingestion.SpawnTrash((creamPie.Owner, edibleComp)); } - protected virtual void SplattedCreamPie(Entity entity) { } + ActivatePayload(creamPie); + PredictedQueueDel(creamPie); + } - public void SetCreamPied(EntityUid uid, CreamPiedComponent creamPied, bool value) - { - if (value == creamPied.CreamPied) - return; + /// + /// 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)); + } - creamPied.CreamPied = value; + /// + /// 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 (TryComp(uid, out AppearanceComponent? appearance)) - { - _appearance.SetData(uid, CreamPiedVisuals.Creamed, value, appearance); - } - } + if (value == ent.Comp.CreamPied) + return; - private void OnCreamPieLand(Entity entity, ref LandEvent args) - { - SplatCreamPie(entity); - } + ent.Comp.CreamPied = value; + Dirty(ent); - private void OnCreamPieHit(Entity entity, ref ThrowDoHitEvent args) - { - SplatCreamPie(entity); - } + _appearance.SetData(ent.Owner, CreamPiedVisuals.Creamed, value); + } - private void OnCreamPiedHitBy(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) - { - if (!Exists(args.Thrown) || !TryComp(args.Thrown, out CreamPieComponent? creamPie)) return; + private void OnCreamPieLand(Entity ent, ref LandEvent args) + { + SplatCreamPie(ent); + } - SetCreamPied(uid, creamPied, true); + private void OnCreamPieHit(Entity ent, ref ThrowDoHitEvent args) + { + SplatCreamPie(ent); + } - CreamedEntity(uid, creamPied, args); + 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); + + // 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; + + // 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); + + var otherPlayers = Filter.PvsExcept(creamPied.Owner); + + // 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); + } - _stunSystem.TryUpdateParalyzeDuration(uid, TimeSpan.FromSeconds(creamPie.ParalyzeTime)); - } + private void OnRejuvenate(Entity ent, ref RejuvenateEvent args) + { + SetCreamPied(ent.AsNullable(), false); + } + + // 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. - 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 315f8d81153..bbefe4f4219 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 bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, O // 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 00000000000..215d87d62f7 --- /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 2dbbfb5efca..327cd608b51 100644 --- a/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs +++ b/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs @@ -3,19 +3,12 @@ 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 392cd46e72e..ee0a7fb0bc9 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 void LockUplink(Entity ent) /// 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 @@ private void OnSetRingtone(Entity ent, ref RingerSetRingtoneMes 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 @@ protected bool ToggleUplinkInternal(Entity ent) 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 75496d93b40..d99fffda748 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 override void Initialize() 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 00000000000..3fb065a6e7a --- /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 00000000000..e8b2334b257 --- /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 00000000000..a636b1ede23 --- /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 74b1a5026dc..26f9df94248 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 5d648032732..2f7e1dfdfd8 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 override void Initialize() // 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 aba41e2432a..c6695571155 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 b4ea4da5652..f295039f0f4 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -860,7 +860,17 @@ public static HumanoidCharacterProfile FromStream(Stream stream, ICommonSession var collection = IoCManager.Instance; // Corvax-Sponsors-Start - var sponsorPrototypes = IoCManager.Resolve().TryGetServerPrototypes(session.UserId, out var prototypes) ? prototypes.ToArray() : []; + string[] sponsorPrototypes; + try + { + var sponsorsManager = IoCManager.Resolve(); + sponsorPrototypes = sponsorsManager.TryGetServerPrototypes(session.UserId, out var prototypes) + ? prototypes.ToArray() : Array.Empty(); + } + catch (Exception) + { + sponsorPrototypes = Array.Empty(); + } profile.EnsureValid(session, collection!, sponsorPrototypes); // Corvax-Sponsors-End return profile; diff --git a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs index a00ec1d0c6d..56c3c2a9a6e 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 @@ private void UpdateTarget(Entity detector) { 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 @@ private void UpdateTarget(Entity detector) 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 874024a9059..51979f1c02c 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 00000000000..a431ffa4fde --- /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 2a8ad077c6e..3f91d0fbedd 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 override bool Check(EntityManager entManager, EntityUid uid) 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 override bool Check(EntityManager entManager, EntityUid uid) { 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 13108e88d6c..eb5b7073eb5 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 override bool Check(EntityManager entManager, EntityUid uid) 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 f95a38d2eb8..e526ab00dce 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 @@ private void OnRepulseAttractAction(Entity ent, ref Rep { 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 02ffc776161..0afb845a9fb 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 e434bba1d9a..846a28e6c7a 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 1d718dbdf33..451b994aff2 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 @@ private void OnCloning(Entity ent, ref CloningEvent args) 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 fb87c907f4d..e733b934834 100644 --- a/Content.Shared/Sericulture/SericultureSystem.cs +++ b/Content.Shared/Sericulture/SericultureSystem.cs @@ -41,13 +41,15 @@ private void OnClone(Entity ent, ref CloningEvent args) 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 891a5c9e121..c551cbc1128 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 override void Initialize() base.Initialize(); SubscribeLocalEvent(OnGridFixtureChange); - - _gridQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); } private void OnGridFixtureChange(EntityUid uid, FixturesComponent manager, GridFixtureChangeEvent args) @@ -58,7 +53,7 @@ private void OnGridFixtureChange(EntityUid uid, FixturesComponent manager, GridF 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 float GetFTLBufferRange(EntityUid shuttleUid, MapGridComponent? grid = nu 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 3095190710a..5d6f1624148 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 void InitializeModule() SubscribeLocalEvent>( OnComponentModuleInstalledRelay); - - _moduleQuery = GetEntityQuery(); } #region BorgModule diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs index fde4952f868..2eec51c410e 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 override void Initialize() { base.Initialize(); - _broadphaseQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - InitializeAirlock(); InitializeHeld(); InitializeLight(); @@ -623,6 +620,29 @@ private bool ValidateAi(Entity entity) 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 7ae27da497e..253c9df60c4 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 override void Initialize() { 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 b7bf0714570..eccfbe58d80 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 void UpdateSingularityLevel(EntityUid uid, byte oldValue, SingularityComp _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 void UpdateSingularityLevel(EntityUid uid, SingularityComponent? singular /// /// 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 e0084c11aba..2bd771da5d9 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 355d898dbf6..6ab466f2e09 100644 --- a/Content.Shared/Slippery/SlipperySystem.cs +++ b/Content.Shared/Slippery/SlipperySystem.cs @@ -31,18 +31,14 @@ public sealed class SlipperySystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [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 1356c8ecda6..372255701cf 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 4ed0da5b9e5..7cc93d25f68 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 770420d47e7..41129da13f0 100644 --- a/Content.Shared/Station/SharedStationSystem.Tracker.cs +++ b/Content.Shared/Station/SharedStationSystem.Tracker.cs @@ -50,7 +50,7 @@ public void UpdateStationTracker(Entity _xformQuery; - private EntityQuery _stationMemberQuery; + [Dependency] private readonly EntityQuery _stationMemberQuery = default!; /// public override void Initialize() @@ -20,9 +19,6 @@ public override void Initialize() base.Initialize(); InitializeTracker(); - - _xformQuery = GetEntityQuery(); - _stationMemberQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs index cd86aef9437..e04de09d658 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 bool TryGetRecord(StationRecordKey key, [NotNullWhen(true)] out T? ent 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 00000000000..e40a62af25b --- /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 1405a5fd624..e5614cd971e 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 override void Initialize() 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 a65d4fe063c..33f9b7f59ba 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs @@ -407,4 +407,56 @@ public bool TryGetEffectsEndTimeWithComp(EntityUid? target, out TimeSpan? end 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 cafe5075c96..cf81f66d9f0 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 @@ private void InitializeRelay() 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 512285eaf34..18dcf20c98a 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 override void Initialize() SubscribeLocalEvent(OnPrototypesReloaded); - _containerQuery = GetEntityQuery(); - _effectQuery = GetEntityQuery(); - ReloadStatusEffectsCache(); } diff --git a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs index f1d6a9c7ddd..c25b37865c0 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 @@ private void OnStartup(EntityUid uid, StepTriggerComponent component, ComponentS 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 override void Update(float frameTime) } } - 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 @@ private bool Update(EntityUid uid, StepTriggerComponent component, TransformComp 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 bc04c81f55f..f060cfe7003 100644 --- a/Content.Shared/Sticky/Systems/StickySystem.cs +++ b/Content.Shared/Sticky/Systems/StickySystem.cs @@ -102,7 +102,7 @@ private bool StartSticking(Entity ent, EntityUid target, Entity 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 @@ private void StartUnsticking(Entity ent, EntityUid user) } // 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 ecfcccc45b7..ba267aa5bf7 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 0d744a4fe90..dea4f79656d 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 27a15c87a6a..6d33fac51f0 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 93a534843a0..9618ee900b7 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -377,12 +377,8 @@ public bool CanInsert(EntityUid toInsert, EntityUid container, EntityStorageComp 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 6ede24e8b7e..5f5eaf0d1c4 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 override void Initialize() { 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 eb4ca1c0e36..a4c5b3c0272 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 00000000000..3b07e03e099 --- /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 7e92c6c5d63..00d912ede96 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -29,6 +29,7 @@ public ListingData(ListingData other) : this( other.Icon, other.Priority, other.ProductEntity, + other.ProductComponents, other.ProductAction, other.ProductUpgradeId, other.ProductActionEntity, @@ -55,6 +56,7 @@ public ListingData( SpriteSpecifier? icon, int priority, EntProtoId? productEntity, + EntProtoId? productComponents, EntProtoId? productAction, ProtoId? productUpgradeId, EntityUid? productActionEntity, @@ -77,6 +79,7 @@ bool applyToMob Icon = icon; Priority = priority; ProductEntity = productEntity; + ProductComponents = productComponents; ProductAction = productAction; ProductUpgradeId = productUpgradeId; ProductActionEntity = productActionEntity; @@ -147,6 +150,15 @@ bool applyToMob [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. /// @@ -222,6 +234,7 @@ public bool Equals(ListingData? listing) Name != listing.Name || Description != listing.Description || ProductEntity != listing.ProductEntity || + ProductComponents != listing.ProductComponents || ProductAction != listing.ProductAction || ProductEvent?.GetType() != listing.ProductEvent?.GetType() || RestockTime != listing.RestockTime || @@ -296,6 +309,7 @@ public ListingDataWithCostModifiers(ListingData 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 412114ce03b..fa0f29bb4ca 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 void RefreshAllListings(StoreComponent component) 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 HashSet GetAllListings() /// 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 IEnumerable GetAvailableListings( /// 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 00000000000..2d4b166c277 --- /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 00000000000..315d70d52a6 --- /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 d8cb9e6ca88..531788df1fe 100644 --- a/Content.Shared/Store/StoreUi.cs +++ b/Content.Shared/Store/StoreUi.cs @@ -37,9 +37,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 58f38c9db85..4fb2022deb9 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 @@ -461,17 +460,14 @@ private bool IntersectingStandingColliders(Entity entity) 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 4d49621a8a1..3ab14114289 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs @@ -10,6 +10,12 @@ public void InitializeAppearance() { 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 d064434eac6..2a3d96b9c75 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -392,14 +392,14 @@ private void OnAttempt(EntityUid uid, StunnedComponent stunned, CancellableEntit 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 a4fa463ee0e..a66060bf4cf 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -26,14 +26,12 @@ public abstract class SharedSubFloorHideSystem : EntitySystem [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 9a7c829f142..03e5e517d6b 100644 --- a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs +++ b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs @@ -80,12 +80,12 @@ private void OnTrayHandEquipped(Entity ent, ref GotEquippe 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 00000000000..f92f395eb9a --- /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 00000000000..27c4bf7c090 --- /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 21381e753f9..914ba4a63f3 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(); - - // 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.) + 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.) + /// [ViewVariables] public HashSet ActiveMonitors { get; } = new(); diff --git a/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs b/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs index b6743669e9e..1be49f8fc8d 100644 --- a/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs +++ b/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs @@ -5,7 +5,7 @@ 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 b75e2a4af10..fd74c10a591 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 69805fd5850..8620cca2923 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 override void Initialize() SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnShutdown); - - _xformQuery = GetEntityQuery(); } private void OnInteract(Entity ent, ref AfterInteractEvent args) @@ -224,7 +220,7 @@ private EntityUid GetTeleportingEntity(Entity ent) 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 c88d11b15a5..cfa5a43c597 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 c0de657fa97..0bbd9698aa0 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 float ConductHeat(this ref HeatContainer c, float temp, float delt /// 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 float ConductHeat(this ref HeatContainer cA, ref HeatContainer cB, /// 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 float ConductHeatQuery(this ref HeatContainer c, float temp, float } /// - /// 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 float ConductHeatQuery(this ref HeatContainer c, float temp, float /// 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 float ConductHeatToTemp(this ref HeatContainer c, float targetTemp } /// - /// 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 e745c593052..0e516446da7 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; + /// + /// Splits a into two, + /// modifying the original container to contain the specified fraction of the original heat capacity and the same temperature. + /// + /// 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 void Split(ref T c, float fraction = 0.5f) + where T : IHeatContainer + { + fraction = Math.Clamp(fraction, 0f, 1f); + var newHeatCapacity = c.HeatCapacity * fraction; + c.HeatCapacity = newHeatCapacity; } /// - /// Divides a source into a specified number of equal parts. + /// Divides a source into a specified number of equal parts. /// - /// 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 . + /// 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 HeatContainer[] Divide(this HeatContainer c, uint num) + 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.ThrowIfZero(num); + 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 75611d449a1..b12dd026ef4 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 @@ 1. Let Q be the amount of heat energy transferred from cA to cB. /// 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 void Equilibrate(this ref HeatContainer cA, ref HeatContainer cB) } /// - /// 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 void Equilibrate(this ref HeatContainer cA, ref HeatContainer cB, #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 void Equilibrate(this HeatContainer[] cN) } /// - /// 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 void Equilibrate(this ref HeatContainer cA, HeatContainer[] cN) } /// - /// 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 @@ 8. Summation. } /// - /// 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 @@ take the difference of the internal energy before and after the exchange. } /// - /// 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 5aa3dcbbdec..d3cb2e258a7 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 HeatContainer Merge(this HeatContainer[] cN) 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 ed1eadc74a7..42b7ae7ca8e 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs @@ -3,22 +3,22 @@ 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 void AddHeat(this ref HeatContainer c, float dQ) /// 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 float AddHeatQuery(this ref HeatContainer c, float dQ) } /// - /// 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 00000000000..95cf9c1728b --- /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 216349d6f59..a5e2dbab38c 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 override void Initialize() 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 244522f192f..93c07d1e7e7 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 c3ea10822e6..530c591f60e 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 void TryThrow(EntityUid uid, 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 void TryThrow(EntityUid uid, Vector2 direction, PhysicsComponent physics, TransformComponent transform, - EntityQuery projectileQuery, float baseThrowSpeed = 10.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, @@ -156,7 +151,7 @@ public void TryThrow(EntityUid uid, 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 a2743ca6caa..15ca721dbd1 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 @@ private void OnAfterInteract(EntityUid uid, FloorTileComponent component, AfterI 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 @@ private void OnAfterInteract(EntityUid uid, FloorTileComponent component, AfterI 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 @@ private void OnAfterInteract(EntityUid uid, FloorTileComponent component, AfterI _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 c6c47d539e0..2651fa7449a 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 override void Initialize() 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 8a122a1f65a..8fad7ea2316 100644 --- a/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs +++ b/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs @@ -53,7 +53,7 @@ private void OnGotEquipped(Entity ent, ref GotEqu 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 @@ private void OnGotUnequipped(Entity ent, ref Go 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 1e7261043f4..3ba9e761867 100644 --- a/Content.Shared/Trigger/Systems/TriggerSystem.cs +++ b/Content.Shared/Trigger/Systems/TriggerSystem.cs @@ -90,6 +90,9 @@ public bool ActivateTimerTrigger(Entity ent, EntityUid? 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 c8474acd332..7993b762346 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 @@ private void OnWarpPointExamine(EntityUid uid, WarpPointComponent component, Exa 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 4bcfe8a69fc..7c4b97abc4b 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 54e6ddbc1ce..03276a43ab8 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 6f4b078fe21..62839cf75b5 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); /// @@ -574,7 +576,7 @@ protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, Entity _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); } @@ -633,7 +635,15 @@ private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeU // 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, @@ -642,20 +652,16 @@ private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeU 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); @@ -728,6 +734,9 @@ private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeU $"{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) @@ -736,7 +745,7 @@ private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeU _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 00000000000..04d4a807745 --- /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 00000000000..896bfbcb6dc --- /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 831db83c3bf..ffd1d71cc06 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.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 @@ protected virtual void InitializeBallistic() SubscribeLocalEvent(OnBallisticRefillerMapInit); SubscribeLocalEvent(OnRefillerEmpPulsed); + + SubscribeLocalEvent(OnBallisticAmmoLoad); } private void OnBallisticRefillerMapInit(Entity entity, ref MapInitEvent _) @@ -63,7 +67,6 @@ private void OnBallisticAfterInteract(EntityUid uid, BallisticAmmoProviderCompon { if (args.Handled || !component.MayTransfer || - !Timing.IsFirstTimePredicted || args.Target == null || args.Used == args.Target || Deleted(args.Target) || @@ -324,11 +327,20 @@ public bool TryBallisticInsert( bool suppressInsertionSound = false ) { - if (!CanInsertBallistic(entity, inserted)) + inserted = _stack.GetOne(inserted); + var ammoEv = new BeforeAmmoLoadedEvent(); + RaiseLocalEvent(inserted, ref ammoEv); + + if (!ammoEv.CanLoad) + return false; + + var ammo = ammoEv.AmmoOverride ?? inserted; + + if (!CanInsertBallistic(entity, ammo)) return false; - entity.Comp.Entities.Add(inserted); - Containers.Insert(inserted, entity.Comp.Container); + entity.Comp.Entities.Add(ammo); + Containers.Insert(ammo, entity.Comp.Container); if (!suppressInsertionSound) { Audio.PlayPredicted(entity.Comp.SoundInsert, entity, user); @@ -343,11 +355,8 @@ public bool TryBallisticInsert( 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) @@ -369,6 +378,21 @@ private void OnRefillerEmpPulsed(Entity enti 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 9f4cc9d6aac..a75af8947d6 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs @@ -92,7 +92,7 @@ private void OnChamberActivationVerb(EntityUid uid, ChamberMagazineAmmoProviderC /// /// 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 @@ private bool TryTakeChamberEntity(EntityUid uid, [NotNullWhen(true)] out EntityU 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 e684d9aad1c..6830c2504b9 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs @@ -118,8 +118,7 @@ public bool TryRevolverInsert(Entity ent, EntityU 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 d2270cf5c0e..08429fc9583 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 bb4abe31978..d94b48d9cbb 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 49db4ff86cf..bb8abffe7e6 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -209,7 +209,7 @@ private void OnUseInHand(EntityUid uid, WieldableComponent component, UseInHandE 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 d691e854ea2..8fdb3de0b1b 100644 --- a/Content.Shared/Wires/SharedWiresComponent.cs +++ b/Content.Shared/Wires/SharedWiresComponent.cs @@ -242,7 +242,7 @@ public static string Name(this WireLetter letter) 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 c4f860e165c..b812115211f 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -4,7 +4,10 @@ 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 override void Initialize() SubscribeLocalEvent(OnPanelDoAfter); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent>(OnGetVerbs); SubscribeLocalEvent(OnAttemptOpenActivatableUI); SubscribeLocalEvent(OnActivatableUIPanelChanged); @@ -96,6 +101,32 @@ private void OnExamine(EntityUid uid, WiresPanelComponent component, ExaminedEve } } + 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 09a1e0bfeaf..9a7b0d8ee33 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 fedb7f1f60d..f6d9331a124 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 9eb627b4f67..4feb2b16493 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 0abad7bdd5b..ca4c645ba15 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 d995a12d6a8..48d0e016364 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 50a7b0200e0..e37f44adf8d 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 013dda5073a..93b925a29c2 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 de44d3ccc65..1ec3fe5cbae 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 8de74124907..00000000000 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 56396fb71ee..00000000000 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 5da73f770e8..5b818eeefae 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 3455c5b4f4a..62cd65bb7bd 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 8fda833b1d2..ada6b70983f 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 00000000000..5730547ad45 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 b389910f136..00000000000 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 00000000000..2789237e39b --- /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 00000000000..512cd8c12fe 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 e9154f9917d..f3067ad07e0 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 00000000000..9f77a610bbe 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 00000000000..cb739cec285 --- /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 00000000000..c2346ad8c3c 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 dcaae63db74..96e27d879f2 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 ac34e50e97e..237a128e692 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 b971abf6435..df01e9ce16c 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 bcd4ee648fe..680b5d04550 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 fd60cbee29a..01ff4724d68 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 40d0c5fa1b1..a0cee580504 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 0702afb33c0..2de4e790818 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 a2cb5301b21..57ed3612487 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 5c540c46dfc..b5d9ed6aaed 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 11b2fb94022..7dd7c779ac9 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 57ad3550bf0..4c639ec77b1 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 cabb808728d..c71fade285c 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 a42cc2da83b..39fea4a1311 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 00000000000..f66a8a012a0 --- /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 00000000000..077c1439a30 --- /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 f430197495f..667db5166fd 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 154196fbb1b..c912df4d769 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 1343aaec7e6..da1794badb7 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 00000000000..03af8cb7a39 --- /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 e41aa4fc996..00000000000 --- 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 3a453b3404b..6e7fae92aeb 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 d61fb8edb27..f9f9c17411b 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 00000000000..a98370e8918 --- /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 209daf1ad3a..54841b2101d 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 c0046ca6934..7191ebbe12e 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 00000000000..358b9e86730 --- /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 aad31d26f9e..c5ea6f47fc1 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 00000000000..99d8aeaead6 --- /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 00000000000..1876fb194d8 --- /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 30069f71713..6068e4385eb 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 cbe3ef6ec02..1f35af41a81 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 4469a576cd8..9ecf4f91d23 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 eb7795ff363..07dee0aa33d 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 4f67fae1cd4..60f13dbc424 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 c02e72da199..37a52a8d4de 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 988c1723635..9878d0bf3d2 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 ffe9741c28d..a982eeaf6f4 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 a2144d08b29..00000000000 --- 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/administration/commands/add-uplink-command.ftl b/Resources/Locale/ru-RU/administration/commands/add-uplink-command.ftl index a6907479de7..6333f5e0e39 100644 --- a/Resources/Locale/ru-RU/administration/commands/add-uplink-command.ftl +++ b/Resources/Locale/ru-RU/administration/commands/add-uplink-command.ftl @@ -5,4 +5,6 @@ add-uplink-command-completion-1 = Username (по-умолчанию это вы add-uplink-command-completion-2 = Uplink uid (по-умолчанию это КПК) add-uplink-command-completion-3 = Включена ли скидка в аплинке add-uplink-command-error-1 = Выбранный игрок не имеет подконтрольную сущность -add-uplink-command-error-2 = Не удалось добавить аплинк игроку \ No newline at end of file +add-uplink-command-error-2 = Не удалось добавить аплинк игроку +add-uplink-command-success-pda = Аплинк добавлен на КПК игрока с кодом {$code} +add-uplink-command-success-implant = Аплинк добавлен игроку как имплант diff --git a/Resources/Locale/ru-RU/administration/smites.ftl b/Resources/Locale/ru-RU/administration/smites.ftl index 66eabe57059..72da77def89 100644 --- a/Resources/Locale/ru-RU/administration/smites.ftl +++ b/Resources/Locale/ru-RU/administration/smites.ftl @@ -107,7 +107,6 @@ admin-smite-disarm-prone-description = Шанс обезоружить цель admin-smite-garbage-can-description = Превратите цель в мусорку, чтобы подчеркнуть, о чём она вам напоминает. admin-smite-super-bonk-description = Заставляет цель удариться о каждый стол на станции и за её пределами. admin-smite-super-bonk-lite-description= Заставляет цель удариться о каждый стол на станции и за её пределами. Прекращает действовать после смерти цели. -admin-smite-terminate-description = Создаёт экстерминатора с ролью призрака, с единственной задачей — убить выбранную цель. admin-smite-super-slip-description = Очень сильно поскальзывает цель. admin-smite-omni-accent-description = Заставляет цель говорить с почти каждым возможным акцентом. admin-smite-crawler-description = Заставляет цель упасть и не позволяет встать. Для дополнительного эффекта удалите и руки! diff --git a/Resources/Locale/ru-RU/atmos/gas-analyzer-component.ftl b/Resources/Locale/ru-RU/atmos/gas-analyzer-component.ftl index 1e49c3e9e91..7c9af0f463e 100644 --- a/Resources/Locale/ru-RU/atmos/gas-analyzer-component.ftl +++ b/Resources/Locale/ru-RU/atmos/gas-analyzer-component.ftl @@ -11,7 +11,6 @@ gas-analyzer-window-tab-title-capitalized = { CAPITALIZE($title) } gas-analyzer-window-refresh-button = Обновить gas-analyzer-window-no-data = Нет данных gas-analyzer-window-no-gas-text = Нет газов -gas-analyzer-window-error-text = Ошибка: { $errorText } gas-analyzer-window-volume-text = Объём: gas-analyzer-window-volume-val-text = { $volume } л gas-analyzer-window-pressure-text = Давление: diff --git a/Resources/Locale/ru-RU/atmos/gases.ftl b/Resources/Locale/ru-RU/atmos/gases.ftl index 5c540c46dfc..9e4ebc0c806 100644 --- a/Resources/Locale/ru-RU/atmos/gases.ftl +++ b/Resources/Locale/ru-RU/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 = Кислород gas-oxygen-abbreviation = O₂ +gas-nitrogen = Азот +gas-nitrogen-abbreviation = N₂ +gas-carbon-dioxide = Углекислый газ +gas-carbon-dioxide-abbreviation = CO₂ +gas-plasma = Плазма gas-plasma-abbreviation = P +gas-tritium = Тритий gas-tritium-abbreviation = T +gas-water-vapor = Водяной пар gas-water-vapor-abbreviation = H₂O -gas-unknown-abbreviation = X +gas-ammonia = Аммиак +gas-ammonia-abbreviation = NH₃ +gas-nitrous-oxide = Оксид азота +gas-nitrous-oxide-abbreviation = N₂O +gas-frezon = Фрезон +gas-frezon-abbreviation = F diff --git a/Resources/Locale/ru-RU/cartridge-loader/cartridges.ftl b/Resources/Locale/ru-RU/cartridge-loader/cartridges.ftl index 853f288426c..8fab39061a5 100644 --- a/Resources/Locale/ru-RU/cartridge-loader/cartridges.ftl +++ b/Resources/Locale/ru-RU/cartridge-loader/cartridges.ftl @@ -7,6 +7,7 @@ news-read-program-name = Новости станции crew-manifest-program-name = Манифест экипажа crew-manifest-cartridge-loading = Загрузка... +crew-manifest-cartridge-loading-failed = Ошибка загрузки манифеста экипажа! net-probe-program-name = Зонд сетей net-probe-scan = Просканирован { $device }! diff --git a/Resources/Locale/ru-RU/changeling/changeling.ftl b/Resources/Locale/ru-RU/changeling/changeling.ftl index 04638cb142d..d5f64b3a070 100644 --- a/Resources/Locale/ru-RU/changeling/changeling.ftl +++ b/Resources/Locale/ru-RU/changeling/changeling.ftl @@ -1,6 +1,11 @@ +# antag selection roles-antag-changeling-name = Генокрад roles-antag-changeling-objective = Умный хищник, приобретающий личности своих жертв. +# devour +changeling-devour-attempt-failed-cannot-devour = Мы не можем поглотить это! +changeling-devour-attempt-failed-already-devoured = Мы уже поглотили это тело! +changeling-devour-attempt-failed-not-dead = Это тело ещё живо! Мы не можем его поглотить! changeling-devour-attempt-failed-rotting = У этого трупа только гниющая биомасса. changeling-devour-attempt-failed-protected = Биомасса этой жертвы защищена бронёй! @@ -8,12 +13,19 @@ changeling-devour-begin-windup-self = Наша жуткая пасть раск changeling-devour-begin-windup-others = { CAPITALIZE(POSS-ADJ($user)) } жуткая пасть раскрывается с неземным голодом. changeling-devour-begin-consume-self = Наша жуткая пасть глубоко впивается в жертву. changeling-devour-begin-consume-others = { CAPITALIZE(POSS-ADJ($user)) } жуткая пасть глубоко впивается в жертву. - -changeling-devour-consume-failed-not-dead = Это тело ещё живо! Его нельзя поглотить! changeling-devour-consume-complete-self = Наша жуткая пасть скрывается, биомасса поглощена. changeling-devour-consume-complete-others = { CAPITALIZE(POSS-ADJ($user)) } жуткая пасть прячется. +# transformation changeling-transform-attempt-self = Наши кости хрустят, мышцы рвутся, одна плоть превращается в другую. changeling-transform-attempt-others = { CAPITALIZE(POSS-ADJ($user)) } кости хрустят, мышцы рвутся, тело превращается в другое. +# transformation BUI +changeling-transform-bui-select-entity = {$entity} +changeling-transform-bui-drop-identity-menu = Избавиться от поглощённой личности. +changeling-transform-bui-drop-identity-entity = Избавиться от {$entity} +changeling-transform-bui-drop-identity-entity-popup = Вы избавились от личности {$entity}. +changeling-transform-bui-drop-identity-cannot-drop = Вы не можете избавиться от текущей личности. + +# other changeling-paused-map-name = Карта хранения личностей генокрада diff --git a/Resources/Locale/ru-RU/clothing/components/fleeting-clothing-coponent.ftl b/Resources/Locale/ru-RU/clothing/components/fleeting-clothing-coponent.ftl new file mode 100644 index 00000000000..2ffff779e8a --- /dev/null +++ b/Resources/Locale/ru-RU/clothing/components/fleeting-clothing-coponent.ftl @@ -0,0 +1,2 @@ +fleeting-clothing-component-default-popup = {CAPITALIZE(THE($item))} рассыпается в пыль. +fleeting-clothing-component-default-examine = Это хрупкий предмет. Он исчезнет если его снять. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl index 39ed5243e92..46433b5b238 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/cargo.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/cargo.ftl index cc339177dba..fe8f62d52a5 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/cargo.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/cargo.ftl @@ -45,8 +45,6 @@ guidebook-SOP-Quartermaster-right = - товар классифицируется как контрабандный или получен незаконным путём; - товар не относится к продукции, произведённой данным отделом. 1. Выбирать вид утилизационных работ для утилизаторов. - 1. Использовать кастет исключительно в целях самообороны при наличии реальной угрозы жизни или здоровью. - - Применение кастета в иных случаях рассматривается как нарушение. 1. Отказать в выполнении заказа в следующих случаях: - при нарушении установленных общих процедур оформления заказов; - при нарушении норм бумажной работы; @@ -79,6 +77,7 @@ guidebook-SOP-SalvageSpecialist-prohibited = - Исключение: посещение экспедиции Лаваленд разрешается исключительно в случае отсутствия сотрудников, занимающих должность шахтёра, на объекте корпорации. 1. Доставлять на объекты корпорации живые экземпляры опасной фауны и мегафауны. +# Corvax-Goob-Start ## Шахтёр guidebook-SOP-ShaftMiner-must = 1. Выполнять добычу руды, а также проводить зачистку и утилизацию заброшенных объектов в рамках экспедиций на Лаваленде. @@ -95,6 +94,7 @@ guidebook-SOP-ShaftMiner-prohibited = 1. Выполнять утилизационные работы с использованием магнита. - Исключение: Разрешается использовать магнит в случае отсутствия сотрудников, занимающих должность утилизатор, на объекте корпорации. 1. Доставлять на объекты корпорации живые экземпляры опасной фауны и мегафауны. +# Corvax-Goob-End ## Грузчик guidebook-SOP-CargoTechnician-must = diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/centcomm.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/centcomm.ftl index 2a297e6c5fa..06401f8d660 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/centcomm.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/centcomm.ftl @@ -93,6 +93,7 @@ guidebook-SOP-CentralCommandChiefStaff-prohibited = 1. Игнорировать важные сообщения или задерживать принятие решений без должных причин. 1. Использовать должностное положение для личной выгоды или поддержки фаворитизма. +# Corvax-Goob-Start ## Офицер Синего Щита guidebook-SOP-BlueshieldOfficer-must = 1. Обеспечивать безопасность капитана станции, членов командного состава, Магистрата и должностных лиц, присланных с Центрального Командования, даже ценой собственной жизни. @@ -109,6 +110,7 @@ guidebook-SOP-BlueshieldOfficer-right = - При критическом состоянии или гибели нарушителя — обеспечить его доставку в медицинский отдел или вызвать парамедика. - Существа без ОПРС, представляющие угрозу, могут быть уничтожены на месте. 1. В условиях [bold][color=red]красного кода[/color][/bold] и выше ОСЩ может запросить в оружейной Службы Безопасности любое доступное оружие. + 1. Проводить сканирование охраняемых лиц. guidebook-SOP-BlueshieldOfficer-prohibited = 1. Проводить процедуру задержания в рамках общих процедур СБ, исключая положение пункта 1 прав. @@ -176,6 +178,7 @@ guidebook-SOP-NanotrasenRepresentative-prohibited = 1. Разглашать конфиденциальную информацию, полученную от Центрального Командования. 1. Разглашать сведения, содержащиеся в золотом листе отчётности, любым лицам, кроме сотрудников Центрального Командования. 1. Копирование корпоративных документов с запросами на печати, выданных через консоль запросов Центрального Командования. +# Corvax-Goob-End # Infobox guidebook-SOP-centcomm-infobox = @@ -184,6 +187,7 @@ guidebook-SOP-centcomm-infobox = {"[bold]Исключение начальные роли станции[/bold]: офицер синего щита, представитель NanoTrasen. Для них СРП всегда актуально, общие процедуры ЦК не распространяются."} +# Corvax-Goob-Start # Tooltip guidebook-SOP-centcomm-tooltip-public-dangerous-crime = К таковым относятся правонарушения тяжести XX3 и выше, а также преступления любой тяжести, связанные с причинением вреда разумным существам или сопротивлением представителям власти. guidebook-SOP-centcomm-tooltip-bso-chasing-offenders = Выпускать охраняемую персону за пределы визуального контакта. @@ -208,3 +212,4 @@ guidebook-SOP-centcomm-tooltip-ntr-valuable-items = 1. Золотой отчётный лист. 1. Переносной факс. guidebook-SOP-centcomm-tooltip-ntr-edit-note = В документ над полем для печатей должна быть добавлена строка, в которой будет указано, что документ был отредактирован; в строке должны быть указаны И/Ф ПNT. +# Corvax-Goob-End diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl index 07d07dc36c7..ad91aaf055b 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,24 @@ 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 = @@ -41,18 +56,26 @@ guidebook-SOP-codes-red-terms = Примеры: - В случае запроса у центрального командования установки [color=gold]гамма кода[/color]. - В случаях, предусмотренных протоколом действия в ЧС: - - Нападение космического дракона. - - Вторжение ревенанта. - - Утрата контроля над сингулярностью/теслой. - - Мятеж на объекте. - - При невозможности устранения угрозы безопасности I уровня в рамках [color=dodgerblue]синего кода[/color]. + - Нападение космического дракона; + - Вторжение ревенанта; + - Утрата контроля над сингулярностью/теслой; + - Мятеж на объекте; + - Появление улья ксеноморфов; + - Обнаружение Блоба; + - Обнаружение Тенеморфа; + - Появление незаконного эгрегориального культа. + - Обнаружение [tooltip="guidebook-SOP-security-tooltip-aggressive-mage" text="агрессивных пользователей магии"]. + - Взлом станционного ИИ. + - Угон приписанного к станции шаттла. - Кража диска ядерной авторизации. - Теракт или крупный саботаж. - Ограбление арсенала станции. - Наличие на станции и/или на ближайшей территории вооруженных формирований. - Убийство или пропажа капитана, магистрата, представителей ЦК/ДСО. - Убийство 5 и более членов экипажа. - - Массовая пропажа экипажа и т.д. + - Массовая пропажа экипажа и так далее. + - Обнаружение [tooltip="guidebook-SOP-general-tooltip-agressive-biological-threat" text="агрессивной биологической угрозы"]. + - При невозможности устранения угрозы безопасности I уровня в рамках [color=dodgerblue]синего кода[/color]. guidebook-SOP-codes-red-norms = - Все члены экипажа должны следовать указаниям капитана при объявлении мобилизации для борьбы с угрозой. @@ -76,11 +99,16 @@ guidebook-SOP-codes-gamma-terms = Примеры: - В случаях, предусмотренных протоколом действия в ЧС: - - Нападение космического дракона - - Вторжение ревенанта - - Вспышка зомби-вируса - - Объявление войны - - Мятеж на объекте + - Нападение космического дракона; + - Вторжение ревенанта; + - Вспышка зомби-вируса; + - Объявление войны; + - Мятеж на объекте; + - Появление улья ксеноморфов; + - Обнаружение Блоба; + - Возвышение еретика; + - Обнаружение Тенеморфа; + - Появление незаконного эгрегориального культа. - При невозможности устранения угрозы безопасности II уровня в рамках [color=red]красного кода[/color]. guidebook-SOP-codes-gamma-norms = @@ -109,10 +137,32 @@ guidebook-SOP-codes-delta-norms = - Распоряжения, полученные от центрального командования или департамента специальных операций должны быть исполнены в полном объеме. - В случае отсутствия распоряжений от центрального командования или департамента специальных операций необходимо организовать незамедлительную эвакуацию. - Все нормы экипажа и распоряжения утрачивают силу, кроме: - - Распоряжения центрального командования или департамента специальных операций. - - Приказы капитана. - - Приказы сотрудников службы безопасности. + - распоряжения центрального командования или департамента специальных операций; + - приказы капитана; + - приказы глав отделов; + - приказы сотрудников службы безопасности. + - Сотрудники службы безопасности уполномочены ликвидировать на месте любого правонарушителя. + +# Corvax-Goob-Start +# Код Октарин +guidebook-SOP-codes-octarine = + {"[italic]Код Октарин. Катастрофическое ноосферное событие угрожает поглотить реальность. Экипажу станции приказано попытаться сдержать событие или эвакуироваться.[/italic]"} + + {"[bold]Устанавливается:[/bold] Центральным Командованием."} + +guidebook-SOP-codes-octarine-terms = + - Центральное Командование зафиксировала угрозу, серьёзно искажающую реальность. + +guidebook-SOP-codes-octarine-norms = + - Распоряжения, полученные от центрального командования или департамента специальных операций должны быть исполнены в полном объеме. + - Допускается немедленная эвакуация, если не поступало иных распоряжений. + - Все нормы экипажа и распоряжения утрачивают силу, кроме: + - распоряжения центрального командования или департамента специальных операций; + - приказы капитана; + - приказы глав отделов; + - приказы сотрудников службы безопасности. - Сотрудники службы безопасности уполномочены ликвидировать на месте любого правонарушителя. +# Corvax-Goob-End # Жёлтый код guidebook-SOP-codes-yellow = @@ -129,7 +179,6 @@ guidebook-SOP-codes-yellow-terms = guidebook-SOP-codes-yellow-norms = - Всему экипажу следует подчиняться правомерным требованиям сотрудников инженерного отдела. - - Капитан уполномочен мобилизировать экипаж для борьбы с угрозой. - Старший инженер обязан организовать процесс выдачи аварийных скафандров, кислородных масок и баллонов с дыхательной смесью. - Капитан или старший инженер уполномочены ограничить перемещение в пределах станции: - Объявить закрытые для посещения зоны. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl index 225ae583f72..b48e9e08bd4 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl @@ -1,3 +1,24 @@ +# Протокол обращения с особо ценными предметами +guidebook-SOP-command-highrisk-items-instructions = + {"[italic]Владелец — сотрудник, которому изначально был выдан данный предмет. Ответственный — сотрудник, законно использующий предмет. Оба лица обязаны в полной мере соблюдать установленную процедуру.[/italic]"} + 1. Владелец предмета обязан бережно хранить его. + - Передача предмета на основании письменного разрешения владельца снимает ответственность с него и передаёт её получившему разрешение сотруднику. + 1. В случае увольнения, ухода в криосон или потери дееспособности предметы должны быть возвращены в место хранения или владельцу. + - Данная обязанность возлагается на ответственного сотрудника или владельца, однако в случае его отсутствия или неспособности выполнить требование — на капитана. + - Исключение: изначальное место хранения подверглось разграблению или присутствуют угрозы, желающие завладеть этим предметом. + 1. Ответственный сотрудник имеет право распоряжаться выданным предметом пока это не противоречит данному протоколу. + 1. Владелец предмета имеет право на основании письменного согласия передать предмет своему подчинённому для улучшения эффективности его работы. + - Допускается передача на основании устного согласия исключительно в [color=gold][bold]гамма и выше[/bold][/color] коды уровни угрозы. По окончанию действия кода разрешение отзывается. + - Владелец обязан провести инструктаж ответственного сотрудника о протоколе обращения с особо ценными предметами, отдельно отметить все наложенные особые ограния, также владелец может создавать ограничения для ответственного сотрудника. + - Пренебрежение оформлением разрешения приравнивается к статье 144 Корпоративного Закона. + 1. Особые ограничения: + - диск ядерной аутентификации запрещено выносить за пределы объекта, за исключением протокола эвакуации. Нарушение приравнивается к статье 144 Корпоративного Закона ; + - диск ядерной аутентификации может быть передан исключительно главе службы безопасности. Нарушение приравнивается к статье 144 Корпоративного Закона; + - в случае передачи диска ядерной аутентификации достаточно устного разрешения капитана. И капитан, и глава службы безопасности несут равную ответственность за сохранность предмета; + - энергетический пистолет/револьвер и/или энергетический меч «Правосудие» могут быть переданы исключительно в [color=dodgerblue][bold]синий код[/bold][/color] на основании письменного разрешения. В [color=red][bold]красный код и выше[/bold][/color] с устного разрешения; + - пользователю ручного телепорта запрещается создавать порталы, ведущие в стратегические и защищённые стратегические точки, если это не приказ Капитана, либо создающие угрозу для экипажа. Исключение: рабочий кабинет пользователя; + - пользователю кастета разрешается использовать его исключительно в целях самообороны, при наличии реальной угрозы жизни или здоровью. + # Общие СРП guidebook-SOP-command-general-must = 1. Обеспечить продуктивность при выполнении отделом должностных обязанностей и указаний Центрального Командования, при необходимости выполнять работу самому. @@ -19,20 +40,20 @@ guidebook-SOP-command-general-must = 1. Соблюдать установленный [tooltip="guidebook-SOP-command-tooltip-dress-code" text="Корпоративный дресс-код"]. - Обязательные элементы униформы могут исключаться или заменяться в случае физиологических или видовых особенностей сотрудника. - Запрещено передавать или обменивать служебную униформу третьим лицам. + 1. Поддерживать датчики костюма в режиме «Координаты». guidebook-SOP-command-general-right = 1. Проводить брифинги, инструктажи и индивидуальные беседы с подчиненными. 1. Создавать правила работы в отделе на свое усмотрение и отдавать правомерные приказы сотрудникам, если они не противоречат Стандартным Рабочим Процедурам, не уменьшают эффективность отдела и не создают в нём токсичную обстановку. - Ограничить реализацию любых прав (дозволений) сотрудников отдела, прописанных в их СРП, если это необходимо для повышения эффективности работы отдела. - - Капитан уполномочен ограничить реализацию дозволений любого сотрудника станции, кроме АВД, ПNT, ОСЩ. + - Капитан уполномочен ограничить реализацию дозволений любого сотрудника станции, кроме [tooltip="guidebook-SOP-command-tooltip-corporate-agents" text="корпоративных агентов"]. 1. Выступать в качестве адвоката для сотрудников своего отдела. - - Капитан не может выступать одновременно и в качестве допрашивающего лица, и в качестве адвоката. - 1. В любой код носить и использовать ослепляющие вспышки и телескопическую дубинку для самообороны. + - Капитан не можить и использовает выступать одновременно и в качестве допрашивающего лица, и в качестве адвоката. + 1. В любой код ность ослепляющие вспышки и телескопическую дубинку для самообороны. 1. В чрезвычайной ситуации носить и использовать пожарный топор. - 1. В случае невозможности связаться с капитаном, написать запрос на вызов любого подразделения ДСО. 1. Написать запрос на вызов членов Центрального Командования. 1. Изменять заработную плату подчинённых сотрудников, но не более, чем на 20% от стандартного оклада. - - Капитан имеет право изменять заработную плату любого сотрудника станции вплоть до 35% от стандартного оклада, кроме АВД, ПNT, ОСЩ. + - Капитан имеет право изменять заработную плату любого сотрудника станции вплоть до 35% от стандартного оклада, кроме [tooltip="guidebook-SOP-command-tooltip-corporate-agents" text="корпоративных агентов"]. - Все документы, касающиеся изменения заработной платы, должны быть переданы Центральному Командованию после или во время смены. guidebook-SOP-command-general-prohibited = @@ -64,11 +85,12 @@ guidebook-SOP-Captain-must = 1. В течение смены сделать уведомление о задаче станции при помощи консоли управления новостями. guidebook-SOP-Captain-right = - 1. [bold]Вето.[/bold] капитан может отменить приказ и решение любого сотрудника станции, кроме решений, принятых советом глав, а также решений, отданных АВД, ПNT, ОСЩ. - - Печать или приказ капитана заменяют собой печать или приказ любого сотрудника станции за исключением агента внутренних дел и Представителя NanoTrasen. + 1. [bold]Вето.[/bold] Капитан может отменить приказ или решение любого сотрудника станции. + - Печать или приказ капитана заменяют собой печать или приказ любого сотрудника станции. - Вето не распространяется на юридически законные решения, связанные с применением Корпоративного Закона. + - Данный пункт не действует в отношении [tooltip="guidebook-SOP-command-tooltip-corporate-agents" text="корпоративных агентов"]. 1. Выдать и подписать любой дозволяющий документ. - 1. Принять решение об эвакуации со станции. + 1. Принимать решение об эвакуации после консультации с Центральным Командованием или самостоятельно в случае ЧС. 1. Принять в состав экипажа станции любое разумное существо, наделенное ОПРС. 1. Проводить судебное слушание в соответствии с процедурой суда. 1. Объявлять в розыск правонарушителей, принимать участие в процедурах допроса, вынесения вердикта и выдачи УДО. @@ -88,7 +110,6 @@ guidebook-SOP-Captain-right = - [color=red][bold]Красном коде[/bold][/color] и [color=gold][bold]гамма и выше[/bold][/color]; - Наличии атмосферной угрозы на станции; - Условиях, описанных в дозволениях. - 1. Вызвать магистрата или подразделения ДСО при необходимости. guidebook-SOP-Captain-prohibited = 1. Использовать данные ему полномочия в ущерб интересам NanoTrasen. @@ -114,7 +135,7 @@ guidebook-SOP-command-procedure-appoint-acting-captain = ## Процедура отстранения капитана от командования guidebook-SOP-command-procedure-remove-captain = 1. Капитан может быть уволен по собственному желанию, по приказу Центрального Командования или при решении не анонимного голосования глав в соответствии с причинами отстранения от должности. - 1. ВрИО капитан обязан вернуться на свою изначальную должность после прибытия нового капитана с Центрального Командования. + - ВрИО капитан обязан вернуться на свою изначальную должность после прибытия нового капитана с Центрального Командования. 1. При голосовании необходимо большинство голосов глав отделов «ЗА» отстранение капитана. - При равном количестве голосов «ЗА» и «ПРОТИВ» отстранение капитана отменяется. - Воздержание от голоса, отказ голосовать и попытка сорвать голосование считается голосом «ЗА». @@ -226,29 +247,28 @@ guidebook-SOP-command-procedure-reasons-for-removal = 1. [tooltip="guidebook-SOP-command-tooltip-abuse-of-power" text="Злоупотребление полномочиями"]. 1. Уход с должности по собственному желанию. -## Протоколы эвакуации -### Окончание смены -guidebook-SOP-command-protocol-evacuation-end-of-shift = - 1. После окончания смены начинается стандартный процесс отправки экипажа на станцию центрального командования для последующего распределения и отдыха. Отбытие производится спустя 2 часа после начала смены или после выполнения дополнительной цели смены. - - Капитан обязан составить письменный отчёт о выполнении основной или дополнительной цели и отправить его на центральное командование. - - Капитан обязан уведомить персонал соответствующим сообщением для подготовки к отбытию. - - Службе безопасности необходимо организовать транспортировку всех заключённых; во время перелёта заключённые должны находиться в отсеке для заключённых. - - Медицинский персонал обязан организовать транспортировку тел погибших сотрудников и тяжело больных пациентов. - - Отбытие шаттла ранее установленного времени в случае отсутствия прямой угрозы не допускается. - -### Невозможность выполнения цели -guidebook-SOP-command-protocol-evacuation-unable-to-complete-goal = - Решение об окончании смены без выполнения цели и отбытии со станции принимается исключительно капитаном под его полную ответственность. - -### Нарушение условий содержания сингулярности -guidebook-SOP-command-protocol-evacuation-violation-of-singular-conditions = - Капитан обязан установить жёлтый код; необходимо провести наблюдение за траекторией движения сингулярности. При сильной угрозе вызвать эвакуационный шаттл и оповестить экипаж и центральное командование. - - Если угрозы больше нет, допускается восстановление повреждённых отсеков и продолжение смены. - -### Вспышка зомби вируса -guidebook-SOP-command-protocol-evacuation-zombie-virus-outbreak = - До полного устранения угрозы эвакуационный шаттл не вызывается. В случае вызова шаттла центральным командованием, шаттл должен быть отклонён и запрошена помощь РХБЗЗ. +## Протокол эвакуации +guidebook-SOP-command-protocol-evacuation = + Эвакуация (протокол смены персонала) — процедура, при которой персонал в полном, в определённых ситуациях частичном, составе покидает объект для дальнейшего распределения. + 1. Правом запросить эвакуацию обладает только капитан и совет глав. + 1. Допускается запросить эвакуацию в следующих случаях: + - выполнении поставленной цели объекта; + - невозможности выполнения поставленной цели в связи с ситуацией на объекте; + - случаях, предусмотренных протоколом действия в ЧС. + 1. Эвакуация не допускается в фиолетовый код уровня угрозы, при официальном карантине или в случае [tooltip="guidebook-SOP-general-tooltip-agressive-biological-threat" text="агрессивной биологической угрозы"] на объекте. + 1. Запрос оформляется письменно, после получения ЦК отчёта о ситуации. + - В случае ЧС допускается устный отчёт и запрос с использованием красного телефона. + 1. Эвакуационный шаттл вызывается ЦК, в случае отсутствия ответа от ЦК в течение 5 минут или при ЧС капитаном или другим главой. + 1. За время до прибытия шаттла: + - весь персонал обязан привести рабочие места в порядок; + - медицинский персонал обязан перенести тела погибших сотрудников в зону стыковки, после на шаттл; + - персонал службы безопасности обязан организовать транспортировку всех заключённых в зону стыковки, после на шаттл; + - инженерному персоналу необходимо немедленно привести пункт 4 обязанностей общих СРП отдела в исполнение. + 1. Персонал отбывает в полном составе, за исключением следующих случаев: + - соблюдение пункт 4 обязанностей общих СРП инженерного отдела; + - в случае наличия угрозы безопасности, при реальной возможности её устранения, сотрудники СБ отбывают лишь в минимальном составе для сопровождения заключённых. + 1. Авторизация для раннего отбытия шаттла эвакуации допускается исключительно при наличии угрозы шаттлу. + - Нарушение данного пункта приравнивается к нарушению статьи 133 Корпоративного Закона. # Tooltip guidebook-SOP-command-tooltip-neutralization = Подразумевается устранение угрозы, которая послужила причиной введения текущего кода. @@ -284,3 +304,9 @@ guidebook-SOP-command-tooltip-dead-title = Примеры: guidebook-SOP-command-tooltip-inactivity = Это относится к сотрудникам в кататоническом ступоре, игнорирующим брифинг и вызовы по рации. Полное игнорирование своих обязательств. guidebook-SOP-command-tooltip-toxic-environment = Это относится к сотрудникам, которые регулярно неуважительно относятся к своим коллегам. guidebook-SOP-command-tooltip-abuse-of-power = Это относится к ГСБ, выдавшему разрешение на ношение оружия без явной причины, продаже сотрудников станции в рабство Квартирмейстером и так далее. + +guidebook-SOP-command-tooltip-corporate-agents = Корпоративные агенты — лица с особым статусом, пребывающие на объекте с целью представления отдельных интересов корпорации NanoTrasen. Таковыми являются: + - магистрат; + - агент внутренних дел; + - представитель NT; + - офицер Синего Щита. 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 00000000000..083c8cbc4bf --- /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/emergencies.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/emergencies.ftl index e19ee419fab..ceb0bfeac8b 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/emergencies.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/emergencies.ftl @@ -94,3 +94,50 @@ guidebook-SOP-emergencies-mutiny = - При необходимости провести мобилизацию экипажа. - В случае подтверждения использования гипноза или иных средств внушения со стороны мятежников допускается принудительная установка импланта «Щит разума» всем членам экипажа. - При невозможности самостоятельно справиться с угрозой запросить у Центрального командования установку [color=gold][bold]гамма кода[/bold][/color] и отправку сил ОБР или Эскадрона Смерти. +# Corvax-Goob-Start +# Обнаружение Блоба +guidebook-SOP-emergencies-blob = + {"[bold]Условия введения:[/bold]"} + - На станции обнаружен живой Блоб. + {"[bold]Рекомендации:[/bold]"} + - Немедленно установить [color=red][bold]красный код[/bold][/color]. + - При необходимости провести мобилизацию экипажа. + - В случае поражения Блобом территории, как минимум сопоставимой по размеру с отделом станции рекомендуется запросить [color=gold][bold]гамма код[/bold][/color] и силы РХБЗЗ. + - В случае получения кодов активации механизма самоуничтожения станции немедленно активировать его, если не поступало иных указаний от сил ДСО. + +# Возвышение еретика +guidebook-SOP-emergencies-heretic = + {"[bold]Условия введения:[/bold]"} + - На станции обнаружен возвышенный еретик. + {"[bold]Рекомендации:[/bold]"} + - Немедленно запросить установку [color=gold][bold]гамма кода[/bold][/color] и отправку сил ОБР. + - Провести мобилизацию экипажа. + +# Появление улья ксеноморфов +guidebook-SOP-emergencies-kseno = + {"[bold]Условия введения:[/bold]"} + - Подтверждено наличие полноценного улья ксеноморфов на станции. + {"[bold]Рекомендации:[/bold]"} + - Немедленно установить [color=red][bold]красный код[/bold][/color] + - При необходимости провести мобилизацию экипажа. + - При невозможности подавления угрозы запросить установку [color=gold][bold]гамма кода[/bold][/color] и отправку сил РХБЗЗ. + +# Обнаружение Тенеморфа +guidebook-SOP-emergencies-shadowkin = + {"[bold]Условия введения:[/bold]"} + - На станции присутствует тенеморф в любой стадии развития. + {"[bold]Рекомендации:[/bold]"} + - Немедленно установить [color=red][bold]красный код[/bold][/color]. + - Обеспечить повышенную освещенность помещений. + - При необходимости провести мобилизацию экипажа. + - При невозможности подавления угрозы или при возвышении тенеморфа запросить установку [color=gold][bold]гамма кода[/bold][/color] и отправку сил ОБР. + +# Появление незаконного эгрегориального культа +guidebook-SOP-emergencies-cult = + {"[bold]Условия введения:[/bold]"} + - На станции подтверждено наличие агрессивно настроенной группы людей, использующих аномальные или магические способности и технологии, ставящие своей целью призыва своего эгрегора. + {"[bold]Рекомендации:[/bold]"} + - Немедленно установить [color=red][bold]красный код[/bold][/color]. + - Использовать УПМК для освобождения персонала от влияния культа и их дальнейшей мобилизации. + - При невозможности подавления угрозы запросить установку [color=gold][bold]гамма кода[/bold][/color] и отправку сил ОБР. +# Corvax-Goob-End diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl index b4bcc89f67d..1eb861604b7 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 d9603048eba..6d3b2604d68 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. По прибытии на территорию станции сообщить о своем прибытии в общий канал связи. @@ -95,3 +95,48 @@ guidebook-SOP-general-orders-prohibited = 1. Запрещается оформлять заказы от имени других сотрудников без их ведома и согласия. 2. Запрещается заказывать любые виды оружия и специальные средства, относящиеся к отделу безопасности, без письменного разрешения главы службы безопасности. 3. Запрещается массовое оформление заказов на однотипные товары без предварительного согласования с главой отдела или капитаном станции. + +guidebook-SOP-general-specialops-instructions = + Свод инструкций по взаимодействию с оперативными группами Департамента Специальных Операций во время проведения ими специальных операций на территории объекта, а также требования к их вызову. + 1. Решение о вызове сил ДСО принимает капитан. + - В отсутствии капитана [tooltip="guidebook-SOP-general-tooltip-profile-head" text="профильный глава"]. + - В отсутствии капитана и профильного главы любой член командования или [tooltip="guidebook-SOP-command-tooltip-corporate-agents" text="корпоративный агент"]. + 2. Вызов силового подразделения ОБР допускаетя только при [color=red][bold]красном и выше[/bold][/color] кодах уровня угрозы, в случае столкновения с враждебными элементами, которые невозможно подавить текущими силами. + 3. Вызов инженерного подразделения ОБР допускается только при [color=yellow][bold]жёлтом коде[/bold][/color] уровня угрозы, если невозможно устранить структурную, атмосферную или радиационные угрозы. + 4. Вызов медицинского подразделения ОБР допускается при [color=violet][bold]фиолетовом коде[/bold][/color] уровня угрозы или при любом другом, если медицинский персонал не способен самостоятельно справится с возникшей проблемой. + 5. Вызов уборочного подразделения ОБР допускается при невозможности устранения угрозы санитарной безопасности собственными силами. + 6. Вызов духовного подразделения ОБР допускается при невозможности устранения сверхъестественной угрозы собственными силами. + 7. Вызов РХБЗЗ допускается только при наличии [tooltip="guidebook-SOP-general-tooltip-agressive-biological-threat" text="агрессивной биологической угрозы"], котрую невозможно подавить текущими силами. + 8. Персонал обязан подчиняться всем требованиям членов оперативной группы и оказывать им содействие, за исключением случаев, когда действия или приказы являются очевидно противозаконными. + 9. Персоналу запрещено самовольно посещать шаттл прибывшией оперативной группы. + 10. На время пребывания оперативной группы на объекте, правом запроса эвакуации обладает исключительно командир группы. + +guidebook-SOP-general-mobilization = + Мобилизация — это мероприятие, предполагающее привлечение экипажа станции к ликвидации угрозы безопасности станции. + 1. Мобилизация может быть объявлена в следующих случаях: + - возникновения ситуаций, предусмотренных протоколами ЧС; + - столкновение с угрозой, не учтенной протоколами ЧС, но влекущей за собой полное уничтожением объекта и экипажа; + - действие [color=gold][bold]гамма кода[/bold][/color] и выше. + 2. Правом объявления мобилизации обладают капитан, Центральное Командование (исключая ПНТ и ОСЩ), командиры оперативных групп ДСО. + - В случае отсутствия или гибели капитана, таким правом наделяется глава службы безопасности. + - В случае отсутствие или гибели капитана и главы службы безопасности, мобилизация может быть объявлена любым главой. + 3. Мобилизация может быть двухуровневой на усмотрение лица, объявляющего мобилизацию: + - частичная: предполагает мобилизацию конкретной части экипажа (например, пассажиров, группы утилизаторов, сотрудников конкретных отделов станции и так далее); + - полная: привлечение всех сотрудников станции. + 4. Мобилизованные имеют право: + - на использование любого доступного вида вооружения, исключая оружие массового поражения; + - на получение расширенного доступа; + - на получение защитных средств и снаряжения, запрещенных Корпоративных Законом в штатной обстановке. + 4. Мобилизованные обязаны: + - следовать указаниям командования и службы безопасности. Неподчинение приравнимается к нарушению статьи 115 Корпоративного Закона; + - использовать выданную экипировку и доступы исключительно с целью устранения угрозы, послужившей причиной мобилизации; + - по окончании мобилизации сдать полученные экипировку и доступы в течение 10 минут. + +guidebook-SOP-general-tooltip-profile-head = Профильный глава в случае вызова сил ДСО — глава, зона ответственности которого совпадает с характером возникшей угрозы. Примеры: + - нападение террористических, вражеских группировок, открытая диверсионная деятельность или агрессивная биологическая угроза — глава службы безопасности; + - критическое загрязнение большинства помещений объекта — глава персонала; + - невозможность медицинского персонала справится с обязанностями — главный врач; + - серьёзная структурная или атмосферная угроза — старший инженер. + +guidebook-SOP-general-tooltip-agressive-biological-threat = Агрессивная биологическая угроза — угрозы биологического характера, способные к заражению гуманоидов с целью подавления их воли или представляющая собой стремительно захватывающую территорию агрессивную биологическую массу. Например: блоб, зомби инфекция, генокрады. + diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl index 51077456efe..2d7f6765b64 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-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. В открытом судебном процессе допустимо присутствие зрителей в зале суда. - - {"[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-responsibilities = + {"Магистрат обязан:"} + 1. Открыть заседание и объявить стороны процесса. + 1. Предоставить первое слово стороне обвинения. + 1. После изложения обвинения уточнить у подсудимого, согласен ли он с обвинением и изложенными фактами. + 1. Предоставить слово стороне защиты. + 1. Руководить допросом свидетелей и экспертов, контролируя порядок в суде. + 1. Предоставить сторонам время для прений: первой выступает сторона обвинения, затем защита. + 1. Предоставить подсудимому последнее слово. + 1. Вынести и огласить приговор. + 1. Объявить заседание закрытым. + {"Сторона обвинения обязана:"} + 1. Первой изложить обвинение и предоставить доказательства. + 1. По необходимости пригласить потерпевших и свидетелей. + 1. Сформулировать требуемую меру наказания в прениях. + {"Сторона защиты обязана:"} + 1. После обвинения изложить свою позицию. + 1. Предоставить доказательства в защиту подсудимого. + 1. Пригласить свидетелей защиты (при наличии). + 1. В прениях указать на смягчающие обстоятельства или требовать оправдания. + {"Подсудимый имеет право:"} + 1. Озвучить свою позицию после выступления стороны обвинения. + 1. Давать показания и представлять доказательства. + 1. Воспользоваться последним словом перед вынесением приговора. + +## Судебное разбирательство +### Судебное разбирательство +guidebook-SOP-legal-court-litigation = + {"[italic]Судебное разбирательство — процесс, в ходе которого магистрат изучает материалы дела, заслушивает показания сторон, свидетелей и экспертов, оценивает доказательства и принимает решение о виновности или невиновности подсудимого.[/italic]"} + 1. Для вынесения обвинительного приговора необходимо наличие доказательной базы, подтверждающей вину подсудимого. + 1. При рассмотрении дела учитываются как прямые, так и косвенные доказательства. + 1. Доказательства признаются состоятельными, если подтверждают факт совершения преступления и не противоречат друг другу. + 1. В случае противоречий материалы считаются недействительными. + 1. При отрицании подсудимым своей вины именно доказательства должны являться достаточными для её подтверждения. + 1. Если доказательств недостаточно — подсудимый признаётся невиновным. + 1. Прямые доказательства: + - Сам объект или субъект преступления (потерпевший). + - Вещественные доказательства: орудие преступления, украденное имущество и д.р. + - Показания минимум трёх свидетелей с совпадающими деталями. + - Показания боргов (ИИ, киборгов). + - Заключения экспертов. + - Явка с повинной. + - Биологические следы: отпечатки пальцев, образцы ДНК, следы крови и т.п. + - Аудиозаписи и фотографии. + 1. Косвенные доказательства: + - Наличие у подсудимого мотива. + - Отсутствие алиби. + - Негативные личные отношения с жертвой. + - Тяжёлое материальное положение. + - Наличие дисциплинарных взысканий, выговоров. + - Судимости. + - Состояние алкогольного или наркотического опьянения. + 1. Обвинение может быть снято магистратом на этапе судебного разбирательства при наличии одного или нескольких из следующих факторов: + - Недостаток прямых доказательств. + - Отсутствует субъект преступления или орудие преступления. + - Нет вещественных доказательств, подтверждающих факт нарушения. + - Показания свидетелей (минимум трёх) противоречивы или не соответствуют фактам. + - Биологические следы не подтверждают участие подсудимого. + - Сомнительность косвенных доказательств. + - Мотив не подтверждён другими фактами. + - Нет убедительных свидетельств отсутствия алиби. + - Негативные отношения с жертвой, материальное положение, дисциплинарные взыскания, судимости или опьянение не подтверждают факт совершения преступления. + - Противоречие между доказательствами. + - Прямые доказательства противоречат друг другу. + - Косвенные доказательства опровергают прямые факты. + - Явная невиновность подсудимого. + - Подсудимый признаёт факт, но доказательства указывают на то, что он не совершал преступления. + - Любой факт, который исключает вину подсудимого по сути или обстоятельствам дела. + +### Вынесение вердикта +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/medical.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/medical.ftl index d33a89ca886..6e202a381c9 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/medical.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/medical.ftl @@ -24,7 +24,6 @@ guidebook-SOP-ChiefMedicalOfficer-must = guidebook-SOP-ChiefMedicalOfficer-right = 1. Исполнять обязанности любого сотрудника медицинского отдела в свободное от командования время, в случаях необходимости оказания экстренной помощи либо в условиях ЧС. - 1. Хранить при себе гипоспрей и портативный монитор экипажа, либо распределить их между сотрудниками отдела, оформив соответствующее разрешение. 1. Назначать врачей на должности: хирург, вирусолог, патологоанатом. 1. Издавать приказ о принудительном психиатрическом лечении члена экипажа. - Если документ касается должностных лиц, он должен быть заверен печатью капитана. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/research.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/research.ftl index 97e5e66ef69..4edf99d156f 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/research.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/research.ftl @@ -33,10 +33,6 @@ guidebook-SOP-ResearchDirector-must = guidebook-SOP-ResearchDirector-right = 1. Исполнять обязанности учёного в свободное от исполнения обязанностей руководителя время, либо в условиях ЧС. 1. Использовать золото и серебро из хранилища для печати плат и предметов на станках, распределять эти ресурсы среди сотрудников научного отдела. - 1. На владение или передачу ручным телепортом любому сотруднику своего отдела. - - Для передачи прибора необходим документ Разрешение на использование снаряжения. - - В случае потери прибора ответственность лежит на научном руководителе и сотруднике, которому был передан телепорт. - - Сотрудник, которому был передан телепорт, должен быть проинформирован о правилах установки порталов, указанных в пункте 2 запретов научного руководителя. 1. На владение медико-научной гарнитурой и медико-научным ключом шифрования или распределение этих предметов среди сотрудников научного и медицинского отделов. - Для передачи прибора неглавам необходим документ Разрешение на использование снаряжения с печатями главного врача и научного руководителя. 1. На владение двоичным ключом шифрования или передачу его любому сотруднику своего отдела. @@ -45,8 +41,6 @@ guidebook-SOP-ResearchDirector-right = guidebook-SOP-ResearchDirector-prohibited = 1. Разрешать научным сотрудникам выносить из отдела вне чрезвычайной ситуации [tooltip="guidebook-SOP-research-tooltip-dangerous-equipment" text="опасное снаряжение"], [tooltip="guidebook-SOP-research-tooltip-dangerous-substances" text="опасные вещества"] без специального разрешения капитана. - 1. Создавать порталы, ведущие в стратегические и защищённые точки (если это не приказ Капитана), либо создающие угрозу для экипажа. - - Исключением среди стратегических точек является собственный кабинет научного руководителя. ## Учёный guidebook-SOP-Scientist-must = diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl index 40773619f72..83dc9291c53 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. Использовать снаряжение, разрешённое в рамках текущего уровня угрозы. @@ -44,8 +48,6 @@ guidebook-SOP-HeadOfSecurity-right = 1. При приёме в отдел нового сотрудника из числа членов экипажа обязать кандидата пройти процедуру установки импланта «Щит разума». 1. При выдаче сотруднику станции доступов уровней «бриг», «служба безопасности» или «оружейная» потребовать от него пройти процедуру установки импланта «Щит разума». 1. В любой код держать при себе личный энергетический пистолет/револьвер и/или энергетический меч «Правосудие» (при наличии). - - В [color=dodgerblue][bold]синий код[/bold][/color] передать указанные образцы оружия одному из сотрудников СБ из числа офицеров, пилотов или смотрителю на основании письменного [tooltip="guidebook-SOP-security-tooltip-paper-permit" text="разрешения"]. - - В [color=red][bold]красный код[/bold][/color] и выше передать указанные образцы вооружения любому сотруднику СБ на основании устного разрешения. 1. Провести процедуру личного досмотра в отношении любого нижестоящего сотрудника службы безопасности при подозрении в хранении контрабандных предметов или недопустимого для текущего уровня угрозы вооружения. 1. При отсутствии пилота/помощника смотрителя одобрить перевод сотрудника из числа офицеров на данную должность. 1. Временно отстранить от службы любого сотрудника СБ при подтверждении состояния опьянения или при наличии серьезных травм, а также в случае нарушения корпоративного закона или СРП сотрудником. @@ -386,6 +388,7 @@ guidebook-SOP-security-procedure-investigation-interrogation = - Сокращение срока не предусмотрено, если итоговый срок заключения, к которому может быть приговорён подозреваемый составляет более 75 минут. - Суммарное время допроса не может превышать срок, равный 10 минутам в сумме с потенциальным сроком заключения, к которому подозреваемый может быть приговорён при подтверждении его виновности. - Если в результате сокращения срока заключения фактический срок заключения равен 0, подозреваемый должен быть признан как отбывший срок заключения в полном объеме. + - В случае направления задержанного в корпоративный суд, допрос ограничивается сроком до 5 минут, после чего задержанный должен быть незамедлительно направлен в суд. Магистрат может отменить это требование в случае занятости судебными делами. 1. В процедуре допроса принимают участие задержанный, допрашивающий, а также адвокат, свидетели и эксперты, если таковые имеются. - В роли допрашивающего может выступать смотритель, помощник смотрителя, глава службы безопасности, капитан, либо иной сотрудник службы безопасности — с разрешения ранее упомянутых лиц. - При нарушении порядка проведения процедуры агент внутренних дел уполномочен отстранить допрашивающего от дальнейшего проведения допроса, в таком случае проведение допроса должно быть передано вышестоящему сотруднику, вплоть до капитана. Агент внутренних дел не может отстранить допрашивающего в [color=red][bold]красном коде[/bold][/color] и выше. @@ -750,6 +753,7 @@ guidebook-SOP-security-procedure-force-level-3 = 1. Использование слезоточивых гранат. 1. Использование травматических гранат. 1. Использование иных видов гранат нелетального характера. + 1. Использование полимерной или шипастой дубинки. {"[head=3]Основания для применения[/head]"} 1. Применяется в ситуациях, когда предыдущий уровень силы не оказал должного влияния на цель. @@ -828,7 +832,6 @@ guidebook-SOP-security-procedure-unregistered-protocol = # Tooltip guidebook-SOP-security-tooltip-lawful-orders = Приказы, не противоречащие ОПРС, КЗ и СРП. -guidebook-SOP-security-tooltip-paper-permit = Письменное разрешение на использование снаряжения, оформленное в соответствии с внутренними правилами. guidebook-SOP-security-tooltip-public-dangerous-crime = К таковым относятся правонарушения тяжести XX3 и выше, а также преступления, связанные с причинением вреда разумным существам или сопротивлением представителям власти. guidebook-SOP-security-tooltip-officials = Капитан; Главы отделов; Сотрудники СБ; Сотрудники юридического департамента; Представители Центрального Командования; ДСО. guidebook-SOP-security-tooltip-regulation-speech = diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl index fec05b3140b..ac1087ab029 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl @@ -40,35 +40,7 @@ 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. Использовать адвокатскую деятельность в целях личной выгоды, не связанных с защитой клиента. - +# Corvax-Goob-Start ## Радиоведущий guidebook-SOP-RadioHost-must = 1. Обеспечивать стабильную и корректную работу радиоканала, информировать экипаж станции о событиях на объекте корпорации, проводить прямые радиоэфиры. @@ -93,19 +65,21 @@ guidebook-SOP-RadioHost-prohibited = 1. Проводить информационные блоки или эфиры в состоянии алкогольного или наркотического опьянения. 1. Проводить радиоэфиры, которые напрямую наносят вред репутации корпорации NanoTrasen. - Допускается пускать в радиоэфир информацию, направленную против конкретных лиц или должностей на объекте корпорации, при условии подтверждённой достоверности информации. +# Corvax-Goob-End ## Сервисный работник 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 0ece5dd9b51..4ec478e50e0 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/corvax/guidebook/corporatelaw/corporatelaw.ftl b/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/corporatelaw.ftl index 81c4c3ab11f..5f4eb754ee5 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/corporatelaw.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/corporatelaw.ftl @@ -77,7 +77,7 @@ guidebook-corporatelaw-OPRS = - [bold]Гарант ОПРС[/bold]. Единственными органами, имеющими право изменять, дополнять, приостанавливать или официально толковать К-ОПРС, являются: [bold]Генеральный директор NanoTrasen[/bold], [bold]Совет директоров NanoTrasen[/bold] и [bold]Юридический департамент NanoTrasen[/bold]. Любые правки, интерпретации или редакции К-ОПРС вступают в силу немедленно после их публикации в закрытых корпоративных реестрах. - [bold]Равенство перед законом[/bold]. Все разумные существа, признанные [bold]NanoTrasen[/bold], равны перед корпоративным правом. - [bold]Регулятор К-ОПРС[/bold]. Контроль за соблюдением и применением К-ОПРС осуществляет [bold]Верховный расовый корпоративный суд NanoTrasen[/bold]. Он рассматривает все дела о нарушении К-ОПРС, включая споры между сотрудниками, департаментами, синтетическими формами жизни и иными корпоративными субъектами, обладающими признанным разумом. - - [bold]Объект К-ОПРС[/bold]. К-ОПРС защищает права всех разумных существ, получивших признание корпорацией NanoTrasen. Признание при рождении получают следующие расы: люди, дворфы, унатхи, слаймолюды, дионы, нианы, арахниды, вульпканины, фелиниды, резоми, воксы, гарпии, таяры, КПБ, плазмолюды, серые, резоми, они, родения, тенекин, хитинид, ферокси. + - [bold]Объект К-ОПРС[/bold]. К-ОПРС защищает права всех разумных существ, получивших признание корпорацией NanoTrasen. Признание при рождении получают следующие расы: люди, дворфы, унатхи, слаймолюды, дионы, нианы, арахниды, вульпканины, фелиниды, резоми, воксы, гарпии, таяры, КПБ, плазмолюды, серые, они, родения, тенекин, хитинид, ферокси. Основные права: 1. [bold]Право на жизнь[/bold]. Каждый имеет право на жизнь. diff --git a/Resources/Locale/ru-RU/corvax/job/job-names.ftl b/Resources/Locale/ru-RU/corvax/job/job-names.ftl index 890b969157f..b4a12486316 100644 --- a/Resources/Locale/ru-RU/corvax/job/job-names.ftl +++ b/Resources/Locale/ru-RU/corvax/job/job-names.ftl @@ -8,6 +8,6 @@ job-name-senior-officer = инструктор СБ JobIAA = агент внутренних дел JobPilot = пилот JobSeniorEngineer = ведущий инженер -JobSeniorOfficer = ведущий учёный +JobSeniorOfficer = инструктор СБ JobSeniorPhysician = ведущий врач -JobSeniorResearcher = инструктор СБ +JobSeniorResearcher = ведущий учёный diff --git a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-nukeops.ftl b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-nukeops.ftl index 884279e2c77..69ee8ec22aa 100644 --- a/Resources/Locale/ru-RU/game-ticking/game-presets/preset-nukeops.ftl +++ b/Resources/Locale/ru-RU/game-ticking/game-presets/preset-nukeops.ftl @@ -24,6 +24,17 @@ nukeops-cond-allnukiesdead = Все ядерные оперативники по nukeops-cond-somenukiesalive = Несколько ядерных оперативников погибли. nukeops-cond-allnukiesalive = Все ядерные оперативники выжили. +nukeops-disk-location-title = Конечное местоположение диска: +nukeops-disk-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 }, в +} + nukeops-list-start = Оперативниками были: nukeops-list-name = - [color=White]{ $name }[/color] nukeops-list-name-user = - [color=White]{ $name }[/color] ([color=gray]{ $user }[/color]) diff --git a/Resources/Locale/ru-RU/gases/gases.ftl b/Resources/Locale/ru-RU/gases/gases.ftl deleted file mode 100644 index 81d19f36208..00000000000 --- a/Resources/Locale/ru-RU/gases/gases.ftl +++ /dev/null @@ -1,9 +0,0 @@ -gases-oxygen = Кислород -gases-nitrogen = Азот -gases-co2 = Диоксид углерода -gases-plasma = Плазма -gases-tritium = Тритий -gases-water-vapor = Водяной пар -gases-ammonia = Аммиак -gases-n2o = Оксид азота -gases-frezon = Фрезон diff --git a/Resources/Locale/ru-RU/guidebook/entity-effects/effects.ftl b/Resources/Locale/ru-RU/guidebook/entity-effects/effects.ftl index a9926d25811..c23cd617bba 100644 --- a/Resources/Locale/ru-RU/guidebook/entity-effects/effects.ftl +++ b/Resources/Locale/ru-RU/guidebook/entity-effects/effects.ftl @@ -508,6 +508,12 @@ entity-effect-guidebook-plant-phalanximine = *[other] восстанавливают } жизнеспособность растения, ставшего нежизнеспособным в результате мутации +entity-effect-guidebook-plant-remove-kudzu = + { $chance -> + [1] Убирает + *[other] убирает + } кудзу из растения + entity-effect-guidebook-plant-diethylamine = { $chance -> [1] Повышает diff --git a/Resources/Locale/ru-RU/hijack-beacon/hijack-beacon.ftl b/Resources/Locale/ru-RU/hijack-beacon/hijack-beacon.ftl new file mode 100644 index 00000000000..1d4f6c756bc --- /dev/null +++ b/Resources/Locale/ru-RU/hijack-beacon/hijack-beacon.ftl @@ -0,0 +1,16 @@ +hijack-beacon-announcement-sender = Автоматизированная торговая станция +hijack-beacon-announcement-activated = Внимание! Обнаружена попытка взлома брандмауэра автоматизированной торговой станции! Примерное время до взлома брандмауэра: {$time} секунд! +hijack-beacon-announcement-deactivated = Взлом брандмауэра не удался. Целостность брандмауэра частично восстановлена. Хорошего дня! +hijack-beacon-announcement-success = Брандмауэр автоматизированной торговой станции успешно отключен. {$fine} кредитов переведено из средств станции на [%ERROR%]. Гарантия вашей торговой станции аннулирована. Об этом инциденте сообщено. + +hijack-beacon-examine-await-activate = Маяк [color=green]готов к активации[/color]. +hijack-beacon-examine-await-cooldown = Маяк [color=red]перезаряжается[/color]. +hijack-beacon-examine-await-hijack-complete = Маяк [color=red]использован[/color]. + +hijack-beacon-popup-anchor = Маяк закрепляется! +hijack-beacon-popup-unanchor = Маяк открепляется. + +hijack-beacon-verb-activate-text = Активировать +hijack-beacon-verb-activate-message = Маяк можно активировать только на автоматизированной торговой станции, на незанятой клетке. +hijack-beacon-verb-deactivate-text = Деактивировать +hijack-beacon-verb-deactivate-message = Маяк сам себя не отключит, понимаете? diff --git a/Resources/Locale/ru-RU/job/job-names.ftl b/Resources/Locale/ru-RU/job/job-names.ftl index 531adafe4ec..d8a51ebb779 100644 --- a/Resources/Locale/ru-RU/job/job-names.ftl +++ b/Resources/Locale/ru-RU/job/job-names.ftl @@ -6,16 +6,16 @@ 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 = священник 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/lathe/lathe-categories.ftl b/Resources/Locale/ru-RU/lathe/lathe-categories.ftl index 4de27121650..620cc68a897 100644 --- a/Resources/Locale/ru-RU/lathe/lathe-categories.ftl +++ b/Resources/Locale/ru-RU/lathe/lathe-categories.ftl @@ -31,8 +31,12 @@ lathe-category-faux-tile = Искусственная lathe-category-maints-tile = Техи lathe-category-marble = Мрамор lathe-category-steel-tile = Сталь +lathe-category-shuttle-tile = Шаттл lathe-category-white-tile = Белая lathe-category-wood-tile = Деревянная +lathe-category-plastic-tile = Пластик +lathe-category-precious-tile = Драгоценный +lathe-category-industrial-tile = Индустриальный # Science lathe-category-mechs = Мехи diff --git a/Resources/Locale/ru-RU/lobby/lobby-state-background.ftl b/Resources/Locale/ru-RU/lobby/lobby-state-background.ftl index c0046ca6934..7191ebbe12e 100644 --- a/Resources/Locale/ru-RU/lobby/lobby-state-background.ftl +++ b/Resources/Locale/ru-RU/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/ru-RU/objectives/conditions/anomaly-supercrit.ftl b/Resources/Locale/ru-RU/objectives/conditions/anomaly-supercrit.ftl new file mode 100644 index 00000000000..bbd3d181d1d --- /dev/null +++ b/Resources/Locale/ru-RU/objectives/conditions/anomaly-supercrit.ftl @@ -0,0 +1 @@ +objective-condition-supercrit-anomalies-title = Коллапсируйте {$count} аномалии diff --git a/Resources/Locale/ru-RU/objectives/conditions/kill-person.ftl b/Resources/Locale/ru-RU/objectives/conditions/kill-person.ftl index f542b9df911..07ac5f99dcb 100644 --- a/Resources/Locale/ru-RU/objectives/conditions/kill-person.ftl +++ b/Resources/Locale/ru-RU/objectives/conditions/kill-person.ftl @@ -1,3 +1,4 @@ objective-condition-kill-person-title = Убейте или не дайте покинуть станцию { $targetName }, должность: { CAPITALIZE($job) }. objective-condition-kill-maroon-title = Убейте или не дайте покинуть станцию { $targetName }, { CAPITALIZE($job) } +objective-condition-kill-station-ai = Уничтожьте {$targetName}, {CAPITALIZE($job)} и убедитесь, чтобы он оставался выведенным из строя. objective-condition-maroon-person-title = Не позвольте { $targetName }, { CAPITALIZE($job) } попасть на Центком. diff --git a/Resources/Locale/ru-RU/photography/photography.ftl b/Resources/Locale/ru-RU/photography/photography.ftl new file mode 100644 index 00000000000..aecfd3d0c8e --- /dev/null +++ b/Resources/Locale/ru-RU/photography/photography.ftl @@ -0,0 +1,7 @@ +# TODO: Make this a fluent function in RT +photograph-name-text = Это фотография { PROPER($entity) -> + *[false] { INDEFINITE($entity) } { $entity } + [true] { $entity } + }. +photograph-name-text-empty = Это фотография. +photograph-name-text-photograph = Это фотография другой фотографии. diff --git a/Resources/Locale/ru-RU/round-end/round-end-system.ftl b/Resources/Locale/ru-RU/round-end/round-end-system.ftl index c5813491624..7a527f120aa 100644 --- a/Resources/Locale/ru-RU/round-end/round-end-system.ftl +++ b/Resources/Locale/ru-RU/round-end/round-end-system.ftl @@ -7,5 +7,11 @@ round-end-system-shuttle-recalled-announcement = Эвакуационный ша round-end-system-shuttle-sender-announcement = Станция round-end-system-round-restart-eta-announcement = Раунд перезапустится через { $time } { $units }... -eta-units-minutes = минут -eta-units-seconds = секунд +eta-units-minutes = {$amount -> + [one] минуту + *[other] минут +} +eta-units-seconds = {$amount -> + [one] секунду + *[other] секунд +} diff --git a/Resources/Locale/ru-RU/silicons/station-ai.ftl b/Resources/Locale/ru-RU/silicons/station-ai.ftl index be6c2b3b54f..c36055e4f1c 100644 --- a/Resources/Locale/ru-RU/silicons/station-ai.ftl +++ b/Resources/Locale/ru-RU/silicons/station-ai.ftl @@ -8,6 +8,7 @@ station-ai-has-no-power-for-upload = Загрузка не удалась — я station-ai-is-too-damaged-for-upload = Загрузка не удалась — ядро ИИ нужно отремонтировать. station-ai-core-losing-power = Ваше ядро ИИ питается от резервной батареи. station-ai-core-critical-power = Ваше ядро ИИ имеет критически низкий уровень энергии. Возобновите внешнее питание, иначе может произойти серьёзное повреждение данных! +station-ai-core-taking-damage = Ваше ядро ИИ получает физические повреждения. # Ghost role station-ai-ghost-role-name = Station AI diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/actions/station_ai.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/actions/station_ai.ftl index 92d829df18e..5e83ffa740d 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/actions/station_ai.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/actions/station_ai.ftl @@ -1,6 +1,4 @@ ent-ActionJumpToCore = Перейти к ядру .desc = Возвращает ваш обзор к ядру. -ent-ActionSurvCameraLights = Переключить подсветку камеры - .desc = Включает подсветку камеры наблюдения вблизи места, где вы находитесь. ent-ActionAIViewLaws = Просмотреть законы .desc = Ознакомьтесь с законами, которым вы должны следовать. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/actions/types.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/actions/types.ftl index b5430e30b15..b1a9de4ff5c 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/actions/types.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/actions/types.ftl @@ -70,6 +70,8 @@ ent-ActionToggleWaggingVulpkanin = { ent-ActionToggleWagging } .desc = { ent-ActionToggleWagging.desc } ent-ActionGravityJump = Прыжок .desc = Активируя усовершенствованную систему перемещения, совершите короткий прыжок в направлении вашего взгляда. +ent-ActionJumpBoost = Прыжок + .desc = { ent-ActionGravityJump.desc } ent-ActionVulpkaninGravityJump = Скачок .desc = Используя свои проворные ноги, совершите скачок на короткую дистанцию. Будьте осторожны, не врежьтесь ни во что! ent-ActionToggleRootable = Укоренение 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 7b55aafa1b4..9702062853e 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/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/hats.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/hats.ftl index d5efc9d9bfb..5da5a1b2317 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/hats.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/clothing/head/hats.ftl @@ -176,3 +176,5 @@ ent-ClothingHeadHatSolidHeadband = солидная головная повяз .desc = "\"Нося её, вы почувствуете себя Невидимым! (ДИСКЛЕЙМЕР: НА САМОМ ДЕЛЕ НЕ ДЕЛАЕТ НОСИТЕЛЯ НЕВИДИМЫМ)\"" ent-ClothingHeadPropellerHat = кепка с пропеллером .desc = Ты самый лучший мальчик. +ent-ClothingHeadHatMitreClown = митра Хонкоматери + .desc = Прихожанам трудно разглядеть банановую кожуру на полу, когда они смотрят на вашу великолепную шляпу. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/travel_camera.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/travel_camera.ftl new file mode 100644 index 00000000000..8e50c7f2b79 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/travel_camera.ftl @@ -0,0 +1,10 @@ +ent-TravelCamera = фотоаппарат для путешествий + .desc = Одна картинка стоит тысячи слов. В комплекте сверхяркая вспышка и встроенная перезаряжаемая фотопленка. +ent-BasePhotograph = фотография +ent-PhotographBlack = { ent-BasePhotograph } +ent-PhotographRed = { ent-BasePhotograph } +ent-PhotographBlue = { ent-BasePhotograph } +ent-PhotographGreen = { ent-BasePhotograph } +ent-PhotographYellow = { ent-BasePhotograph } +ent-PhotographPurple = { ent-BasePhotograph } +ent-PhotographRainbow = { ent-BasePhotograph } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/robotics/borg_modules.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/robotics/borg_modules.ftl index 8025f342f5a..12e24268d9a 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/robotics/borg_modules.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/specific/robotics/borg_modules.ftl @@ -116,6 +116,8 @@ ent-XenoborgModuleFireExtinguisher = огнетушащий модуль ксе .desc = Модуль с самозаполняющимся огнетушителем. ent-XenoborgModuleDoorControl = модуль контроля шлюзов ксеноборгов .desc = Модуль позволяющий ксеноборгам контролировать шлюзы. +ent-XenoborgModuleTileGun = плиткомётный модуль ксеноборга + .desc = Модуль с плиткомётом. Подождите, что? ent-XenoborgModuleJammer = глушащий модуль ксеноборга .desc = Модуль с глушителем связи. ent-XenoborgModuleLaser = модуль ксеноборга с лазерной винтовкой @@ -124,6 +126,8 @@ ent-XenoborgModuleHeavyLaser = модуль ксеноборга с лазерн .desc = Модуль с лазерной пушкой. ent-XenoborgModuleSpaceMovement = модуль ксеноборга для движения в космосе .desc = Модуль, который помогает ксеноборгу лучше передвигаться в космосе. +ent-XenoborgModuleJump = прыжковый модуль ксеноборга + .desc = Модуль, позволяющий ксеноборгу совершать прыжок вперёд. ent-XenoborgModuleSword = модуль ксеноборга с ножом .desc = Модуль с ножом кукри. ent-XenoborgModuleEnergySword = модуль ксеноборга с энергокинжалом diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/tools/hijack_beacon.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/tools/hijack_beacon.ftl new file mode 100644 index 00000000000..40d460b6631 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/tools/hijack_beacon.ftl @@ -0,0 +1,2 @@ +ent-HijackBeacon = маяк взлома + .desc = Устройство, позволяющее обходить брандмауэр на автоматизированных торговых станциях Nanotrasen. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/guns/tile_gun.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/guns/tile_gun.ftl new file mode 100644 index 00000000000..1779a7c3531 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/guns/tile_gun.ftl @@ -0,0 +1,5 @@ +ent-WeaponTileGun = плиткострел + .desc = Странное оружие, стреляющее плитками. Стреляйте в них полом! +ent-WeaponTileGunEmpty = { ent-WeaponTileGun} + .desc = { ent-WeaponTileGun.desc } + .suffix = пустой diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/statuseffects/damage.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/statuseffects/damage.ftl new file mode 100644 index 00000000000..b24c69d75d4 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/statuseffects/damage.ftl @@ -0,0 +1 @@ +ent-StatusEffectRadiationProtection = защита от радиации diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/objectives/traitor.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/objectives/traitor.ftl index 267ae165db7..cbf0eb11d73 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/objectives/traitor.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/objectives/traitor.ftl @@ -12,6 +12,8 @@ ent-KillRandomPersonObjective = { ent-BaseTraitorObjective } .desc = Сделайте это, как посчитаете нужным. Только убедитесь, что цель не покинет станцию. ent-KillRandomHeadObjective = { ent-BaseTraitorObjective } .desc = Нам нужно, чтобы этот глава исчез, и вы, вероятно, знаете, почему. Убедитесь, что глава не попадёт на Центком даже в мёртвом виде. Удачи, агент. +ent-KillStationAiObjective = {ent-BaseTraitorObjective } + .desc = Nanotrasen с гордостью заявляет о своей передовой технологии искусственного интеллекта. Напомните им, что это всего лишь игрушка, которую можно сломать. ent-RandomTraitorAliveObjective = { ent-BaseTraitorSocialObjective } .desc = Раскрывать себя или нет — решайте сами. Нам нужно, чтобы он выжил. ent-RandomTraitorProgressObjective = { ent-BaseTraitorSocialObjective } @@ -46,7 +48,9 @@ ent-CaptainJetpackStealObjective = { ent-BaseCaptainObjective } .desc = { ent-BaseCaptainObjective.desc } ent-CaptainGunStealObjective = { ent-BaseCaptainObjective } .desc = { ent-BaseCaptainObjective.desc } - -# ent-NukeDiskStealObjective = { ent-BaseCaptainObjective } -# .desc = { ent-BaseCaptainObjective.desc } - +ent-NukeDiskStealObjective = { ent-BaseCaptainObjective } + .desc = { ent-BaseCaptainObjective.desc } +ent-SupercritAnomaliesObjective = { ent-BaseTraitorObjective} + .desc = Nanotrasen проявляет большой интерес к аномалиям, которые могут иметь потенциально катастрофические последствия. Познакомьте их с огнем, с которым они играют. +ent-HijackTradeStationObjective = Взломайте автоматизированную торговую станцию + .desc = Вашему аплинку разрешен один маяк взлома. Разместите его на автоматизированной торговой станции и защищайте его, пока он взламывает торговую станцию. diff --git a/Resources/Locale/ru-RU/store/categories.ftl b/Resources/Locale/ru-RU/store/categories.ftl index dbc33617e6b..cd5090582bc 100644 --- a/Resources/Locale/ru-RU/store/categories.ftl +++ b/Resources/Locale/ru-RU/store/categories.ftl @@ -12,6 +12,7 @@ store-category-allies = Союзники store-category-job = Работа store-category-wearables = Экипировка store-category-pointless = Безделушки +store-category-objective = Цель store-discounted-items = Скидки # Revenant diff --git a/Resources/Locale/ru-RU/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/store/uplink-catalog.ftl index 1292740b500..4c866f9bd2f 100644 --- a/Resources/Locale/ru-RU/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/store/uplink-catalog.ftl @@ -409,6 +409,9 @@ uplink-soap-desc = Не вызывающий доверия кусок мыла. uplink-ultrabright-lantern-name = Сверхъяркий светильник uplink-ultrabright-lantern-desc = Этот сверхъяркий фонарь можно использовать для ослепления людей, подобно вспышке. +uplink-travel-camera-name = Фотоаппарат для путешествий +uplink-travel-camera-desc = Поразите окружающих своими навыками фотографии и удобной, но легальной фотовспышкой. Вы будете выглядеть как турист. + uplink-combat-medkit-name = Боевая аптечка uplink-combat-medkit-desc = Аптечка, предназначенная для лечения боевых ранений. @@ -520,3 +523,7 @@ uplink-briefcase-gun-desc = Незаметный чемодан с очень к uplink-energycrossbow-name = мини-энергоарбалет uplink-energycrossbow-desc = Незаменимое личное оружие каждого оперативника, предпочитающего, чтобы его цели не двигались. Стреляет возобновляющимися ядовитыми зарядами, которые мгновенно сбивают жертву с ног. + +#Objective items +uplink-hijack-beacon-name = Маяк взлома +uplink-hijack-beacon-desc = Это маяк взлома, разработанный Синдикатом для обхода брандмауэров автоматизированных торговых станций Nanotrasen. Для срабатывания требуется 200 секунд и торговые станции объявляют о взломе, поэтому подготовьтесь соответствующим образом. diff --git a/Resources/Locale/ru-RU/worldgen/applyworldgenconfig.ftl b/Resources/Locale/ru-RU/worldgen/applyworldgenconfig.ftl deleted file mode 100644 index c755d35c555..00000000000 --- a/Resources/Locale/ru-RU/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/Maps/Corvax/corvax_astra.yml b/Resources/Maps/Corvax/corvax_astra.yml index c3e912111ce..ac632c3d3ab 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 db6bcb024f7..4129560b784 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 + 0: 34959 + 1: 13056 -6,-11: - 1: 63675 + 1: 63539 + 0: 8 -6,-10: - 1: 65523 + 1: 65535 -6,-13: - 0: 24320 + 0: 22272 -5,-12: - 0: 34959 - 1: 13056 + 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: -0.5,-29.5 + parent: 2 + - uid: 12160 components: - type: Transform - pos: -3.5,-27.5 + 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 + - uid: 10322 + components: + - type: Transform + pos: -11.5,-17.5 + parent: 2 + - uid: 11175 + components: + - type: Transform + pos: -11.5,-19.5 + parent: 2 + - uid: 12158 + components: + - type: Transform + pos: -18.5,-40.5 + parent: 2 +- proto: AirlockEVAGlassLocked entities: - - uid: 193 + - uid: 19365 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -52.5,31.5 + pos: 20.5,-31.5 parent: 2 - - uid: 194 + - uid: 19366 components: - type: Transform - pos: -50.5,31.5 + 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 -- 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 + - uid: 14425 components: - type: Transform - pos: -3.5,-31.5 + rot: 1.5707963267948966 rad + pos: -53.5,32.5 parent: 2 - - uid: 684 + - uid: 15318 components: - type: Transform - pos: -2.5,-31.5 + rot: -1.5707963267948966 rad + pos: -51.5,32.5 parent: 2 - - uid: 685 +- proto: AtmosDeviceFanTiny + entities: + - uid: 8097 components: - type: Transform - pos: -2.5,-32.5 + rot: 3.141592653589793 rad + pos: -8.5,-32.5 parent: 2 +- proto: AtmosFixBlockerMarker + entities: - 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 - components: - - type: Transform - pos: -1.5,-33.5 - parent: 2 - - uid: 967 - components: - - type: Transform - pos: -9.5,-30.5 - parent: 2 - - uid: 968 - components: - - type: Transform - pos: -12.5,-30.5 - parent: 2 - - uid: 969 + - uid: 683 components: - type: Transform - pos: -17.5,-33.5 + pos: -15.5,-35.5 parent: 2 - - uid: 970 + - uid: 685 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: -5.5,-38.5 + parent: 2 + - uid: 2851 components: - type: Transform - pos: 0.5,-30.5 + pos: -11.5,-38.5 + parent: 2 + - uid: 2862 + components: + - type: Transform + pos: -8.5,-38.5 parent: 2 - uid: 16953 components: @@ -14366,75 +14254,81 @@ entities: parent: 2 - proto: BlastDoorOpen entities: - - uid: 993 + - uid: 995 components: - type: Transform - pos: -6.5,-19.5 + pos: 6.5,-18.5 parent: 2 - - uid: 994 + - uid: 998 components: - type: Transform - pos: -6.5,-18.5 + pos: -4.5,-3.5 parent: 2 - - uid: 995 + - uid: 999 components: - type: Transform - pos: 6.5,-18.5 + pos: -4.5,-4.5 parent: 2 - - uid: 996 + - uid: 1000 components: - type: Transform - pos: -8.5,-19.5 + pos: -4.5,-5.5 parent: 2 - - uid: 997 + - uid: 1004 components: - type: Transform - pos: -8.5,-18.5 + pos: 29.5,-26.5 parent: 2 - - uid: 998 + - uid: 1005 components: - type: Transform - pos: -4.5,-3.5 + pos: 28.5,-26.5 parent: 2 - - uid: 999 + - uid: 1006 components: - type: Transform - pos: -4.5,-4.5 + pos: 59.5,-7.5 parent: 2 - - uid: 1000 + - uid: 19388 components: - type: Transform - pos: -4.5,-5.5 + rot: 1.5707963267948966 rad + pos: -5.5,-33.5 parent: 2 - - uid: 1001 + - uid: 19389 components: - type: Transform - pos: -8.5,-32.5 + rot: 1.5707963267948966 rad + pos: -7.5,-33.5 parent: 2 - - uid: 1002 + - uid: 19390 components: - type: Transform - pos: -11.5,-32.5 + rot: 1.5707963267948966 rad + pos: -6.5,-33.5 parent: 2 - - uid: 1003 + - uid: 19391 components: - type: Transform - pos: 1.5,-32.5 + rot: 1.5707963267948966 rad + pos: -5.5,-31.5 parent: 2 - - uid: 1004 + - uid: 19392 components: - type: Transform - pos: 29.5,-26.5 + rot: 1.5707963267948966 rad + pos: -6.5,-31.5 parent: 2 - - uid: 1005 + - uid: 19393 components: - type: Transform - pos: 28.5,-26.5 + rot: 1.5707963267948966 rad + pos: -7.5,-31.5 parent: 2 - - uid: 1006 + - uid: 19394 components: - type: Transform - pos: 59.5,-7.5 + pos: -4.5,-32.5 parent: 2 - proto: BlockGameArcade entities: @@ -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,1182 +26411,1582 @@ entities: - type: Transform pos: 11.5,20.5 parent: 16911 -- proto: CableApcStack - entities: - - uid: 3259 + - uid: 19378 components: - type: Transform - pos: -6.5362616,47.765694 + pos: -11.5,-17.5 parent: 2 - - uid: 3260 + - uid: 19379 components: - type: Transform - pos: 0.9590117,-23.480143 + pos: -10.5,-17.5 parent: 2 - - uid: 3261 + - uid: 19380 components: - type: Transform - pos: 0.61839914,-18.39082 + pos: -5.5,-15.5 parent: 2 - - uid: 3262 + - uid: 19381 components: - type: Transform - pos: 1.0076228,-23.237087 + pos: -5.5,-16.5 parent: 2 - - uid: 3263 + - uid: 19382 components: - type: Transform - pos: 0.9729006,-23.362087 + pos: -5.5,-17.5 parent: 2 - - uid: 3264 + - uid: 19395 components: - type: Transform - pos: -45.439762,38.055 + pos: -17.5,-26.5 parent: 2 - - uid: 3265 + - uid: 19396 components: - type: Transform - pos: -45.439762,37.883125 + pos: -17.5,-27.5 parent: 2 - - uid: 3266 + - uid: 19397 components: - type: Transform - pos: 50.431786,-21.32471 + pos: -17.5,-28.5 parent: 2 - - uid: 3267 + - uid: 19398 components: - type: Transform - pos: -6.5206366,47.578194 + pos: -17.5,-29.5 parent: 2 -- proto: CableApcStack1 - entities: - - uid: 3268 + - uid: 19399 components: - type: Transform - pos: -69.195435,35.493317 + pos: -17.5,-30.5 parent: 2 - - type: Stack - count: 4 - - uid: 3269 + - uid: 19400 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -32.76652,63.53888 + pos: -17.5,-31.5 parent: 2 - - uid: 3270 + - uid: 19401 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -32.79777,63.44513 + pos: -17.5,-32.5 parent: 2 -- proto: CableHV - entities: - - uid: 3271 + - uid: 19402 components: - type: Transform - pos: -33.5,-21.5 + pos: -16.5,-30.5 parent: 2 - - uid: 3272 + - uid: 19403 components: - type: Transform - pos: -7.5,48.5 + pos: -15.5,-30.5 parent: 2 - - uid: 3273 + - uid: 19404 components: - type: Transform - pos: -30.5,-22.5 + pos: -14.5,-30.5 parent: 2 - - uid: 3274 + - uid: 19405 components: - type: Transform - pos: 24.5,17.5 + pos: -14.5,-31.5 parent: 2 - - uid: 3275 + - uid: 19406 components: - type: Transform - pos: -31.5,-22.5 + pos: -14.5,-32.5 parent: 2 - - uid: 3276 + - uid: 19407 components: - type: Transform - pos: 23.5,13.5 + pos: -14.5,-33.5 parent: 2 - - uid: 3277 + - uid: 19408 components: - type: Transform - pos: -35.5,-21.5 + pos: -13.5,-33.5 parent: 2 - - uid: 3278 + - uid: 19409 components: - type: Transform - pos: -26.5,-24.5 + pos: -12.5,-33.5 parent: 2 - - uid: 3279 + - uid: 19410 components: - type: Transform - pos: -26.5,-26.5 + pos: -12.5,-34.5 parent: 2 - - uid: 3280 + - uid: 19411 components: - type: Transform - pos: -26.5,-22.5 + pos: -12.5,-35.5 parent: 2 - - uid: 3281 + - uid: 19412 components: - type: Transform - pos: -7.5,-18.5 + pos: -12.5,-36.5 parent: 2 - - uid: 3282 + - uid: 19413 components: - type: Transform - pos: -7.5,-16.5 + pos: -11.5,-36.5 parent: 2 - - uid: 3283 + - uid: 19414 components: - type: Transform - pos: -35.5,-18.5 + pos: -10.5,-36.5 parent: 2 - - uid: 3284 + - uid: 19415 components: - type: Transform - pos: -35.5,-17.5 + pos: -9.5,-36.5 parent: 2 - - uid: 3285 + - uid: 19416 components: - type: Transform - pos: -37.5,-24.5 + pos: -8.5,-36.5 parent: 2 - - uid: 3286 + - uid: 19417 components: - type: Transform - pos: -35.5,-20.5 + pos: -7.5,-36.5 parent: 2 - - uid: 3287 + - uid: 19418 components: - type: Transform - pos: -24.5,-24.5 + pos: -4.5,-36.5 parent: 2 - - uid: 3288 + - uid: 19419 components: - type: Transform - pos: -24.5,-23.5 + pos: -5.5,-36.5 parent: 2 - - uid: 3289 + - uid: 19420 components: - type: Transform - pos: -35.5,-15.5 + pos: -3.5,-36.5 parent: 2 - - uid: 3290 + - uid: 19421 components: - type: Transform - pos: -35.5,-16.5 + pos: -2.5,-36.5 parent: 2 - - uid: 3291 + - uid: 19422 components: - type: Transform - pos: -7.5,-17.5 + pos: -6.5,-36.5 parent: 2 - - uid: 3292 + - uid: 19423 components: - type: Transform - pos: -7.5,-19.5 + pos: -1.5,-36.5 parent: 2 - - uid: 3293 + - uid: 19424 components: - type: Transform - pos: -7.5,-20.5 + pos: -0.5,-36.5 parent: 2 - - uid: 3294 + - uid: 19425 components: - type: Transform - pos: -35.5,-29.5 + pos: 0.5,-36.5 parent: 2 - - uid: 3295 + - uid: 19426 components: - type: Transform - pos: -35.5,-32.5 + pos: -14.5,-34.5 parent: 2 - - uid: 3296 + - uid: 19427 components: - type: Transform - pos: -35.5,-27.5 + pos: -14.5,-35.5 parent: 2 - - uid: 3297 + - uid: 19428 components: - type: Transform - pos: -36.5,-33.5 + pos: -14.5,-36.5 parent: 2 - - uid: 3298 + - uid: 19429 components: - type: Transform - pos: -23.5,-19.5 + pos: -10.5,-37.5 parent: 2 - - uid: 3299 + - uid: 19430 components: - type: Transform - pos: -33.5,-22.5 + pos: -10.5,-38.5 parent: 2 - - uid: 3300 + - uid: 19431 components: - type: Transform - pos: -23.5,-21.5 + pos: -10.5,-39.5 parent: 2 - - uid: 3301 + - uid: 19432 components: - type: Transform - pos: -24.5,-22.5 + pos: -7.5,-37.5 parent: 2 - - uid: 3302 + - uid: 19433 components: - type: Transform - pos: -26.5,-25.5 + pos: -7.5,-38.5 parent: 2 - - uid: 3303 + - uid: 19434 components: - type: Transform - pos: -26.5,-23.5 + pos: -7.5,-39.5 parent: 2 - - uid: 3304 + - uid: 19435 components: - type: Transform - pos: -26.5,-28.5 + pos: -4.5,-37.5 parent: 2 - - uid: 3305 + - uid: 19436 components: - type: Transform - pos: -36.5,-35.5 + pos: -4.5,-38.5 parent: 2 - - uid: 3306 + - uid: 19437 components: - type: Transform - pos: -26.5,-27.5 + pos: -4.5,-39.5 parent: 2 - - uid: 3307 + - uid: 19438 components: - type: Transform - pos: -24.5,-26.5 + pos: -1.5,-37.5 parent: 2 - - uid: 3308 + - uid: 19439 components: - type: Transform - pos: -17.5,4.5 + pos: -1.5,-38.5 parent: 2 - - uid: 3309 + - uid: 19440 components: - type: Transform - pos: -32.5,-22.5 + pos: -1.5,-39.5 parent: 2 - - uid: 3310 + - uid: 19441 components: - type: Transform - pos: -36.5,-34.5 + pos: -11.5,-33.5 parent: 2 - - uid: 3311 + - uid: 19442 components: - type: Transform - pos: -33.5,-24.5 + pos: -10.5,-33.5 parent: 2 - - uid: 3312 + - uid: 19443 components: - type: Transform - pos: -26.5,-29.5 + pos: -9.5,-33.5 parent: 2 - - uid: 3313 + - uid: 19444 components: - type: Transform - pos: -24.5,-29.5 + pos: -9.5,-32.5 parent: 2 - - uid: 3314 + - uid: 19445 components: - type: Transform - pos: -35.5,-19.5 + pos: -8.5,-32.5 parent: 2 - - uid: 3315 + - uid: 19446 components: - type: Transform - pos: -24.5,-30.5 + pos: -14.5,-29.5 parent: 2 - - uid: 3316 + - uid: 19447 components: - type: Transform - pos: -35.5,-14.5 + pos: -14.5,-28.5 parent: 2 - - uid: 3317 + - uid: 19448 components: - type: Transform - pos: -35.5,-22.5 + pos: -13.5,-28.5 parent: 2 - - uid: 3318 + - uid: 19449 components: - type: Transform - pos: -35.5,-13.5 + pos: -12.5,-28.5 parent: 2 - - uid: 3319 + - uid: 19450 components: - type: Transform - pos: -29.5,-22.5 + pos: -12.5,-27.5 parent: 2 - - uid: 3320 + - uid: 19451 components: - type: Transform - pos: -28.5,-22.5 + pos: -11.5,-28.5 parent: 2 - - uid: 3321 + - uid: 19452 components: - type: Transform - pos: -27.5,-22.5 + pos: -10.5,-28.5 parent: 2 - - uid: 3322 + - uid: 19453 components: - type: Transform - pos: -38.5,-18.5 + pos: -9.5,-28.5 parent: 2 - - uid: 3323 + - uid: 19454 components: - type: Transform - pos: -38.5,-17.5 + pos: -8.5,-28.5 parent: 2 - - uid: 3324 + - uid: 19455 components: - type: Transform - pos: -38.5,-16.5 + pos: -7.5,-28.5 parent: 2 - - uid: 3325 + - uid: 19456 components: - type: Transform - pos: -38.5,-15.5 + pos: -6.5,-28.5 parent: 2 - - uid: 3326 + - uid: 19457 components: - type: Transform - pos: -32.5,-15.5 + pos: -5.5,-28.5 parent: 2 - - uid: 3327 + - uid: 19458 components: - type: Transform - pos: -32.5,-18.5 + pos: -4.5,-28.5 parent: 2 - - uid: 3328 + - uid: 19459 components: - type: Transform - pos: -32.5,-16.5 + pos: -3.5,-28.5 parent: 2 - - uid: 3329 + - uid: 19460 components: - type: Transform - pos: -32.5,-17.5 + pos: -2.5,-28.5 parent: 2 - - uid: 3330 + - uid: 19488 components: - type: Transform - pos: -35.5,-12.5 + pos: 1.5,-24.5 parent: 2 - - uid: 3331 + - uid: 19489 components: - type: Transform - pos: -35.5,-11.5 + pos: 1.5,-25.5 parent: 2 - - uid: 3332 + - uid: 19490 components: - type: Transform - pos: -35.5,-10.5 + pos: 1.5,-26.5 parent: 2 - - uid: 3333 + - uid: 19491 components: - type: Transform - pos: -36.5,-10.5 + pos: 2.5,-27.5 parent: 2 - - uid: 3334 + - uid: 19492 components: - type: Transform - pos: -37.5,-10.5 + pos: 2.5,-28.5 parent: 2 - - uid: 3335 + - uid: 19493 components: - type: Transform - pos: -34.5,-10.5 + pos: 2.5,-29.5 parent: 2 - - uid: 3336 + - uid: 19507 components: - type: Transform - pos: -33.5,-10.5 + pos: 15.5,-27.5 parent: 2 - - uid: 3337 + - uid: 19508 components: - type: Transform - pos: -36.5,-13.5 + pos: 15.5,-28.5 parent: 2 - - uid: 3338 + - uid: 19509 components: - type: Transform - pos: -37.5,-13.5 + pos: 16.5,-28.5 parent: 2 - - uid: 3339 + - uid: 19510 components: - type: Transform - pos: -38.5,-13.5 + pos: 16.5,-30.5 parent: 2 - - uid: 3340 + - uid: 19511 components: - type: Transform - pos: -39.5,-13.5 + pos: 16.5,-29.5 parent: 2 - - uid: 3341 + - uid: 19512 components: - type: Transform - pos: -39.5,-12.5 + pos: 16.5,-31.5 parent: 2 - - uid: 3342 + - uid: 19513 components: - type: Transform - pos: -39.5,-14.5 + pos: 17.5,-31.5 parent: 2 - - uid: 3343 + - uid: 19514 components: - type: Transform - pos: -34.5,-13.5 + pos: 18.5,-31.5 parent: 2 - - uid: 3344 + - uid: 19515 components: - type: Transform - pos: -33.5,-13.5 + pos: 19.5,-31.5 parent: 2 - - uid: 3345 + - uid: 19516 components: - type: Transform - pos: -32.5,-13.5 + pos: 17.5,-29.5 parent: 2 - - uid: 3346 + - uid: 19517 components: - type: Transform - pos: -31.5,-13.5 + pos: 18.5,-29.5 parent: 2 - - uid: 3347 + - uid: 19518 components: - type: Transform - pos: -31.5,-12.5 + pos: 19.5,-29.5 parent: 2 - - uid: 3348 + - uid: 19530 components: - type: Transform - pos: -31.5,-14.5 + pos: -21.5,-36.5 parent: 2 - - uid: 3349 + - uid: 19531 components: - type: Transform - pos: -39.5,-10.5 + pos: -21.5,-35.5 parent: 2 - - uid: 3350 + - uid: 19532 components: - type: Transform - pos: -38.5,-10.5 + pos: -20.5,-36.5 parent: 2 - - uid: 3351 + - uid: 19533 components: - type: Transform - pos: -39.5,-11.5 + pos: -19.5,-36.5 parent: 2 - - uid: 3352 + - uid: 19534 components: - type: Transform - pos: -32.5,-10.5 + pos: -18.5,-36.5 parent: 2 - - uid: 3353 + - uid: 19535 components: - type: Transform - pos: -31.5,-10.5 + pos: -20.5,-37.5 parent: 2 - - uid: 3354 + - uid: 19536 components: - type: Transform - pos: -31.5,-11.5 + pos: -20.5,-38.5 parent: 2 - - uid: 3355 + - uid: 19537 components: - type: Transform - pos: -36.5,-15.5 + pos: -20.5,-39.5 parent: 2 - - uid: 3356 + - uid: 19538 components: - type: Transform - pos: -37.5,-15.5 + pos: -20.5,-40.5 parent: 2 - - uid: 3357 + - uid: 19539 components: - type: Transform - pos: -34.5,-15.5 + pos: -19.5,-40.5 parent: 2 - - uid: 3358 + - uid: 19540 components: - type: Transform - pos: -33.5,-15.5 + pos: -18.5,-40.5 parent: 2 - - uid: 3359 + - uid: 19541 components: - type: Transform - pos: -38.5,-19.5 + pos: -17.5,-40.5 parent: 2 - - uid: 3360 + - uid: 19542 components: - type: Transform - pos: -37.5,-19.5 + pos: -16.5,-40.5 parent: 2 - - uid: 3361 + - uid: 19543 components: - type: Transform - pos: -36.5,-19.5 + pos: -15.5,-40.5 parent: 2 - - uid: 3362 + - uid: 19544 components: - type: Transform - pos: -34.5,-19.5 + pos: -14.5,-40.5 parent: 2 - - uid: 3363 +- proto: CableApcStack + entities: + - uid: 3259 components: - type: Transform - pos: -33.5,-19.5 + pos: -6.5362616,47.765694 parent: 2 - - uid: 3364 + - uid: 3260 components: - type: Transform - pos: -32.5,-19.5 + pos: 0.9590117,-23.480143 parent: 2 - - uid: 3365 + - uid: 3261 components: - type: Transform - pos: -34.5,-33.5 + pos: 0.61839914,-18.39082 parent: 2 - - uid: 3366 + - uid: 3262 components: - type: Transform - pos: -36.5,-24.5 + pos: 1.0076228,-23.237087 parent: 2 - - uid: 3367 + - uid: 3263 components: - type: Transform - pos: -33.5,-25.5 + pos: 0.9729006,-23.362087 parent: 2 - - uid: 3368 + - uid: 3264 components: - type: Transform - pos: -37.5,-25.5 + pos: -45.439762,38.055 parent: 2 - - uid: 3369 + - uid: 3265 components: - type: Transform - pos: -35.5,-30.5 + pos: -45.439762,37.883125 parent: 2 - - uid: 3370 + - uid: 3266 components: - type: Transform - pos: -35.5,-33.5 + pos: 50.431786,-21.32471 parent: 2 - - uid: 3371 + - uid: 3267 components: - type: Transform - pos: -35.5,-31.5 + pos: -6.5206366,47.578194 parent: 2 - - uid: 3372 +- proto: CableApcStack1 + entities: + - uid: 3268 components: - type: Transform - pos: -35.5,-28.5 + pos: -69.195435,35.493317 parent: 2 - - uid: 3373 + - type: Stack + count: 4 + - uid: 3269 components: - type: Transform - pos: -35.5,-25.5 + rot: -1.5707963267948966 rad + pos: -32.76652,63.53888 parent: 2 - - uid: 3374 + - uid: 3270 components: - type: Transform - pos: -24.5,-28.5 + rot: -1.5707963267948966 rad + pos: -32.79777,63.44513 parent: 2 - - uid: 3375 +- proto: CableHV + entities: + - uid: 10 components: - type: Transform - pos: -24.5,-25.5 + pos: -16.5,-17.5 parent: 2 - - uid: 3376 + - uid: 177 components: - type: Transform - pos: -24.5,-27.5 + pos: -15.5,-28.5 parent: 2 - - uid: 3377 + - uid: 916 components: - type: Transform - pos: -24.5,-31.5 + pos: -9.5,-17.5 parent: 2 - - uid: 3378 + - uid: 936 components: - type: Transform - pos: -23.5,-22.5 + pos: -15.5,-17.5 parent: 2 - - uid: 3379 + - uid: 1086 components: - type: Transform - pos: -23.5,-20.5 + pos: -12.5,-28.5 parent: 2 - - uid: 3380 + - uid: 1145 components: - type: Transform - pos: -25.5,-29.5 + pos: -17.5,-28.5 parent: 2 - - uid: 3381 + - uid: 1272 components: - type: Transform - pos: -25.5,-30.5 + pos: -8.5,-28.5 parent: 2 - - uid: 3382 + - uid: 1278 components: - type: Transform - pos: -25.5,-31.5 + pos: -11.5,-28.5 parent: 2 - - uid: 3383 + - uid: 3090 components: - type: Transform - pos: -26.5,-31.5 + pos: -11.5,-17.5 parent: 2 - - uid: 3384 + - uid: 3101 components: - type: Transform - pos: -26.5,-30.5 + pos: -8.5,-17.5 parent: 2 - - uid: 3385 + - uid: 3102 components: - type: Transform - pos: -7.5,-23.5 + pos: -12.5,-17.5 parent: 2 - - uid: 3386 + - uid: 3105 components: - type: Transform - pos: -7.5,-21.5 + pos: -14.5,-28.5 parent: 2 - - uid: 3387 + - uid: 3106 components: - type: Transform - pos: -35.5,-26.5 + pos: -17.5,-19.5 parent: 2 - - uid: 3388 + - uid: 3107 components: - type: Transform - pos: -7.5,-22.5 + pos: -13.5,-28.5 parent: 2 - - uid: 3389 + - uid: 3271 components: - type: Transform - pos: -26.5,-32.5 + pos: -33.5,-21.5 parent: 2 - - uid: 3390 + - uid: 3272 components: - type: Transform - pos: -26.5,-33.5 + pos: -7.5,48.5 parent: 2 - - uid: 3391 + - uid: 3273 components: - type: Transform - pos: -25.5,-33.5 + pos: -30.5,-22.5 parent: 2 - - uid: 3392 + - uid: 3274 components: - type: Transform - pos: -24.5,-33.5 + pos: 24.5,17.5 parent: 2 - - uid: 3393 + - uid: 3275 components: - type: Transform - pos: -23.5,-33.5 + pos: -31.5,-22.5 parent: 2 - - uid: 3394 + - uid: 3276 components: - type: Transform - pos: -23.5,-32.5 + pos: 23.5,13.5 parent: 2 - - uid: 3395 + - uid: 3277 components: - type: Transform - pos: -22.5,-32.5 + pos: -35.5,-21.5 parent: 2 - - uid: 3396 + - uid: 3278 components: - type: Transform - pos: -21.5,-32.5 + pos: -26.5,-24.5 parent: 2 - - uid: 3397 + - uid: 3279 components: - type: Transform - pos: -20.5,-32.5 + pos: -26.5,-26.5 parent: 2 - - uid: 3398 + - uid: 3280 components: - type: Transform - pos: -20.5,-30.5 + pos: -26.5,-22.5 parent: 2 - - uid: 3399 + - uid: 3283 components: - type: Transform - pos: -20.5,-29.5 + pos: -35.5,-18.5 parent: 2 - - uid: 3400 + - uid: 3284 components: - type: Transform - pos: -20.5,-28.5 + pos: -35.5,-17.5 parent: 2 - - uid: 3401 + - uid: 3285 components: - type: Transform - pos: -20.5,-27.5 + pos: -37.5,-24.5 parent: 2 - - uid: 3402 + - uid: 3286 components: - type: Transform - pos: -20.5,-26.5 + pos: -35.5,-20.5 parent: 2 - - uid: 3403 + - uid: 3287 components: - type: Transform - pos: -20.5,-25.5 + pos: -24.5,-24.5 parent: 2 - - uid: 3404 + - uid: 3288 components: - type: Transform - pos: -20.5,-24.5 + pos: -24.5,-23.5 parent: 2 - - uid: 3405 + - uid: 3289 components: - type: Transform - pos: -20.5,-23.5 + pos: -35.5,-15.5 parent: 2 - - uid: 3406 + - uid: 3290 components: - type: Transform - pos: -19.5,-40.5 + pos: -35.5,-16.5 parent: 2 - - uid: 3407 + - uid: 3294 components: - type: Transform - pos: -7.5,-24.5 + pos: -35.5,-29.5 parent: 2 - - uid: 3408 + - uid: 3295 components: - type: Transform - pos: -45.5,1.5 + pos: -35.5,-32.5 parent: 2 - - uid: 3409 + - uid: 3296 components: - type: Transform - pos: -44.5,1.5 + pos: -35.5,-27.5 parent: 2 - - uid: 3410 + - uid: 3297 components: - type: Transform - pos: -42.5,2.5 + pos: -36.5,-33.5 parent: 2 - - uid: 3411 + - uid: 3298 components: - type: Transform - pos: -42.5,-0.5 + pos: -23.5,-19.5 parent: 2 - - uid: 3412 + - uid: 3299 components: - type: Transform - pos: -39.5,1.5 + pos: -33.5,-22.5 parent: 2 - - uid: 3413 + - uid: 3300 components: - type: Transform - pos: -41.5,1.5 + pos: -23.5,-21.5 parent: 2 - - uid: 3414 + - uid: 3301 components: - type: Transform - pos: -8.5,-24.5 + pos: -24.5,-22.5 parent: 2 - - uid: 3415 + - uid: 3302 components: - type: Transform - pos: -9.5,-24.5 + pos: -26.5,-25.5 parent: 2 - - uid: 3416 + - uid: 3303 components: - type: Transform - pos: -6.5,-24.5 + pos: -26.5,-23.5 parent: 2 - - uid: 3417 + - uid: 3304 components: - type: Transform - pos: -5.5,-24.5 + pos: -26.5,-28.5 parent: 2 - - uid: 3418 + - uid: 3305 components: - type: Transform - pos: -8.5,-21.5 + pos: -36.5,-35.5 parent: 2 - - uid: 3419 + - uid: 3306 components: - type: Transform - pos: -5.5,-23.5 + pos: -26.5,-27.5 parent: 2 - - uid: 3420 + - uid: 3307 components: - type: Transform - pos: -4.5,-23.5 + pos: -24.5,-26.5 parent: 2 - - uid: 3421 + - uid: 3308 components: - type: Transform - pos: -9.5,-21.5 + pos: -17.5,4.5 parent: 2 - - uid: 3422 + - uid: 3309 components: - type: Transform - pos: -9.5,-20.5 + pos: -32.5,-22.5 parent: 2 - - uid: 3423 + - uid: 3310 components: - type: Transform - pos: -10.5,-20.5 + pos: -36.5,-34.5 parent: 2 - - uid: 3424 + - uid: 3311 components: - type: Transform - pos: -6.5,-21.5 + pos: -33.5,-24.5 parent: 2 - - uid: 3425 + - uid: 3312 components: - type: Transform - pos: -5.5,-21.5 + pos: -26.5,-29.5 parent: 2 - - uid: 3426 + - uid: 3313 components: - type: Transform - pos: -5.5,-20.5 + pos: -24.5,-29.5 parent: 2 - - uid: 3427 + - uid: 3314 components: - type: Transform - pos: -4.5,-20.5 + pos: -35.5,-19.5 parent: 2 - - uid: 3428 + - uid: 3315 components: - type: Transform - pos: -7.5,-15.5 + pos: -24.5,-30.5 parent: 2 - - uid: 3429 + - uid: 3316 components: - type: Transform - pos: -8.5,-15.5 + pos: -35.5,-14.5 parent: 2 - - uid: 3430 + - uid: 3317 components: - type: Transform - pos: -9.5,-15.5 + pos: -35.5,-22.5 parent: 2 - - uid: 3431 + - uid: 3318 components: - type: Transform - pos: -10.5,-15.5 + pos: -35.5,-13.5 parent: 2 - - uid: 3432 + - uid: 3319 components: - type: Transform - pos: -11.5,-15.5 + pos: -29.5,-22.5 parent: 2 - - uid: 3433 + - uid: 3320 components: - type: Transform - pos: -12.5,-15.5 + pos: -28.5,-22.5 parent: 2 - - uid: 3434 + - uid: 3321 components: - type: Transform - pos: -13.5,-15.5 + pos: -27.5,-22.5 parent: 2 - - uid: 3435 + - uid: 3322 components: - type: Transform - pos: -14.5,-15.5 + pos: -38.5,-18.5 parent: 2 - - uid: 3436 + - uid: 3323 components: - type: Transform - pos: -15.5,-15.5 + pos: -38.5,-17.5 parent: 2 - - uid: 3437 + - uid: 3324 components: - type: Transform - pos: -16.5,-15.5 + pos: -38.5,-16.5 parent: 2 - - uid: 3438 + - uid: 3325 components: - type: Transform - pos: -16.5,-16.5 + pos: -38.5,-15.5 parent: 2 - - uid: 3439 + - uid: 3326 components: - type: Transform - pos: -16.5,-17.5 + pos: -32.5,-15.5 parent: 2 - - uid: 3440 + - uid: 3327 components: - type: Transform - pos: -17.5,-17.5 + pos: -32.5,-18.5 parent: 2 - - uid: 3441 + - uid: 3328 components: - type: Transform - pos: -17.5,-18.5 + pos: -32.5,-16.5 parent: 2 - - uid: 3442 + - uid: 3329 components: - type: Transform - pos: -17.5,-19.5 + pos: -32.5,-17.5 parent: 2 - - uid: 3443 + - uid: 3330 components: - type: Transform - pos: -17.5,-20.5 + pos: -35.5,-12.5 parent: 2 - - uid: 3444 + - uid: 3331 components: - type: Transform - pos: -17.5,-21.5 + pos: -35.5,-11.5 parent: 2 - - uid: 3445 + - uid: 3332 components: - type: Transform - pos: -17.5,-22.5 + pos: -35.5,-10.5 parent: 2 - - uid: 3446 + - uid: 3333 components: - type: Transform - pos: -17.5,-23.5 + pos: -36.5,-10.5 parent: 2 - - uid: 3447 + - uid: 3334 components: - type: Transform - pos: -18.5,-23.5 + pos: -37.5,-10.5 parent: 2 - - uid: 3448 + - uid: 3335 components: - type: Transform - pos: -19.5,-23.5 + pos: -34.5,-10.5 parent: 2 - - uid: 3449 + - uid: 3336 components: - type: Transform - pos: -20.5,-31.5 + pos: -33.5,-10.5 parent: 2 - - uid: 3450 + - uid: 3337 components: - type: Transform - pos: -18.5,-24.5 + pos: -36.5,-13.5 parent: 2 - - uid: 3451 + - uid: 3338 components: - type: Transform - pos: -16.5,-23.5 + pos: -37.5,-13.5 parent: 2 - - uid: 3452 + - uid: 3339 components: - type: Transform - pos: -16.5,-24.5 + pos: -38.5,-13.5 parent: 2 - - uid: 3453 + - uid: 3340 components: - type: Transform - pos: -37.5,1.5 + pos: -39.5,-13.5 parent: 2 - - uid: 3454 + - uid: 3341 components: - type: Transform - pos: -40.5,1.5 + pos: -39.5,-12.5 parent: 2 - - uid: 3455 + - uid: 3342 components: - type: Transform - pos: -33.5,-20.5 + pos: -39.5,-14.5 parent: 2 - - uid: 3456 + - uid: 3343 components: - type: Transform - pos: -34.5,-20.5 + pos: -34.5,-13.5 parent: 2 - - uid: 3457 + - uid: 3344 components: - type: Transform - pos: -35.5,-23.5 + pos: -33.5,-13.5 parent: 2 - - uid: 3458 + - uid: 3345 components: - type: Transform - pos: -35.5,-24.5 + pos: -32.5,-13.5 parent: 2 - - uid: 3459 + - uid: 3346 components: - type: Transform - pos: -34.5,-24.5 + pos: -31.5,-13.5 parent: 2 - - uid: 3460 + - uid: 3347 components: - type: Transform - pos: -34.5,-35.5 + pos: -31.5,-12.5 parent: 2 - - uid: 3461 + - uid: 3348 components: - type: Transform - pos: -6.5,48.5 + pos: -31.5,-14.5 parent: 2 - - uid: 3462 + - uid: 3349 components: - type: Transform - pos: -13.5,5.5 + pos: -39.5,-10.5 parent: 2 - - uid: 3463 + - uid: 3350 components: - type: Transform - pos: -17.5,5.5 + pos: -38.5,-10.5 parent: 2 - - uid: 3464 + - uid: 3351 components: - type: Transform - pos: -16.5,5.5 + pos: -39.5,-11.5 parent: 2 - - uid: 3465 + - uid: 3352 components: - type: Transform - pos: -15.5,5.5 + pos: -32.5,-10.5 parent: 2 - - uid: 3466 + - uid: 3353 components: - type: Transform - pos: -14.5,5.5 + pos: -31.5,-10.5 parent: 2 - - uid: 3467 + - uid: 3354 components: - type: Transform - pos: -24.5,68.5 + pos: -31.5,-11.5 parent: 2 - - uid: 3468 + - uid: 3355 components: - type: Transform - pos: -23.5,68.5 + pos: -36.5,-15.5 parent: 2 - - uid: 3469 + - uid: 3356 components: - type: Transform - pos: -22.5,68.5 + pos: -37.5,-15.5 parent: 2 - - uid: 3470 + - uid: 3357 components: - type: Transform - pos: -21.5,68.5 + pos: -34.5,-15.5 parent: 2 - - uid: 3471 + - uid: 3358 components: - type: Transform - pos: -20.5,68.5 + pos: -33.5,-15.5 parent: 2 - - uid: 3472 + - uid: 3359 components: - type: Transform - pos: -19.5,68.5 + pos: -38.5,-19.5 parent: 2 - - uid: 3473 + - uid: 3360 components: - type: Transform - pos: -18.5,68.5 + pos: -37.5,-19.5 parent: 2 - - uid: 3474 + - uid: 3361 components: - type: Transform - pos: -17.5,68.5 + pos: -36.5,-19.5 parent: 2 - - uid: 3475 + - uid: 3362 components: - type: Transform - pos: -16.5,68.5 + pos: -34.5,-19.5 parent: 2 - - uid: 3476 + - uid: 3363 components: - type: Transform - pos: -15.5,68.5 + pos: -33.5,-19.5 parent: 2 - - uid: 3477 + - uid: 3364 components: - type: Transform - pos: -14.5,68.5 + pos: -32.5,-19.5 parent: 2 - - uid: 3478 + - uid: 3365 components: - type: Transform - pos: -13.5,68.5 + pos: -34.5,-33.5 parent: 2 - - uid: 3479 + - uid: 3366 components: - type: Transform - pos: -13.5,66.5 + pos: -36.5,-24.5 parent: 2 - - uid: 3480 + - uid: 3367 components: - type: Transform - pos: -14.5,66.5 + pos: -33.5,-25.5 parent: 2 - - uid: 3481 + - uid: 3368 components: - type: Transform - pos: -15.5,66.5 + pos: -37.5,-25.5 parent: 2 - - uid: 3482 + - uid: 3369 components: - type: Transform - pos: -16.5,66.5 + pos: -35.5,-30.5 parent: 2 - - uid: 3483 + - uid: 3370 components: - type: Transform - pos: -17.5,66.5 + pos: -35.5,-33.5 parent: 2 - - uid: 3484 + - uid: 3371 components: - type: Transform - pos: -18.5,66.5 + pos: -35.5,-31.5 parent: 2 - - uid: 3485 + - uid: 3372 components: - type: Transform - pos: -19.5,66.5 + pos: -35.5,-28.5 parent: 2 - - uid: 3486 + - uid: 3373 components: - type: Transform - pos: -20.5,66.5 + pos: -35.5,-25.5 parent: 2 - - uid: 3487 + - uid: 3374 components: - type: Transform - pos: -21.5,66.5 + pos: -24.5,-28.5 parent: 2 - - uid: 3488 + - uid: 3375 components: - type: Transform - pos: -22.5,66.5 + pos: -24.5,-25.5 parent: 2 - - uid: 3489 + - uid: 3376 components: - type: Transform - pos: -23.5,66.5 + pos: -24.5,-27.5 parent: 2 - - uid: 3490 + - uid: 3377 components: - type: Transform - pos: -24.5,66.5 + pos: -24.5,-31.5 parent: 2 - - uid: 3491 + - uid: 3378 components: - type: Transform - pos: -24.5,67.5 + pos: -23.5,-22.5 parent: 2 - - uid: 3492 + - uid: 3379 + components: + - type: Transform + pos: -23.5,-20.5 + parent: 2 + - uid: 3380 + components: + - type: Transform + pos: -25.5,-29.5 + parent: 2 + - uid: 3381 + components: + - type: Transform + pos: -25.5,-30.5 + parent: 2 + - uid: 3382 + components: + - type: Transform + pos: -25.5,-31.5 + parent: 2 + - uid: 3383 + components: + - type: Transform + pos: -26.5,-31.5 + parent: 2 + - uid: 3384 + components: + - type: Transform + pos: -26.5,-30.5 + parent: 2 + - uid: 3387 + components: + - type: Transform + pos: -35.5,-26.5 + parent: 2 + - uid: 3389 + components: + - type: Transform + pos: -26.5,-32.5 + parent: 2 + - uid: 3390 + components: + - type: Transform + pos: -26.5,-33.5 + parent: 2 + - uid: 3391 + components: + - type: Transform + pos: -25.5,-33.5 + parent: 2 + - uid: 3392 + components: + - type: Transform + pos: -24.5,-33.5 + parent: 2 + - uid: 3393 + components: + - type: Transform + pos: -23.5,-33.5 + parent: 2 + - uid: 3394 + components: + - type: Transform + pos: -23.5,-32.5 + parent: 2 + - uid: 3395 + components: + - type: Transform + pos: -22.5,-32.5 + parent: 2 + - uid: 3396 + components: + - type: Transform + pos: -21.5,-32.5 + parent: 2 + - uid: 3397 + components: + - type: Transform + pos: -20.5,-32.5 + parent: 2 + - uid: 3398 + components: + - type: Transform + pos: -20.5,-30.5 + parent: 2 + - uid: 3399 + components: + - type: Transform + pos: -20.5,-29.5 + parent: 2 + - uid: 3400 + components: + - type: Transform + pos: -20.5,-28.5 + parent: 2 + - uid: 3401 + components: + - type: Transform + pos: -20.5,-27.5 + parent: 2 + - uid: 3402 + components: + - type: Transform + pos: -20.5,-26.5 + parent: 2 + - uid: 3403 + components: + - type: Transform + pos: -20.5,-25.5 + parent: 2 + - uid: 3404 + components: + - type: Transform + pos: -20.5,-24.5 + parent: 2 + - uid: 3405 + components: + - type: Transform + pos: -20.5,-23.5 + parent: 2 + - uid: 3408 + components: + - type: Transform + pos: -17.5,-21.5 + parent: 2 + - uid: 3409 + components: + - type: Transform + pos: -9.5,-28.5 + parent: 2 + - uid: 3410 + components: + - type: Transform + pos: -42.5,2.5 + parent: 2 + - uid: 3411 + components: + - type: Transform + pos: -42.5,-0.5 + parent: 2 + - uid: 3412 + components: + - type: Transform + pos: -5.5,-28.5 + parent: 2 + - uid: 3413 + components: + - type: Transform + pos: -17.5,-20.5 + parent: 2 + - uid: 3414 + components: + - type: Transform + pos: -17.5,-17.5 + parent: 2 + - uid: 3415 + components: + - type: Transform + pos: -17.5,-22.5 + parent: 2 + - uid: 3416 + components: + - type: Transform + pos: -17.5,-18.5 + parent: 2 + - uid: 3417 + components: + - type: Transform + pos: -7.5,-28.5 + parent: 2 + - uid: 3419 + components: + - type: Transform + pos: -6.5,-28.5 + parent: 2 + - uid: 3420 + components: + - type: Transform + pos: -8.5,-16.5 + parent: 2 + - uid: 3428 + components: + - type: Transform + pos: -7.5,-15.5 + parent: 2 + - uid: 3429 + components: + - type: Transform + pos: -8.5,-15.5 + parent: 2 + - uid: 3435 + components: + - type: Transform + pos: -17.5,-23.5 + parent: 2 + - uid: 3447 + components: + - type: Transform + pos: -18.5,-23.5 + parent: 2 + - uid: 3448 + components: + - type: Transform + pos: -19.5,-23.5 + parent: 2 + - uid: 3449 + components: + - type: Transform + pos: -20.5,-31.5 + parent: 2 + - uid: 3452 + components: + - type: Transform + pos: -16.5,-24.5 + parent: 2 + - uid: 3455 + components: + - type: Transform + pos: -33.5,-20.5 + parent: 2 + - uid: 3456 + components: + - type: Transform + pos: -34.5,-20.5 + parent: 2 + - uid: 3457 + components: + - type: Transform + pos: -35.5,-23.5 + parent: 2 + - uid: 3458 + components: + - type: Transform + pos: -35.5,-24.5 + parent: 2 + - uid: 3459 + components: + - type: Transform + pos: -34.5,-24.5 + parent: 2 + - uid: 3460 + components: + - type: Transform + pos: -34.5,-35.5 + parent: 2 + - uid: 3461 + components: + - type: Transform + pos: -6.5,48.5 + parent: 2 + - uid: 3462 + components: + - type: Transform + pos: -13.5,5.5 + parent: 2 + - uid: 3463 + components: + - type: Transform + pos: -17.5,5.5 + parent: 2 + - uid: 3464 + components: + - type: Transform + pos: -16.5,5.5 + parent: 2 + - uid: 3465 + components: + - type: Transform + pos: -15.5,5.5 + parent: 2 + - uid: 3466 + components: + - type: Transform + pos: -14.5,5.5 + parent: 2 + - uid: 3467 + components: + - type: Transform + pos: -24.5,68.5 + parent: 2 + - uid: 3468 + components: + - type: Transform + pos: -23.5,68.5 + parent: 2 + - uid: 3469 + components: + - type: Transform + pos: -22.5,68.5 + parent: 2 + - uid: 3470 + components: + - type: Transform + pos: -21.5,68.5 + parent: 2 + - uid: 3471 + components: + - type: Transform + pos: -20.5,68.5 + parent: 2 + - uid: 3472 + components: + - type: Transform + pos: -19.5,68.5 + parent: 2 + - uid: 3473 + components: + - type: Transform + pos: -18.5,68.5 + parent: 2 + - uid: 3474 + components: + - type: Transform + pos: -17.5,68.5 + parent: 2 + - uid: 3475 + components: + - type: Transform + pos: -16.5,68.5 + parent: 2 + - uid: 3476 + components: + - type: Transform + pos: -15.5,68.5 + parent: 2 + - uid: 3477 + components: + - type: Transform + pos: -14.5,68.5 + parent: 2 + - uid: 3478 + components: + - type: Transform + pos: -13.5,68.5 + parent: 2 + - uid: 3479 + components: + - type: Transform + pos: -13.5,66.5 + parent: 2 + - uid: 3480 + components: + - type: Transform + pos: -14.5,66.5 + parent: 2 + - uid: 3481 + components: + - type: Transform + pos: -15.5,66.5 + parent: 2 + - uid: 3482 + components: + - type: Transform + pos: -16.5,66.5 + parent: 2 + - uid: 3483 + components: + - type: Transform + pos: -17.5,66.5 + parent: 2 + - uid: 3484 + components: + - type: Transform + pos: -18.5,66.5 + parent: 2 + - uid: 3485 + components: + - type: Transform + pos: -19.5,66.5 + parent: 2 + - uid: 3486 + components: + - type: Transform + pos: -20.5,66.5 + parent: 2 + - uid: 3487 + components: + - type: Transform + pos: -21.5,66.5 + parent: 2 + - uid: 3488 + components: + - type: Transform + pos: -22.5,66.5 + parent: 2 + - uid: 3489 + components: + - type: Transform + pos: -23.5,66.5 + parent: 2 + - uid: 3490 + components: + - type: Transform + pos: -24.5,66.5 + parent: 2 + - uid: 3491 + components: + - type: Transform + pos: -24.5,67.5 + parent: 2 + - uid: 3492 components: - type: Transform pos: -30.5,66.5 @@ -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,130 +30556,190 @@ entities: - type: Transform pos: -22.5,-19.5 parent: 2 - - uid: 4026 + - uid: 4040 components: - type: Transform - pos: -20.5,-43.5 + pos: -22.5,-40.5 parent: 2 - - uid: 4027 + - uid: 4041 components: - type: Transform - pos: -20.5,-44.5 + pos: -24.5,-40.5 parent: 2 - - uid: 4028 + - uid: 4042 components: - type: Transform - pos: -19.5,-39.5 + pos: -24.5,-39.5 parent: 2 - - uid: 4029 + - uid: 4043 components: - type: Transform - pos: -20.5,-45.5 + pos: -23.5,-40.5 parent: 2 - - uid: 4030 + - uid: 4045 components: - type: Transform - pos: -19.5,-44.5 + pos: -24.5,-38.5 parent: 2 - - uid: 4031 + - uid: 4047 components: - type: Transform - pos: -19.5,-45.5 + pos: -24.5,-34.5 parent: 2 - - uid: 4032 + - uid: 4048 components: - type: Transform - pos: -18.5,-43.5 + pos: -12.5,7.5 parent: 2 - - uid: 4033 + - uid: 4049 components: - type: Transform - pos: -18.5,-44.5 + pos: -12.5,6.5 parent: 2 - - uid: 4034 + - uid: 4050 components: - type: Transform - pos: -19.5,-43.5 + pos: -12.5,5.5 parent: 2 - - uid: 4035 + - uid: 4404 components: - type: Transform - pos: -18.5,-45.5 + pos: 27.5,18.5 parent: 2 - - uid: 4036 + - uid: 4946 components: - type: Transform - pos: -20.5,-39.5 + pos: -19.5,-27.5 parent: 2 - - uid: 4037 + - uid: 7172 components: - type: Transform - pos: -19.5,-42.5 + pos: -26.5,-7.5 parent: 2 - - uid: 4038 + - uid: 7173 components: - type: Transform - pos: -18.5,-39.5 + pos: -20.5,28.5 parent: 2 - - uid: 4039 + - uid: 8169 components: - type: Transform - pos: -18.5,-41.5 + pos: -13.5,-17.5 parent: 2 - - uid: 4040 + - uid: 9502 components: - type: Transform - pos: -22.5,-40.5 + pos: -10.5,-17.5 parent: 2 - - uid: 4041 + - uid: 10695 components: - type: Transform - pos: -24.5,-40.5 + pos: -13.5,-39.5 parent: 2 - - uid: 4042 + - uid: 10696 components: - type: Transform - pos: -24.5,-39.5 + pos: -14.5,-39.5 parent: 2 - - uid: 4043 + - uid: 10748 components: - type: Transform - pos: -23.5,-40.5 + pos: -15.5,-39.5 parent: 2 - - uid: 4044 + - uid: 10750 components: - type: Transform - pos: -21.5,-40.5 + pos: -14.5,-40.5 parent: 2 - - uid: 4045 + - uid: 10951 components: - type: Transform - pos: -24.5,-38.5 + pos: -13.5,-41.5 parent: 2 - - uid: 4046 + - uid: 10967 components: - type: Transform - pos: -20.5,-40.5 + pos: -14.5,-41.5 parent: 2 - - uid: 4047 + - uid: 10968 components: - type: Transform - pos: -24.5,-34.5 + pos: -15.5,-41.5 parent: 2 - - uid: 4048 + - uid: 10969 components: - type: Transform - pos: -12.5,7.5 + pos: -16.5,-41.5 parent: 2 - - uid: 4049 + - uid: 10970 components: - type: Transform - pos: -12.5,6.5 + pos: -16.5,-40.5 parent: 2 - - uid: 4050 + - uid: 10971 components: - type: Transform - pos: -12.5,5.5 + 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: @@ -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 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -19.5,-41.5 - parent: 2 - - uid: 4996 + - uid: 13550 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 + - 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: -14.5,-17.5 + parent: 2 - proto: DisposalJunctionFlipped entities: - - uid: 6596 + - uid: 3426 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-15.5 + 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,16 +52844,22 @@ entities: parent: 2 - proto: DisposalTrunk entities: - - uid: 7166 + - uid: 2722 components: - type: Transform rot: 3.141592653589793 rad - pos: 9.5,-3.5 + pos: -18.5,-15.5 parent: 2 - - uid: 7167 + - uid: 6500 components: - type: Transform - pos: -10.5,-14.5 + pos: 30.5,36.5 + parent: 2 + - uid: 7166 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 9.5,-3.5 parent: 2 - uid: 7168 components: @@ -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 + - uid: 8394 + components: + - type: Transform + pos: -14.5,-19.5 + parent: 2 + - uid: 16393 + components: + - type: Transform + pos: -18.5,-26.5 + parent: 2 - proto: DisposalYJunction entities: - - uid: 7276 + - uid: 2705 components: - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-15.5 + 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,22 +54397,22 @@ entities: fixtures: {} - proto: FaxMachineBase entities: - - uid: 7422 + - uid: 3444 components: - type: Transform - pos: -20.5,-4.5 + pos: -10.5,-23.5 parent: 2 - type: FaxMachine - name: Научный руководитель - destinationAddress: Научный руководитель - - uid: 7423 + name: Старший инженер + destinationAddress: Старший инженер + - uid: 7422 components: - type: Transform - pos: -14.5,-23.5 + pos: -20.5,-4.5 parent: 2 - type: FaxMachine - name: Старший инженер - destinationAddress: Старший инженер + 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,71 +57022,85 @@ entities: parent: 2 - proto: FirelockGlass entities: - - uid: 7760 - components: - - type: Transform - pos: -37.5,-22.5 - parent: 2 - - uid: 7761 - components: - - type: Transform - pos: -37.5,-21.5 - parent: 2 - - uid: 7762 + - uid: 98 components: - type: Transform - pos: -37.5,-20.5 + rot: 1.5707963267948966 rad + pos: 23.5,-35.5 parent: 2 - - uid: 7763 + - type: DeviceNetwork + deviceLists: + - 10044 + - uid: 3292 components: - type: Transform - pos: 5.5,7.5 + pos: -17.5,-16.5 parent: 2 - - uid: 7764 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 + - uid: 3437 components: - type: Transform - pos: 5.5,9.5 + pos: -11.5,-17.5 parent: 2 - - uid: 7765 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 + - uid: 3441 components: - type: Transform - rot: -1.5707963267948966 rad pos: -16.5,-16.5 parent: 2 - type: DeviceNetwork deviceLists: - - 14 - - 15 - - uid: 7766 + - 993 + - 9 + - uid: 3454 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-16.5 + pos: -11.5,-22.5 parent: 2 - type: DeviceNetwork deviceLists: - - 14 - - 15 - - uid: 7767 + - 993 + - 170 + - uid: 3550 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-13.5 + pos: -11.5,-19.5 parent: 2 - type: DeviceNetwork deviceLists: - - 15 + - 993 - 9 - - uid: 7768 + - uid: 7760 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-15.5 + pos: -37.5,-22.5 + parent: 2 + - uid: 7761 + components: + - type: Transform + pos: -37.5,-21.5 + parent: 2 + - uid: 7762 + components: + - type: Transform + pos: -37.5,-20.5 + parent: 2 + - uid: 7763 + components: + - type: Transform + pos: 5.5,7.5 + parent: 2 + - uid: 7764 + components: + - type: Transform + pos: 5.5,9.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 + - uid: 3096 components: - type: Transform - pos: -8.5,-32.5 + rot: 3.141592653589793 rad + pos: -7.5,-40.5 parent: 2 - proto: GasMinerOxygenStation entities: - - uid: 7842 + - uid: 651 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-32.5 + rot: 3.141592653589793 rad + pos: -10.5,-40.5 parent: 2 - proto: GasMinerPlasma entities: - - uid: 7843 + - uid: 2839 components: - type: Transform - pos: 1.5,-32.5 + rot: -1.5707963267948966 rad + pos: -14.5,-37.5 parent: 2 - - type: GasMiner - spawnAmount: 25 -- proto: GasOutletInjector +- proto: GasMinerWaterVapor entities: - - uid: 7844 + - uid: 9832 components: - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-31.5 + rot: -1.5707963267948966 rad + pos: -1.5,-40.5 parent: 2 - - uid: 7845 +- 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: 7846 + - uid: 4028 components: - type: Transform rot: 1.5707963267948966 rad - pos: -12.5,-32.5 + pos: -15.5,-37.5 parent: 2 - - uid: 7847 + - uid: 4035 components: - type: Transform rot: 1.5707963267948966 rad - pos: -9.5,-32.5 + pos: -2.5,-40.5 parent: 2 - - uid: 7848 + - uid: 7847 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-32.5 + rot: -1.5707963267948966 rad + pos: -7.5,-32.5 parent: 2 - - uid: 7849 + - uid: 9535 components: - type: Transform rot: 1.5707963267948966 rad - pos: 0.5,-32.5 + pos: -8.5,-40.5 parent: 2 -- proto: GasPassiveVent - entities: - - uid: 7850 + - uid: 9667 components: - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-31.5 + rot: -1.5707963267948966 rad + pos: 2.5,-36.5 parent: 2 - - uid: 7851 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9844 components: - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-32.5 + rot: 1.5707963267948966 rad + pos: -5.5,-40.5 parent: 2 - - uid: 7852 +- proto: GasPassiveVent + entities: + - uid: 4036 components: - type: Transform rot: 3.141592653589793 rad - pos: -12.5,-31.5 + pos: -15.5,-36.5 parent: 2 - - uid: 7853 + - uid: 7846 components: - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-32.5 + rot: 1.5707963267948966 rad + 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 + - 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: 0.5,-31.5 + pos: -5.5,-39.5 parent: 2 - - uid: 7860 + - uid: 9673 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 58.5,-8.5 + 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,623 +60558,1025 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' -- proto: GasPipeStraight - entities: - - uid: 8063 + - uid: 8206 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -25.5,18.5 + pos: -18.5,-21.5 parent: 2 - - uid: 8064 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13722 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -30.5,14.5 + pos: -51.5,30.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8065 + color: '#0000FFFF' +- proto: GasPipeStraight + entities: + - uid: 14 components: - type: Transform - pos: -7.5,-10.5 + rot: -1.5707963267948966 rad + pos: -13.5,-19.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8066 + - 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: 16.5,-21.5 + pos: -15.5,-21.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8067 + color: '#FF0000FF' + - uid: 183 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-32.5 + rot: -1.5707963267948966 rad + pos: -14.5,-17.5 parent: 2 - - uid: 8068 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 647 components: - type: Transform - pos: -38.5,38.5 + rot: -1.5707963267948966 rad + pos: -9.5,-21.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8069 + color: '#FF0000FF' + - uid: 649 components: - type: Transform rot: -1.5707963267948966 rad - pos: -39.5,36.5 + pos: -10.5,-21.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8070 + - uid: 676 components: - type: Transform rot: 3.141592653589793 rad - pos: -12.5,-30.5 + pos: -9.5,-39.5 parent: 2 - - uid: 8071 + - uid: 677 components: - type: Transform rot: 3.141592653589793 rad - pos: -9.5,-30.5 + pos: -6.5,-38.5 parent: 2 - - uid: 8072 + - uid: 996 components: - type: Transform rot: -1.5707963267948966 rad - pos: 15.5,-21.5 + pos: -14.5,-21.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8073 + color: '#FF0000FF' + - uid: 997 components: - type: Transform - rot: 3.141592653589793 rad - pos: 17.5,-20.5 + rot: -1.5707963267948966 rad + pos: -13.5,-23.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8074 + - uid: 1002 components: - type: Transform - pos: 12.5,23.5 + rot: 3.141592653589793 rad + pos: -13.5,-36.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8075 + - uid: 1003 components: - type: Transform rot: 1.5707963267948966 rad - pos: -31.5,11.5 + 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: '#0000FFFF' - - uid: 8076 + color: '#FF0000FF' + - uid: 2707 components: - type: Transform - pos: 22.5,-30.5 + rot: 3.141592653589793 rad + pos: -18.5,-20.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8077 + color: '#FF0000FF' + - uid: 2708 components: - type: Transform - pos: 22.5,-29.5 + pos: -17.5,-20.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8078 + - uid: 2709 components: - type: Transform - pos: 9.5,-3.5 + rot: 3.141592653589793 rad + pos: -8.5,-15.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8079 + - uid: 2710 components: - type: Transform - rot: 3.141592653589793 rad - pos: -16.5,-24.5 + rot: 1.5707963267948966 rad + pos: -13.5,-13.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8080 + color: '#FF0000FF' + - uid: 2835 components: - type: Transform - pos: -38.5,40.5 + rot: 1.5707963267948966 rad + pos: -10.5,-40.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8081 + - uid: 3099 components: - type: Transform rot: 3.141592653589793 rad - pos: -30.5,12.5 + pos: -9.5,-38.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8082 + - uid: 3100 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -37.5,41.5 + pos: -15.5,-35.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8083 + - uid: 3281 components: - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-23.5 + rot: -1.5707963267948966 rad + pos: -8.5,-23.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8084 + color: '#0000FFFF' + - uid: 3385 components: - type: Transform rot: -1.5707963267948966 rad - pos: -6.5,12.5 + pos: -9.5,-23.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8085 + - uid: 3386 components: - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-30.5 + rot: -1.5707963267948966 rad + pos: -11.5,-23.5 parent: 2 - - uid: 8086 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3388 components: - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-31.5 + rot: -1.5707963267948966 rad + pos: -10.5,-23.5 parent: 2 - - uid: 8087 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3406 components: - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-30.5 + pos: -5.5,-38.5 parent: 2 - - uid: 8088 + - uid: 3427 components: - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-31.5 + rot: -1.5707963267948966 rad + pos: -15.5,-17.5 parent: 2 - - uid: 8089 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3430 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -8.5,-32.5 + rot: -1.5707963267948966 rad + pos: -12.5,-19.5 parent: 2 - - uid: 8090 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3432 components: - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-24.5 + rot: -1.5707963267948966 rad + pos: -9.5,-19.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8091 + - uid: 3433 components: - type: Transform rot: 3.141592653589793 rad - pos: -18.5,-25.5 + pos: -8.5,-14.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8092 - components: - - type: Transform - pos: -16.5,-30.5 - parent: 2 - - uid: 8093 + - uid: 3434 components: - type: Transform - pos: -16.5,-31.5 + rot: 1.5707963267948966 rad + pos: -11.5,-15.5 parent: 2 - - uid: 8094 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3436 components: - type: Transform - pos: -18.5,-31.5 + rot: 1.5707963267948966 rad + pos: -14.5,-13.5 parent: 2 - - uid: 8095 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3976 components: - type: Transform - pos: -15.5,-31.5 + pos: -2.5,-38.5 parent: 2 - - uid: 8096 + - uid: 4030 components: - type: Transform - pos: -18.5,-30.5 + rot: 3.141592653589793 rad + pos: -6.5,-39.5 parent: 2 - - uid: 8097 + - uid: 4046 components: - type: Transform - pos: -15.5,-30.5 + pos: -8.5,-38.5 parent: 2 - - uid: 8098 + - uid: 4061 components: - type: Transform rot: -1.5707963267948966 rad - pos: 8.5,-29.5 + pos: -10.5,-19.5 parent: 2 - - uid: 8099 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 4062 components: - type: Transform - rot: 3.141592653589793 rad - pos: 23.5,-23.5 + pos: -17.5,-19.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8100 + - uid: 4063 components: - type: Transform - rot: 3.141592653589793 rad - pos: 23.5,-24.5 + pos: -17.5,-18.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8101 + - uid: 4947 components: - type: Transform - rot: 3.141592653589793 rad - pos: 23.5,-22.5 + rot: 1.5707963267948966 rad + pos: -11.5,-13.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8102 + color: '#FF0000FF' + - uid: 4950 components: - type: Transform - rot: 3.141592653589793 rad - pos: 23.5,-21.5 + pos: -17.5,-22.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8103 + - uid: 4951 components: - type: Transform - rot: 3.141592653589793 rad - pos: 23.5,-19.5 + rot: 1.5707963267948966 rad + pos: -6.5,-14.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8104 + color: '#FF0000FF' + - uid: 4955 components: - type: Transform rot: 3.141592653589793 rad - pos: 23.5,-18.5 + pos: -17.5,-14.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8105 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-3.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8106 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -23.5,-13.5 - parent: 2 - - uid: 8107 + - uid: 4956 components: - type: Transform rot: 1.5707963267948966 rad - pos: 18.5,-16.5 + pos: -15.5,-15.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8108 + - uid: 4995 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 17.5,-16.5 + rot: 3.141592653589793 rad + pos: -3.5,-39.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8109 + - uid: 4996 components: - type: Transform rot: 1.5707963267948966 rad - pos: 19.5,-16.5 + pos: -4.5,-40.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8110 + - uid: 6119 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 20.5,-16.5 + rot: -1.5707963267948966 rad + pos: -16.5,-19.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8111 + color: '#FF0000FF' + - uid: 6123 components: - type: Transform - pos: -37.5,38.5 + rot: 3.141592653589793 rad + pos: -18.5,-22.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8112 + - uid: 6141 components: - type: Transform - rot: 3.141592653589793 rad - pos: -16.5,-25.5 + rot: -1.5707963267948966 rad + pos: -11.5,-17.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8113 + - uid: 6437 components: - type: Transform - pos: 28.5,-20.5 + rot: 1.5707963267948966 rad + pos: -14.5,-15.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8114 + color: '#0000FFFF' + - uid: 6445 components: - type: Transform rot: 1.5707963267948966 rad - pos: 4.5,-2.5 + pos: -15.5,-13.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8115 + - uid: 6538 components: - type: Transform rot: -1.5707963267948966 rad - pos: 8.5,-27.5 - parent: 2 - - uid: 8116 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -19.5,15.5 + pos: -12.5,-17.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8117 + color: '#0000FFFF' + - uid: 6841 components: - type: Transform - pos: -37.5,39.5 + rot: -1.5707963267948966 rad + pos: -11.5,-19.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8118 + - uid: 6999 components: - type: Transform - pos: -37.5,37.5 + rot: -1.5707963267948966 rad + pos: -17.5,-21.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8119 + - uid: 7000 components: - type: Transform - pos: -38.5,39.5 + rot: 1.5707963267948966 rad + pos: -12.5,-15.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8120 + - uid: 7002 components: - type: Transform rot: -1.5707963267948966 rad - pos: -36.5,40.5 + pos: -11.5,-21.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8121 + - uid: 7003 components: - type: Transform - rot: 3.141592653589793 rad - pos: 21.5,-15.5 + rot: -1.5707963267948966 rad + pos: -13.5,-17.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8122 + - uid: 7005 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 22.5,-16.5 + rot: 3.141592653589793 rad + pos: -17.5,-13.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8123 + - uid: 7009 components: - type: Transform - pos: -13.5,-30.5 + rot: 3.141592653589793 rad + pos: -8.5,-18.5 parent: 2 - - uid: 8124 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7012 components: - type: Transform - pos: -13.5,-31.5 + rot: 1.5707963267948966 rad + pos: -16.5,-13.5 parent: 2 - - uid: 8125 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7116 components: - type: Transform rot: 3.141592653589793 rad - pos: 21.5,-13.5 + pos: -10.5,-16.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8126 + - uid: 7117 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 23.5,-15.5 + pos: -17.5,-21.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8127 + color: '#0000FFFF' + - uid: 7118 components: - type: Transform - pos: -7.5,6.5 + rot: -1.5707963267948966 rad + pos: -17.5,-19.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8128 + - uid: 7273 components: - type: Transform - pos: -25.5,13.5 + rot: 1.5707963267948966 rad + pos: -10.5,-13.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8129 + - uid: 7766 components: - type: Transform rot: 1.5707963267948966 rad - pos: 20.5,9.5 + pos: -8.5,-16.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8130 + - uid: 8063 components: - type: Transform rot: -1.5707963267948966 rad - pos: 22.5,-15.5 + pos: -25.5,18.5 + parent: 2 + - uid: 8064 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -30.5,14.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8131 + - uid: 8065 components: - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,13.5 + pos: -7.5,-10.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8132 + - uid: 8066 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 32.5,5.5 + rot: -1.5707963267948966 rad + pos: 16.5,-21.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8133 + - uid: 8068 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 34.5,7.5 + pos: -38.5,38.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8134 + color: '#0000FFFF' + - uid: 8069 components: - type: Transform rot: -1.5707963267948966 rad - pos: 21.5,-15.5 + pos: -39.5,36.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8135 + - uid: 8072 components: - type: Transform rot: -1.5707963267948966 rad - pos: 20.5,-15.5 + pos: 15.5,-21.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8136 + color: '#0000FFFF' + - uid: 8073 components: - type: Transform rot: 3.141592653589793 rad - pos: 24.5,-16.5 + pos: 17.5,-20.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8137 + color: '#0000FFFF' + - uid: 8074 components: - type: Transform - rot: 3.141592653589793 rad - pos: 19.5,-11.5 + pos: 12.5,23.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8138 + color: '#0000FFFF' + - uid: 8075 components: - type: Transform - rot: 3.141592653589793 rad - pos: 24.5,-18.5 + rot: 1.5707963267948966 rad + pos: -31.5,11.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8139 + color: '#0000FFFF' + - uid: 8076 components: - type: Transform - rot: 3.141592653589793 rad - pos: 21.5,-14.5 + pos: 22.5,-30.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8140 + - uid: 8078 components: - type: Transform - pos: 19.5,-12.5 + pos: 9.5,-3.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8141 + - uid: 8079 components: - type: Transform rot: 3.141592653589793 rad - pos: 24.5,-17.5 + pos: -16.5,-24.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8142 + color: '#0000FFFF' + - uid: 8080 components: - type: Transform - rot: 3.141592653589793 rad - pos: 24.5,-19.5 + pos: -38.5,40.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8143 + color: '#0000FFFF' + - uid: 8081 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -27.5,14.5 + rot: 3.141592653589793 rad + pos: -30.5,12.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8144 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -24.5,18.5 - parent: 2 - - uid: 8145 + color: '#0000FFFF' + - uid: 8082 components: - type: Transform - pos: -23.5,17.5 + rot: 1.5707963267948966 rad + pos: -37.5,41.5 parent: 2 - - uid: 8146 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8084 components: - type: Transform rot: -1.5707963267948966 rad - pos: -23.5,17.5 + pos: -6.5,12.5 parent: 2 - - uid: 8147 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8088 components: - type: Transform rot: 1.5707963267948966 rad - pos: -21.5,17.5 + pos: -4.5,-32.5 parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8148 + - uid: 8089 components: - type: Transform rot: 1.5707963267948966 rad - pos: -20.5,17.5 + pos: -0.5,-29.5 + parent: 2 + - uid: 8090 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-24.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8091 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-25.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8095 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-29.5 + parent: 2 + - uid: 8098 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,-29.5 + parent: 2 + - uid: 8099 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 23.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8100 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 23.5,-24.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8101 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 23.5,-22.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8102 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 23.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8103 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 23.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8104 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 23.5,-18.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8105 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-3.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8106 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -23.5,-13.5 + parent: 2 + - uid: 8107 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 18.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8108 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 17.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8109 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 19.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8110 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8111 + components: + - type: Transform + pos: -37.5,38.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8112 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-25.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8113 + components: + - type: Transform + pos: 28.5,-20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8114 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 4.5,-2.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8115 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,-27.5 + parent: 2 + - uid: 8116 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -19.5,15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8117 + components: + - type: Transform + pos: -37.5,39.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8118 + components: + - type: Transform + pos: -37.5,37.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8119 + components: + - type: Transform + pos: -38.5,39.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8120 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -36.5,40.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8121 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 21.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8122 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 22.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8123 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-32.5 + parent: 2 + - uid: 8125 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 21.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8126 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 23.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8127 + components: + - type: Transform + pos: -7.5,6.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8128 + components: + - type: Transform + pos: -25.5,13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8129 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,9.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8130 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 22.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8131 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -21.5,13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8132 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 32.5,5.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8133 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 34.5,7.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8134 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 21.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8135 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8136 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 24.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8137 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 19.5,-11.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8138 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 24.5,-18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8139 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 21.5,-14.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8140 + components: + - type: Transform + pos: 19.5,-12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8141 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 24.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8142 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 24.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8143 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -27.5,14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8144 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -24.5,18.5 + parent: 2 + - uid: 8145 + components: + - type: Transform + pos: -23.5,17.5 + parent: 2 + - uid: 8146 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -23.5,17.5 + parent: 2 + - uid: 8147 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -21.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8148 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -20.5,17.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -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,258 +61919,125 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8198 + - uid: 8201 components: - type: Transform - pos: -16.5,-22.5 + rot: 1.5707963267948966 rad + pos: -13.5,-15.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8199 + - uid: 8202 components: - type: Transform - pos: -16.5,-21.5 + rot: 1.5707963267948966 rad + pos: -17.5,-13.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8200 + color: '#FF0000FF' + - uid: 8203 components: - type: Transform - pos: -16.5,-19.5 + rot: 3.141592653589793 rad + pos: -18.5,-23.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8201 + color: '#FF0000FF' + - uid: 8204 components: - type: Transform - pos: -18.5,-18.5 + rot: 3.141592653589793 rad + pos: -8.5,-16.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8202 + - uid: 8214 components: - type: Transform - pos: -16.5,-17.5 + rot: 3.141592653589793 rad + pos: -8.5,-17.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8203 + color: '#FF0000FF' + - uid: 8217 components: - type: Transform - pos: -16.5,-16.5 + rot: 1.5707963267948966 rad + pos: -18.5,-23.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8204 + - uid: 8218 components: - type: Transform - pos: -18.5,-19.5 + rot: 1.5707963267948966 rad + pos: -12.5,-13.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8205 + - uid: 8219 components: - type: Transform - pos: -18.5,-17.5 + rot: 1.5707963267948966 rad + pos: -16.5,-15.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8206 + color: '#0000FFFF' + - uid: 8223 components: - type: Transform - pos: -18.5,-16.5 + pos: -7.5,-11.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8207 + - uid: 8224 components: - type: Transform - pos: -18.5,-14.5 + pos: -9.5,-9.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8208 + color: '#0000FFFF' + - uid: 8225 components: - type: Transform - pos: -18.5,-15.5 + pos: -9.5,-11.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8209 + color: '#0000FFFF' + - uid: 8226 components: - type: Transform - rot: 3.141592653589793 rad - pos: -17.5,-13.5 + pos: -9.5,-13.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8210 + - uid: 8228 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-13.5 + rot: -1.5707963267948966 rad + pos: -9.5,-13.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8211 + - uid: 8229 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-13.5 + pos: -7.5,-9.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8212 + - uid: 8230 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-13.5 + pos: -7.5,-12.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8213 + - uid: 8231 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 - pos: -13.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - 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 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8223 - components: - - type: Transform - pos: -7.5,-11.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8224 - components: - - type: Transform - pos: -9.5,-9.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8225 - components: - - type: Transform - pos: -9.5,-11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8226 - components: - - type: Transform - pos: -9.5,-13.5 - 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 - rot: -1.5707963267948966 rad - pos: -9.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8229 - components: - - type: Transform - pos: -7.5,-9.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8230 - components: - - type: Transform - pos: -7.5,-12.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8231 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -6.5,-16.5 + rot: -1.5707963267948966 rad + pos: -6.5,-16.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' @@ -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,661 +70848,1076 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' -- proto: GasPipeTJunction - entities: - - uid: 9422 + - uid: 9444 components: - type: Transform - rot: 3.141592653589793 rad - pos: -31.5,41.5 + pos: 22.5,-31.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9423 + - uid: 9501 components: - type: Transform rot: -1.5707963267948966 rad - pos: 17.5,-21.5 + pos: -12.5,-23.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9424 + - uid: 9503 components: - type: Transform - rot: 3.141592653589793 rad - pos: -5.5,-26.5 + rot: -1.5707963267948966 rad + pos: -14.5,-23.5 parent: 2 - - uid: 9425 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9508 components: - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-26.5 + rot: -1.5707963267948966 rad + pos: -12.5,-21.5 parent: 2 - - uid: 9426 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9583 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 9.5,19.5 + pos: 23.5,-32.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9427 + - uid: 9661 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -27.5,17.5 + pos: -11.5,-38.5 parent: 2 - - uid: 9428 + - uid: 10010 components: - type: Transform rot: -1.5707963267948966 rad - pos: -26.5,17.5 + pos: -8.5,-21.5 parent: 2 - - uid: 9429 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10011 components: - type: Transform - pos: -51.5,30.5 + rot: -1.5707963267948966 rad + pos: -7.5,-21.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9430 + color: '#FF0000FF' + - uid: 10012 components: - type: Transform - rot: 3.141592653589793 rad - pos: -33.5,32.5 + rot: -1.5707963267948966 rad + pos: -6.5,-21.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9431 + - uid: 10026 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -25.5,-15.5 + rot: -1.5707963267948966 rad + pos: 19.5,-31.5 parent: 2 - - uid: 9432 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10541 components: - type: Transform rot: -1.5707963267948966 rad - pos: -21.5,-15.5 + pos: 20.5,-31.5 parent: 2 - - uid: 9433 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 11829 components: - type: Transform - pos: -22.5,-13.5 + rot: -1.5707963267948966 rad + pos: 19.5,-29.5 parent: 2 - - uid: 9434 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 12571 components: - type: Transform - rot: 3.141592653589793 rad - pos: 21.5,-16.5 + rot: 1.5707963267948966 rad + pos: -1.5,-40.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9435 + - uid: 12572 components: - type: Transform - pos: 0.5,-3.5 + rot: 3.141592653589793 rad + pos: -13.5,-35.5 parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9436 + - uid: 12733 components: - type: Transform - pos: -0.5,-4.5 + rot: -1.5707963267948966 rad + pos: 21.5,-29.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9437 + - uid: 12734 components: - type: Transform - pos: -22.5,14.5 + rot: -1.5707963267948966 rad + pos: 18.5,-31.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9438 + - uid: 12872 components: - type: Transform - pos: -25.5,14.5 + rot: 3.141592653589793 rad + pos: -0.5,-38.5 parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9439 + - uid: 12873 components: - type: Transform rot: 3.141592653589793 rad - pos: -13.5,-26.5 + pos: -0.5,-39.5 parent: 2 - - uid: 9440 + - uid: 12927 components: - type: Transform rot: 3.141592653589793 rad - pos: -8.5,14.5 + pos: -3.5,-38.5 parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9441 + - uid: 13559 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 19.5,-15.5 + rot: -1.5707963267948966 rad + pos: -16.5,-21.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9442 + - uid: 13562 components: - type: Transform - pos: -7.5,9.5 + rot: 1.5707963267948966 rad + pos: -16.5,-30.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9443 + - uid: 13563 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,37.5 + pos: -18.5,-29.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9444 + - uid: 13566 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-31.5 + rot: -1.5707963267948966 rad + pos: 18.5,-29.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9445 + - uid: 13581 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 24.5,-26.5 + pos: -18.5,-28.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9446 + - uid: 13594 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 23.5,-20.5 + rot: 3.141592653589793 rad + pos: -16.5,-26.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9447 + - uid: 13623 components: - type: Transform rot: -1.5707963267948966 rad - pos: 21.5,-12.5 + pos: 21.5,-31.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9448 + color: '#FF0000FF' + - uid: 13655 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 21.5,-11.5 + rot: 3.141592653589793 rad + pos: -51.5,31.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9449 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 19.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9450 + - uid: 13714 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 19.5,-10.5 + rot: 3.141592653589793 rad + pos: -51.5,32.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9451 + color: '#0000FFFF' + - uid: 13715 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 23.5,-25.5 + rot: 3.141592653589793 rad + pos: -51.5,33.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9452 + - uid: 13716 components: - type: Transform - pos: 49.5,-7.5 + rot: 3.141592653589793 rad + pos: -53.5,33.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9453 + - uid: 13717 components: - type: Transform rot: 3.141592653589793 rad - pos: 15.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9454 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 48.5,-8.5 + pos: -53.5,32.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9455 + color: '#FF0000FF' + - uid: 13718 components: - type: Transform rot: 3.141592653589793 rad - pos: 31.5,5.5 + pos: -53.5,31.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9456 + color: '#FF0000FF' + - uid: 13719 components: - type: Transform - pos: 33.5,7.5 + rot: 3.141592653589793 rad + pos: -53.5,30.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9457 + - uid: 13720 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 19.5,-2.5 + rot: 3.141592653589793 rad + pos: -53.5,29.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9458 + - uid: 13723 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 21.5,-4.5 + rot: 1.5707963267948966 rad + pos: -50.5,30.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9459 + - uid: 13755 components: - type: Transform - pos: -21.5,14.5 + rot: 1.5707963267948966 rad + pos: -52.5,28.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9460 - components: - - type: Transform - pos: -24.5,17.5 - parent: 2 - - uid: 9461 + - uid: 14018 components: - type: Transform rot: 3.141592653589793 rad - pos: -23.5,16.5 + pos: -16.5,-27.5 parent: 2 - - uid: 9462 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14019 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -20.5,16.5 + rot: 3.141592653589793 rad + pos: -16.5,-28.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9463 + - uid: 14086 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 12.5,-15.5 + pos: -14.5,-32.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9464 + - uid: 14087 components: - type: Transform - rot: 3.141592653589793 rad - pos: 13.5,-16.5 + pos: -14.5,-31.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9465 + color: '#FF0000FF' + - uid: 14088 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 26.5,2.5 + pos: -12.5,-35.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9466 + color: '#FF0000FF' + - uid: 14130 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 24.5,5.5 + pos: -12.5,-34.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9467 + - uid: 14157 components: - type: Transform rot: -1.5707963267948966 rad - pos: 21.5,7.5 + pos: -10.5,-36.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9468 + color: '#FF0000FF' + - uid: 14171 components: - type: Transform rot: -1.5707963267948966 rad - pos: 13.5,-6.5 + pos: -11.5,-36.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9469 + color: '#FF0000FF' + - uid: 14205 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 19.5,4.5 + rot: -1.5707963267948966 rad + pos: -8.5,-36.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9470 + - uid: 14210 components: - type: Transform rot: -1.5707963267948966 rad - pos: 19.5,7.5 + pos: -7.5,-36.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9471 + - uid: 14264 components: - type: Transform rot: -1.5707963267948966 rad - pos: -7.5,3.5 + pos: -5.5,-36.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9472 + - uid: 14268 components: - type: Transform - rot: 3.141592653589793 rad - pos: -52.5,28.5 + rot: -1.5707963267948966 rad + pos: -4.5,-36.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9473 + - uid: 14269 components: - type: Transform rot: -1.5707963267948966 rad - pos: 19.5,-14.5 + pos: -2.5,-36.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9474 + - uid: 14270 components: - type: Transform - pos: -19.5,13.5 + rot: -1.5707963267948966 rad + pos: -1.5,-36.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9475 + color: '#FF0000FF' + - uid: 14283 components: - type: Transform rot: -1.5707963267948966 rad - pos: -1.5,15.5 + pos: 1.5,-36.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9476 + - uid: 14284 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,20.5 + rot: 1.5707963267948966 rad + pos: -17.5,-26.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9477 + - uid: 14289 components: - type: Transform rot: 1.5707963267948966 rad - pos: -1.5,19.5 + pos: -16.5,-26.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9478 + - uid: 14293 components: - type: Transform rot: 1.5707963267948966 rad - pos: -1.5,26.5 + pos: -15.5,-26.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9479 + - uid: 14294 components: - type: Transform rot: 1.5707963267948966 rad - pos: -3.5,18.5 + pos: -17.5,-27.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9480 + color: '#FF0000FF' + - uid: 14309 components: - type: Transform rot: 1.5707963267948966 rad - pos: -3.5,22.5 + pos: -16.5,-27.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9481 + color: '#FF0000FF' + - uid: 14310 components: - type: Transform rot: 1.5707963267948966 rad - pos: -3.5,24.5 + pos: -15.5,-27.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9482 + color: '#FF0000FF' + - uid: 14433 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,26.5 + rot: -1.5707963267948966 rad + pos: 20.5,-29.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9483 + - uid: 16388 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,28.5 + rot: 3.141592653589793 rad + pos: -22.5,-36.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9484 + - uid: 16389 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,37.5 + rot: -1.5707963267948966 rad + pos: -22.5,-36.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9485 +- proto: GasPipeTJunction + entities: + - uid: 2711 components: - type: Transform rot: 3.141592653589793 rad - pos: -9.5,31.5 + pos: -14.5,-19.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9486 + - uid: 3424 components: - type: Transform - pos: -7.5,33.5 + rot: 3.141592653589793 rad + pos: -18.5,-13.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9487 + color: '#FF0000FF' + - uid: 3440 components: - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,31.5 + pos: -16.5,-17.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9488 + color: '#0000FFFF' + - uid: 4328 components: - type: Transform - pos: -13.5,33.5 + rot: 3.141592653589793 rad + pos: -17.5,-23.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9489 + - uid: 8077 components: - type: Transform - pos: -23.5,34.5 + rot: -1.5707963267948966 rad + pos: 22.5,-29.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9490 + - uid: 8189 components: - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,32.5 + rot: -1.5707963267948966 rad + pos: 23.5,-31.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9491 + - uid: 8205 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-21.5 + pos: -10.5,-15.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9492 + color: '#0000FFFF' + - uid: 8213 components: - type: Transform - pos: -32.5,34.5 + rot: 3.141592653589793 rad + pos: -15.5,-23.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9493 + - uid: 8215 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 21.5,-7.5 + pos: -16.5,-23.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9494 + - uid: 8216 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -29.5,38.5 + pos: -8.5,-13.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9495 + - uid: 9422 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -27.5,36.5 + rot: 3.141592653589793 rad + pos: -31.5,41.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9496 + - uid: 9423 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -29.5,29.5 + rot: -1.5707963267948966 rad + pos: 17.5,-21.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9497 + color: '#0000FFFF' + - uid: 9426 components: - type: Transform rot: 1.5707963267948966 rad - pos: -29.5,30.5 + pos: 9.5,19.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9498 + - uid: 9427 components: - type: Transform - pos: -35.5,34.5 + rot: 1.5707963267948966 rad + pos: -27.5,17.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9499 + - uid: 9428 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -38.5,23.5 + rot: -1.5707963267948966 rad + pos: -26.5,17.5 + parent: 2 + - uid: 9430 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -33.5,32.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9431 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -25.5,-15.5 + parent: 2 + - uid: 9432 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-15.5 + parent: 2 + - uid: 9433 + components: + - type: Transform + pos: -22.5,-13.5 + parent: 2 + - uid: 9434 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 21.5,-16.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9500 + - uid: 9435 components: - type: Transform - pos: -8.5,-16.5 + pos: 0.5,-3.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9436 + components: + - type: Transform + pos: -0.5,-4.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9501 + - uid: 9437 + components: + - type: Transform + pos: -22.5,14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9438 + components: + - type: Transform + pos: -25.5,14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9440 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9441 components: - type: Transform rot: 1.5707963267948966 rad - pos: -16.5,-20.5 + pos: 19.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9442 + components: + - type: Transform + pos: -7.5,9.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9502 + - uid: 9443 components: - type: Transform rot: 1.5707963267948966 rad - pos: -18.5,-22.5 + pos: -1.5,37.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9503 + - uid: 9445 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 24.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9446 components: - type: Transform rot: 1.5707963267948966 rad - pos: -18.5,-20.5 + pos: 23.5,-20.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9447 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 21.5,-12.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9448 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,-11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9449 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 19.5,-13.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9504 + - uid: 9450 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 19.5,-10.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9451 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 23.5,-25.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9452 + components: + - type: Transform + pos: 49.5,-7.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9453 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 15.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9454 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 48.5,-8.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9455 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 31.5,5.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9456 + components: + - type: Transform + pos: 33.5,7.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9457 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 19.5,-2.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9458 components: - type: Transform rot: -1.5707963267948966 rad - pos: -16.5,-23.5 + pos: 21.5,-4.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9505 + - uid: 9459 + components: + - type: Transform + pos: -21.5,14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9460 + components: + - type: Transform + pos: -24.5,17.5 + parent: 2 + - uid: 9461 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -23.5,16.5 + parent: 2 + - uid: 9462 components: - type: Transform rot: -1.5707963267948966 rad - pos: -16.5,-18.5 + pos: -20.5,16.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9506 + - uid: 9463 components: - type: Transform - pos: -16.5,-15.5 + rot: 1.5707963267948966 rad + pos: 12.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9464 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 13.5,-16.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9507 + - uid: 9465 components: - type: Transform - pos: -16.5,-13.5 + rot: -1.5707963267948966 rad + pos: 26.5,2.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9466 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 24.5,5.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9508 + - uid: 9467 components: - type: Transform rot: -1.5707963267948966 rad - pos: -17.5,-14.5 + pos: 21.5,7.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9468 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 13.5,-6.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9469 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 19.5,4.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9470 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,7.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9471 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,3.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9473 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,-14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9474 + components: + - type: Transform + pos: -19.5,13.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9475 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9476 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9477 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9478 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9479 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9480 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9481 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,24.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9482 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,26.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9483 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,28.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9484 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,37.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9485 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9486 + components: + - type: Transform + pos: -7.5,33.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9487 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9488 + components: + - type: Transform + pos: -13.5,33.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9489 + components: + - type: Transform + pos: -23.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9490 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -21.5,32.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9492 + components: + - type: Transform + pos: -32.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9493 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,-7.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9494 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -29.5,38.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9495 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -27.5,36.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9496 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -29.5,29.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9497 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -29.5,30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9498 + components: + - type: Transform + pos: -35.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9499 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -38.5,23.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' @@ -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' -- proto: GasPort - entities: - - uid: 9660 + - uid: 13591 components: - type: Transform - pos: -5.5,-25.5 + rot: 1.5707963267948966 rad + pos: -18.5,-26.5 parent: 2 - - uid: 9661 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13592 components: - type: Transform - pos: -4.5,-25.5 + 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: 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 + - uid: 5 components: - type: Transform - pos: 2.5,-29.5 + rot: 3.141592653589793 rad + pos: -15.5,-34.5 parent: 2 - - uid: 9666 + - uid: 234 components: - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-29.5 + pos: -9.5,-37.5 parent: 2 - - uid: 9667 + - uid: 3977 components: - type: Transform - pos: -13.5,-29.5 + pos: -13.5,-34.5 parent: 2 - - uid: 9668 + - uid: 4026 components: - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-29.5 + pos: -6.5,-37.5 parent: 2 - - uid: 9669 + - uid: 6038 components: - type: Transform rot: 3.141592653589793 rad - pos: -15.5,-29.5 + pos: -5.5,-37.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9670 + - uid: 6429 components: - type: Transform - pos: -7.5,-29.5 + rot: 3.141592653589793 rad + pos: -2.5,-37.5 parent: 2 - - uid: 9671 + - uid: 7406 components: - type: Transform - pos: -10.5,-29.5 + rot: 3.141592653589793 rad + pos: -8.5,-37.5 parent: 2 - - uid: 9672 + - uid: 7953 components: - type: Transform - pos: -16.5,-29.5 + pos: -3.5,-37.5 parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9673 + - uid: 9670 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 + - uid: 9992 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-37.5 + parent: 2 + - uid: 12552 components: - type: Transform rot: -1.5707963267948966 rad - pos: -11.5,-26.5 + pos: -15.5,-29.5 parent: 2 - - uid: 9680 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 12553 components: - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-29.5 + 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,37 +74595,102 @@ 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: 9821 + - 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: -32.5,13.5 + pos: 17.5,-31.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9822 + - type: DeviceNetwork + deviceLists: + - 1509 + - uid: 3291 components: - type: Transform - pos: 15.5,-19.5 + rot: 3.141592653589793 rad + pos: -16.5,-18.5 parent: 2 - type: DeviceNetwork deviceLists: - - 66 + - 993 - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9823 + color: '#0000FFFF' + - uid: 3443 components: - type: Transform - rot: -1.5707963267948966 rad + rot: 3.141592653589793 rad pos: -13.5,-22.5 parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9821 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -32.5,13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9822 + components: + - type: Transform + pos: 15.5,-19.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 66 - type: AtmosPipeColor color: '#FF0000FF' - uid: 9824 @@ -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 +- proto: Grille entities: - - uid: 9973 + - uid: 99 components: - type: Transform - pos: -10.396269,-22.727524 + rot: 1.5707963267948966 rad + pos: 14.5,-29.5 parent: 2 - - uid: 9974 + - uid: 419 components: - type: Transform - pos: -10.599394,-22.555649 + 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 -- proto: Grille - entities: - 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 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-33.5 - parent: 2 - - uid: 10692 + - uid: 12732 components: - type: Transform rot: -1.5707963267948966 rad - pos: -1.5,-33.5 + pos: -51.5,35.5 parent: 2 - - uid: 10693 + - uid: 12735 components: - type: Transform - pos: -19.5,-34.5 + pos: -54.5,39.5 parent: 2 - - uid: 10694 + - uid: 14281 components: - type: Transform - pos: -19.5,-35.5 + rot: -1.5707963267948966 rad + pos: 1.5,-36.5 parent: 2 - - uid: 10695 + - uid: 15322 components: - type: Transform - pos: -19.5,-37.5 + pos: -57.5,36.5 parent: 2 - - uid: 10696 + - uid: 15604 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 - components: - - type: Transform - pos: -14.5,-44.5 - parent: 2 - - uid: 11153 - components: - - type: Transform - pos: -14.5,-42.5 - parent: 2 - - uid: 11154 + - uid: 11310 components: - type: Transform - pos: -14.5,-43.5 + pos: -18.5,-46.5 parent: 2 - - uid: 11155 + - uid: 11337 components: - type: Transform - pos: -14.5,-40.5 + pos: -18.5,-48.5 parent: 2 - - uid: 11156 + - uid: 13653 components: - type: Transform - pos: -14.5,-38.5 + pos: -8.5,-44.5 parent: 2 - - uid: 11157 + - uid: 14230 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: 11173 + - uid: 193 components: - type: Transform - pos: -35.5,-25.5 + pos: -52.5,33.5 parent: 2 - - uid: 11174 + - uid: 11173 components: - type: Transform - pos: -7.5,-20.5 + pos: -35.5,-25.5 parent: 2 - - uid: 11175 + - uid: 13006 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,47 +83718,112 @@ entities: fixtures: {} - proto: LockableButtonAtmospherics entities: - - uid: 11303 + - uid: 2853 components: - type: Transform - pos: -17.5,-24.5 + rot: 3.141592653589793 rad + pos: -9.5,-38.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 983: + 2851: - - Pressed - Toggle - 982: + - 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: 11304 + - 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: MetaData - desc: Эта кнопка открывает вход в цистерну и закрывает подачу газа. Не забудьте опустошить цистерну. - type: Transform rot: 3.141592653589793 rad - pos: -10.5,-30.5 + pos: -0.5,-38.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 968: + 685: - - Pressed - Toggle - type: Fixtures fixtures: {} - - uid: 11305 + - 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: MetaData - desc: Эта кнопка открывает вход в цистерну и закрывает подачу газа. Не забудьте опустошить цистерну. - type: Transform rot: 3.141592653589793 rad - pos: -7.5,-30.5 + pos: -3.5,-38.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 967: + 2836: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 11303 + components: + - type: Transform + pos: -17.5,-24.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 983: + - - Pressed + - Toggle + 982: - - Pressed - Toggle - type: Fixtures @@ -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 - components: - - type: Transform - pos: -6.5,-21.5 - parent: 2 - - uid: 11365 + - uid: 15319 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,40 +89616,63 @@ entities: parent: 16911 - proto: PoweredSmallLight entities: - - uid: 11927 + - uid: 760 components: - type: Transform - rot: 3.141592653589793 rad - pos: -14.5,50.5 + rot: -1.5707963267948966 rad + pos: -1.5,-40.5 parent: 2 - - uid: 11928 + - uid: 763 components: - type: Transform rot: -1.5707963267948966 rad - pos: 24.5,28.5 + pos: -10.5,-40.5 parent: 2 - - uid: 11929 + - 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: -11.5,-31.5 + pos: -7.5,-40.5 parent: 2 - - uid: 11930 + - uid: 2856 components: - type: Transform rot: -1.5707963267948966 rad - pos: -8.5,-31.5 + pos: -4.5,-40.5 parent: 2 - - uid: 11931 + - uid: 7765 components: - type: Transform - pos: -41.5,37.5 + pos: -51.5,32.5 parent: 2 - - uid: 11932 + - uid: 9701 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-18.5 + parent: 2 + - uid: 11927 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,50.5 + parent: 2 + - uid: 11928 components: - type: Transform rot: -1.5707963267948966 rad - pos: -5.5,-31.5 + pos: 24.5,28.5 + parent: 2 + - uid: 11931 + components: + - type: Transform + pos: -41.5,37.5 parent: 2 - uid: 11933 components: @@ -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,31 +90707,27 @@ entities: parent: 16911 - proto: Rack entities: - - uid: 12032 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -47.5,35.5 - parent: 2 - - uid: 12033 + - uid: 650 components: - type: Transform - pos: -21.5,6.5 + pos: -7.5,-21.5 parent: 2 - - uid: 12034 + - uid: 8393 components: - type: Transform - pos: -14.5,-19.5 + rot: 3.141592653589793 rad + pos: -15.5,-17.5 parent: 2 - - uid: 12035 + - uid: 12032 components: - type: Transform - pos: -10.5,-25.5 + rot: -1.5707963267948966 rad + pos: -47.5,35.5 parent: 2 - - uid: 12036 + - uid: 12033 components: - type: Transform - pos: -11.5,-25.5 + pos: -21.5,6.5 parent: 2 - uid: 12037 components: @@ -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 -- proto: RadiationCollectorFullTank - entities: - - uid: 12085 + - uid: 12204 components: - type: Transform - pos: -20.5,-45.5 + pos: -4.5,-25.5 parent: 2 - - uid: 12086 + - uid: 13287 components: - type: Transform - pos: -18.5,-44.5 + rot: 3.141592653589793 rad + pos: -14.5,-17.5 parent: 2 - - uid: 12087 + - uid: 16529 components: - type: Transform - pos: -20.5,-44.5 + rot: 1.5707963267948966 rad + pos: 15.5,-29.5 parent: 2 - - uid: 12088 + - uid: 16530 components: - type: Transform - pos: -18.5,-45.5 + rot: 1.5707963267948966 rad + pos: 15.5,-31.5 parent: 2 - - uid: 12089 +- proto: RadiationCollectorFullTank + entities: + - uid: 10616 components: - type: Transform - pos: -18.5,-43.5 + pos: -13.5,-41.5 parent: 2 - - uid: 12090 + - uid: 10640 components: - type: Transform - pos: -20.5,-43.5 + pos: -14.5,-41.5 + parent: 2 + - uid: 10693 + components: + - type: Transform + pos: -14.5,-39.5 + parent: 2 + - uid: 10694 + components: + - type: Transform + pos: -15.5,-39.5 parent: 2 - proto: RadioHandheld entities: @@ -90335,23 +91040,39 @@ entities: parent: 2 - proto: Railing entities: - - uid: 12094 + - uid: 4327 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 24.5,26.5 + pos: -4.5,-16.5 parent: 2 - - uid: 12095 + - uid: 8392 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-29.5 + rot: 1.5707963267948966 rad + pos: -8.5,-19.5 parent: 2 - - uid: 12096 + - uid: 9472 + components: + - type: Transform + pos: -7.5,-16.5 + parent: 2 + - uid: 9505 components: - type: Transform rot: 1.5707963267948966 rad - pos: -0.5,-29.5 + 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: 12097 components: @@ -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 + - uid: 437 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,-36.5 + parent: 2 + - uid: 2827 components: - type: Transform rot: 3.141592653589793 rad - pos: -0.5,-28.5 + pos: -6.5,-36.5 parent: 2 - - uid: 12189 + - 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: 0.5,-28.5 + 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,144 +94375,219 @@ entities: parent: 19351 - proto: ReinforcedPlasmaWindow entities: - - uid: 12413 + - uid: 2772 components: - type: Transform - rot: 3.141592653589793 rad - pos: -24.5,-15.5 + rot: -1.5707963267948966 rad + pos: -7.5,-41.5 parent: 2 - - uid: 12414 + - uid: 2815 components: - type: Transform - rot: 3.141592653589793 rad - pos: -26.5,-15.5 + rot: -1.5707963267948966 rad + pos: -7.5,-31.5 parent: 2 - - uid: 12415 + - uid: 2816 components: - type: Transform - rot: 3.141592653589793 rad - pos: -22.5,-15.5 + rot: -1.5707963267948966 rad + pos: -6.5,-31.5 parent: 2 - - uid: 12416 + - uid: 2819 components: - type: Transform - rot: 3.141592653589793 rad - pos: -20.5,-15.5 + rot: -1.5707963267948966 rad + pos: -7.5,-38.5 parent: 2 - - uid: 12417 + - uid: 2821 components: - type: Transform - pos: -5.5,-24.5 + rot: -1.5707963267948966 rad + pos: -8.5,-41.5 parent: 2 - - uid: 12418 + - uid: 2829 components: - type: Transform - pos: -9.5,-24.5 + rot: -1.5707963267948966 rad + pos: -5.5,-31.5 parent: 2 - - uid: 12419 + - uid: 2830 components: - type: Transform - pos: -5.5,-20.5 + rot: -1.5707963267948966 rad + pos: -4.5,-32.5 parent: 2 - - uid: 12420 + - uid: 7848 components: - type: Transform - pos: -9.5,-20.5 + rot: -1.5707963267948966 rad + pos: -10.5,-38.5 parent: 2 - - uid: 12421 + - uid: 7849 components: - type: Transform - pos: -4.5,-20.5 + rot: -1.5707963267948966 rad + pos: -13.5,-37.5 parent: 2 - - uid: 12422 + - uid: 7859 components: - type: Transform - pos: -10.5,-20.5 + rot: -1.5707963267948966 rad + pos: -10.5,-41.5 parent: 2 - - uid: 12423 + - uid: 7875 components: - type: Transform - pos: -7.5,-24.5 + rot: -1.5707963267948966 rad + pos: -5.5,-41.5 parent: 2 - - uid: 12473 + - uid: 7877 components: - type: Transform rot: -1.5707963267948966 rad - pos: 1.5,-33.5 + pos: -1.5,-41.5 parent: 2 - - uid: 12474 + - uid: 8067 components: - type: Transform rot: -1.5707963267948966 rad - pos: 0.5,-33.5 + pos: -1.5,-38.5 parent: 2 - - uid: 12475 + - uid: 9398 components: - type: Transform rot: -1.5707963267948966 rad - pos: -14.5,-30.5 + pos: -14.5,-35.5 parent: 2 - - uid: 12476 + - uid: 9424 components: - type: Transform rot: -1.5707963267948966 rad - pos: -12.5,-33.5 + pos: -7.5,-33.5 parent: 2 - - uid: 12477 + - uid: 9664 components: - type: Transform rot: -1.5707963267948966 rad - pos: -11.5,-33.5 + pos: -6.5,-33.5 parent: 2 - - uid: 12478 + - uid: 9665 components: - type: Transform rot: -1.5707963267948966 rad - pos: -11.5,-30.5 + pos: -11.5,-41.5 parent: 2 - - uid: 12480 + - uid: 9666 components: - type: Transform rot: -1.5707963267948966 rad - pos: -9.5,-33.5 + pos: -5.5,-33.5 parent: 2 - - uid: 12481 + - uid: 9993 components: - type: Transform rot: -1.5707963267948966 rad - pos: -8.5,-33.5 + pos: -2.5,-41.5 parent: 2 - - uid: 12571 + - uid: 10002 components: - type: Transform rot: -1.5707963267948966 rad - pos: -8.5,-30.5 + pos: -4.5,-41.5 parent: 2 - - uid: 12572 + - uid: 10006 components: - type: Transform rot: -1.5707963267948966 rad - pos: -17.5,-30.5 + pos: -4.5,-38.5 parent: 2 - - uid: 12755 + - uid: 10048 components: - type: Transform rot: -1.5707963267948966 rad - pos: 1.5,-30.5 + pos: -13.5,-36.5 parent: 2 - - uid: 12775 + - uid: 12413 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-30.5 + rot: 3.141592653589793 rad + pos: -24.5,-15.5 parent: 2 -- proto: ReinforcedWindow + - uid: 12414 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -26.5,-15.5 + parent: 2 + - uid: 12415 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -22.5,-15.5 + parent: 2 + - uid: 12416 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -20.5,-15.5 + parent: 2 +- 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,30 +97869,30 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage - - uid: 12869 + - uid: 12205 components: - type: Transform - pos: -26.50581,-36.224377 + pos: -4.5,-25.5 parent: 2 - - uid: 12870 + - uid: 12206 components: - type: Transform - pos: -26.521435,-36.615 + pos: -4.5,-25.5 parent: 2 - - uid: 12871 + - uid: 12869 components: - type: Transform - pos: 3.3614388,14.621397 + pos: -26.50581,-36.224377 parent: 2 - - uid: 12872 + - uid: 12870 components: - type: Transform - pos: -10.5,-25.5 + pos: -26.521435,-36.615 parent: 2 - - uid: 12873 + - uid: 12871 components: - type: Transform - pos: -11.5,-25.5 + pos: 3.3614388,14.621397 parent: 2 - uid: 12874 components: @@ -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 + 3557: - - Pressed - Toggle - 966: + 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: SignEngineering + entities: + - uid: 8207 + components: + - type: Transform + 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: 13006 + - uid: 19363 components: - type: Transform - pos: -51.5,31.5 + 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,19 +99753,18 @@ entities: fixtures: {} - proto: SignVault entities: - - uid: 13056 + - uid: 194 components: - type: Transform - rot: 3.141592653589793 rad - pos: -26.5,-41.5 + pos: -51.5,31.5 parent: 2 - type: Fixtures fixtures: {} - - uid: 13057 + - uid: 13056 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-17.5 + rot: 3.141592653589793 rad + pos: -26.5,-41.5 parent: 2 - type: Fixtures fixtures: {} @@ -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,62 +99938,67 @@ 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: 13079 + - uid: 4424 components: - type: Transform - pos: -7.5,48.5 + pos: 26.5,18.5 parent: 2 - - uid: 13080 + - uid: 13073 components: - type: Transform - pos: -20.5,28.5 + pos: -33.5,-20.5 parent: 2 - - uid: 13081 + - uid: 13079 components: - type: Transform - pos: 27.5,18.5 + pos: -7.5,48.5 parent: 2 - - uid: 13082 + - uid: 13081 components: - type: Transform - pos: 20.5,-23.5 + pos: -50.5,44.5 parent: 2 - - uid: 13083 + - uid: 13082 components: - type: Transform - pos: 32.5,-15.5 + pos: 20.5,-23.5 parent: 2 - uid: 13084 components: @@ -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,35 +101860,35 @@ 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: -21.5,-36.5 + pos: -17.5,-20.5 parent: 2 - - uid: 13410 + - uid: 10067 components: - type: Transform - pos: -21.5,-37.5 + pos: -19.5,-36.5 parent: 2 - - uid: 13411 + - uid: 10083 components: - type: Transform - pos: -25.5,-36.5 + pos: -18.5,-36.5 parent: 2 - - uid: 13412 + - uid: 11652 components: - type: Transform - pos: -25.5,-38.5 + pos: -23.5,-37.5 parent: 2 - - uid: 13413 + - uid: 11653 components: - type: Transform - pos: -25.5,-37.5 + pos: -21.5,-36.5 parent: 2 - proto: SpawnPointSurgeon entities: @@ -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 -- proto: StorageCanister - entities: - - uid: 13522 + - uid: 14453 components: - type: Transform - pos: -5.5,-25.5 + rot: -1.5707963267948966 rad + pos: -17.5,-33.5 parent: 2 - - uid: 13523 + - uid: 14470 components: - type: Transform - pos: -4.5,-25.5 + rot: -1.5707963267948966 rad + pos: -17.5,-32.5 parent: 2 - - uid: 13524 +- proto: StorageCanister + entities: + - uid: 970 components: - type: Transform - pos: -6.5,-25.5 + pos: -7.5,-26.5 parent: 2 - - uid: 13525 + - uid: 9680 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 +- proto: SuitStorageEVA entities: - - uid: 13561 + - uid: 12526 components: - type: Transform - pos: -23.5,-37.5 + pos: 19.5,-30.5 parent: 2 - - uid: 13562 + - uid: 16527 components: - type: Transform - pos: -23.5,-35.5 + pos: 19.5,-28.5 parent: 2 - - uid: 13563 + - uid: 16528 components: - type: Transform - pos: -23.5,-36.5 + pos: 17.5,-28.5 parent: 2 -- proto: SuitStorageEVA - entities: - - uid: 13564 + - uid: 16531 components: - type: Transform - pos: -51.5,35.5 + pos: 18.5,-28.5 parent: 2 - - uid: 13565 + - uid: 16532 components: - type: Transform - pos: -50.5,35.5 + pos: 19.5,-32.5 parent: 2 - - uid: 13566 + - uid: 16664 components: - type: Transform - pos: -52.5,35.5 + pos: 18.5,-32.5 parent: 2 - - uid: 13567 + - uid: 16666 components: - type: Transform - pos: -53.5,35.5 + 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,90 +103241,64 @@ entities: id: Комната игр - proto: SurveillanceCameraEngineering entities: - - uid: 13587 + - uid: 187 components: - type: Transform rot: 1.5707963267948966 rad - pos: 1.5,-20.5 + pos: -0.5,-33.5 parent: 2 - - type: SurveillanceCamera - id: Технологическое хранилище - - uid: 13588 + - uid: 8094 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -33.5,-29.5 + rot: -1.5707963267948966 rad + pos: 1.5,-31.5 parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Генератор Гравитации - - uid: 13589 + - uid: 9712 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-20.5 + rot: 3.141592653589793 rad + pos: -13.5,-17.5 parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Общие - Вход - - uid: 13590 + - uid: 11746 components: - type: Transform - pos: -23.5,-23.5 + pos: -14.5,-23.5 parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Специальное - Двигатель антиматерии - - uid: 13591 + - uid: 12475 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-33.5 + rot: 3.141592653589793 rad + pos: -22.5,-34.5 parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Общие - Основное помещение - - uid: 13592 + - uid: 13587 components: - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-25.5 + rot: 1.5707963267948966 rad + pos: 1.5,-20.5 parent: 2 - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Атмос - Основное помещение - - uid: 13593 + id: Технологическое хранилище + - uid: 13588 components: - type: Transform - rot: 3.141592653589793 rad - pos: 1.5,-25.5 + rot: 1.5707963267948966 rad + pos: -33.5,-29.5 parent: 2 - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Атмос - ТЭГ - - uid: 13594 + id: Генератор Гравитации + - uid: 13590 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -22.5,-27.5 + pos: -23.5,-23.5 parent: 2 - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Атмос - Гардероб + 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,118 +104344,128 @@ entities: parent: 2 - proto: Table entities: - - uid: 13702 + - uid: 3445 components: - type: Transform - pos: -13.5,20.5 + pos: -10.5,-23.5 parent: 2 - - uid: 13703 + - uid: 3653 components: - type: Transform - pos: -15.5,-5.5 + pos: -8.5,-23.5 parent: 2 - - uid: 13704 + - uid: 3654 components: - type: Transform - pos: -14.5,-5.5 + pos: -9.5,-23.5 parent: 2 - - uid: 13705 + - uid: 7727 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 27.5,6.5 + pos: -53.5,34.5 parent: 2 - - uid: 13706 + - uid: 9660 components: - type: Transform - pos: 18.5,24.5 + pos: 0.5,-34.5 parent: 2 - - uid: 13707 + - uid: 10974 components: - type: Transform - pos: -10.5,-7.5 + rot: 1.5707963267948966 rad + pos: -22.5,-36.5 parent: 2 - - uid: 13708 + - uid: 10975 components: - type: Transform rot: 1.5707963267948966 rad - pos: 12.5,20.5 + pos: -22.5,-37.5 parent: 2 - - uid: 13709 + - uid: 10976 components: - type: Transform rot: 1.5707963267948966 rad - pos: 9.5,20.5 + pos: -22.5,-38.5 parent: 2 - - uid: 13710 + - uid: 12476 components: - type: Transform - pos: -13.5,15.5 + rot: 3.141592653589793 rad + pos: -0.5,-33.5 parent: 2 - - uid: 13711 + - uid: 12477 components: - type: Transform - pos: -45.5,46.5 + rot: 3.141592653589793 rad + pos: -0.5,-34.5 parent: 2 - - uid: 13712 + - uid: 13582 components: - type: Transform - pos: -26.5,-13.5 + pos: -54.5,34.5 parent: 2 - - uid: 13713 + - uid: 13702 components: - type: Transform - pos: 49.5,-6.5 + pos: -13.5,20.5 parent: 2 - - uid: 13714 + - uid: 13703 components: - type: Transform - pos: -10.5,-21.5 + pos: -15.5,-5.5 parent: 2 - - uid: 13715 + - uid: 13704 components: - type: Transform - pos: -10.5,-22.5 + pos: -14.5,-5.5 parent: 2 - - uid: 13716 + - uid: 13705 components: - type: Transform - pos: -9.5,-21.5 + rot: -1.5707963267948966 rad + pos: 27.5,6.5 parent: 2 - - uid: 13717 + - uid: 13706 components: - type: Transform - pos: -10.5,-23.5 + pos: 18.5,24.5 parent: 2 - - uid: 13718 + - uid: 13707 components: - type: Transform - pos: -5.5,-21.5 + pos: -10.5,-7.5 parent: 2 - - uid: 13719 + - uid: 13708 components: - type: Transform - pos: -4.5,-21.5 + rot: 1.5707963267948966 rad + pos: 12.5,20.5 parent: 2 - - uid: 13720 + - uid: 13709 components: - type: Transform - pos: -14.5,-23.5 + rot: 1.5707963267948966 rad + pos: 9.5,20.5 parent: 2 - - uid: 13721 + - uid: 13710 components: - type: Transform - pos: -13.5,-23.5 + pos: -13.5,15.5 parent: 2 - - uid: 13722 + - uid: 13711 components: - type: Transform - pos: -12.5,-23.5 + pos: -45.5,46.5 parent: 2 - - uid: 13723 + - uid: 13712 components: - type: Transform - pos: -14.5,-22.5 + pos: -26.5,-13.5 + parent: 2 + - uid: 13713 + components: + - type: Transform + pos: 49.5,-6.5 parent: 2 - uid: 13724 components: @@ -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 +- proto: ToolboxMechanicalFilled entities: - - uid: 14054 + - uid: 10037 components: - type: Transform - pos: -9.521576,-21.399467 + pos: -14.503511,-17.225327 parent: 2 -- proto: ToolboxMechanicalFilled - entities: - 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 @@ -106604,400 +107321,935 @@ entities: - type: Transform pos: -37.5,42.5 parent: 2 - - uid: 14123 + - uid: 14123 + components: + - type: Transform + pos: -37.5,43.5 + parent: 2 +- proto: VendingMachineClothing + entities: + - uid: 14124 + components: + - type: Transform + pos: 1.5,14.5 + parent: 2 + - uid: 14125 + components: + - type: Transform + pos: -32.5,43.5 + parent: 2 +- proto: VendingMachineCoffee + entities: + - uid: 14126 + components: + - type: Transform + pos: 7.5,42.5 + parent: 2 + - uid: 18965 + components: + - type: Transform + pos: 2.5,4.5 + parent: 16911 +- proto: VendingMachineCuraDrobe + entities: + - uid: 14127 + components: + - type: Transform + pos: 11.5,43.5 + parent: 2 +- proto: VendingMachineDetDrobe + entities: + - uid: 14128 + components: + - type: Transform + pos: 25.5,-32.5 + parent: 2 +- proto: VendingMachineDinnerware + entities: + - uid: 14129 + components: + - type: Transform + pos: 17.5,23.5 + parent: 2 +- proto: VendingMachineEngiDrobe + entities: + - uid: 10984 + components: + - type: Transform + 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 + pos: -26.5,-40.5 + parent: 2 +- proto: VendingMachineGames + entities: + - uid: 14132 + components: + - type: Transform + pos: 0.5,42.5 + parent: 2 +- proto: VendingMachineGeneDrobe + entities: + - uid: 14133 + components: + - type: Transform + pos: -12.5,23.5 + parent: 2 +- proto: VendingMachineHappyHonk + entities: + - uid: 14134 + components: + - type: Transform + pos: 0.5,11.5 + parent: 2 + - uid: 14135 + components: + - type: Transform + pos: -26.5,4.5 + parent: 2 +- proto: VendingMachineHydrobe + entities: + - uid: 14136 + components: + - type: Transform + pos: 5.5,24.5 + parent: 2 +- proto: VendingMachineJaniDrobe + entities: + - uid: 14137 + components: + - type: Transform + pos: 2.5,31.5 + parent: 2 +- proto: VendingMachineLawDrobe + entities: + - uid: 14138 + components: + - type: Transform + pos: 14.5,-23.5 + parent: 2 +- proto: VendingMachineMedical + entities: + - uid: 14139 + components: + - type: Transform + pos: -16.5,22.5 + parent: 2 + - uid: 14140 + components: + - type: Transform + pos: -22.5,12.5 + parent: 2 + - uid: 14141 + components: + - type: Transform + pos: -9.5,12.5 + parent: 2 + - uid: 14142 + components: + - type: Transform + pos: -15.5,15.5 + parent: 2 + - uid: 14143 + components: + - type: Transform + pos: -12.5,27.5 + parent: 2 +- proto: VendingMachineMediDrobe + entities: + - uid: 14144 + components: + - type: Transform + pos: -12.5,24.5 + parent: 2 +- proto: VendingMachineNutri + entities: + - uid: 14145 + components: + - type: Transform + pos: 9.5,23.5 + parent: 2 +- proto: VendingMachineRoboDrobe + entities: + - uid: 14146 + components: + - type: Transform + pos: -20.5,-10.5 + parent: 2 +- proto: VendingMachineRobotics + entities: + - uid: 14147 + components: + - type: Transform + pos: -21.5,-10.5 + parent: 2 +- proto: VendingMachineSalvage + entities: + - uid: 14148 + components: + - type: Transform + pos: 32.5,13.5 + parent: 2 +- proto: VendingMachineSciDrobe + entities: + - uid: 14149 + components: + - type: Transform + pos: -17.5,-2.5 + parent: 2 +- proto: VendingMachineSec + entities: + - uid: 14150 + components: + - type: Transform + pos: 26.5,-23.5 + parent: 2 +- proto: VendingMachineSecDrobe + entities: + - uid: 14151 + components: + - type: Transform + pos: 26.5,-22.5 + parent: 2 +- proto: VendingMachineSeeds + entities: + - uid: 14152 + components: + - type: Transform + pos: 10.5,23.5 + parent: 2 +- proto: VendingMachineSeedsUnlocked + entities: + - uid: 14153 + components: + - type: Transform + pos: 46.5,-16.5 + parent: 2 +- proto: VendingMachineSustenance + entities: + - uid: 14154 + components: + - type: Transform + pos: 46.5,-15.5 + parent: 2 + - uid: 14155 + components: + - type: Transform + pos: 34.5,-10.5 + parent: 2 +- proto: VendingMachineTankDispenserEngineering + entities: + - uid: 11352 + components: + - type: Transform + pos: -19.5,-38.5 + parent: 2 + - uid: 14156 + components: + - type: Transform + pos: -24.5,-45.5 + parent: 2 +- proto: VendingMachineTankDispenserEVA + entities: + - uid: 14158 + components: + - type: Transform + pos: 39.5,15.5 + parent: 2 + - uid: 14159 + components: + - type: Transform + pos: 33.5,-33.5 + parent: 2 + - uid: 14160 + components: + - type: Transform + pos: -28.5,68.5 + parent: 2 + - uid: 16753 + components: + - type: Transform + pos: 15.5,-30.5 + parent: 2 +- proto: VendingMachineTheater + entities: + - uid: 14162 + components: + - type: Transform + pos: 0.5,14.5 + parent: 2 + - uid: 14163 + components: + - type: Transform + pos: -31.5,43.5 + parent: 2 +- proto: VendingMachineViroDrobe + entities: + - uid: 14164 + components: + - type: Transform + pos: -12.5,22.5 + parent: 2 +- proto: VendingMachineWallMedical + entities: + - uid: 14165 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -12.5,9.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 14166 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,18.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 14167 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -18.5,16.5 + parent: 2 + - type: Fixtures + fixtures: {} +- proto: VendingMachineWinter + entities: + - uid: 14168 + components: + - type: Transform + pos: -33.5,43.5 + 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 + pos: 3.5,46.5 + parent: 2 + - uid: 14170 + components: + - type: Transform + pos: -43.5,38.5 + parent: 2 +- proto: WallmountGeneratorAPUElectronics + entities: + - uid: 14172 + components: + - type: Transform + pos: 1.3689811,-20.515684 + parent: 2 +- proto: WallmountGeneratorElectronics + entities: + - uid: 14173 + components: + - type: Transform + pos: 1.0525479,-20.325802 + parent: 2 + - uid: 14174 + components: + - type: Transform + pos: 0.8858813,-20.304968 + parent: 2 +- proto: WallmountTelescreen + entities: + - uid: 14175 + components: + - type: Transform + pos: -34.5,30.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 14176 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 14177 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 49.5,-5.5 + parent: 2 + - type: Fixtures + fixtures: {} +- proto: WallmountTelevision + entities: + - uid: 14178 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 26.5,-37.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 14179 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 4.5,-8.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 14180 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -71.5,36.5 + parent: 2 + - type: Fixtures + 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 - pos: -37.5,43.5 + rot: 3.141592653589793 rad + pos: -12.5,-16.5 parent: 2 -- proto: VendingMachineClothing - entities: - - uid: 14124 + - uid: 4020 components: - type: Transform - pos: 1.5,14.5 + rot: 3.141592653589793 rad + pos: -13.5,-16.5 parent: 2 - - uid: 14125 + - uid: 4032 components: - type: Transform - pos: -32.5,43.5 + pos: 0.5,-33.5 parent: 2 -- proto: VendingMachineCoffee - entities: - - uid: 14126 + - uid: 4033 components: - type: Transform - pos: 7.5,42.5 + pos: 0.5,-38.5 parent: 2 - - uid: 18965 + - uid: 4037 components: - type: Transform - pos: 2.5,4.5 - parent: 16911 -- proto: VendingMachineCuraDrobe - entities: - - uid: 14127 + pos: -13.5,-35.5 + parent: 2 + - uid: 4039 components: - type: Transform - pos: 11.5,43.5 + pos: -13.5,-38.5 parent: 2 -- proto: VendingMachineDetDrobe - entities: - - uid: 14128 + - uid: 4075 components: - type: Transform - pos: 25.5,-32.5 + rot: 3.141592653589793 rad + pos: -11.5,-23.5 parent: 2 -- proto: VendingMachineDinnerware - entities: - - uid: 14129 + - uid: 4321 components: - type: Transform - pos: 17.5,23.5 + rot: 3.141592653589793 rad + pos: -14.5,-20.5 parent: 2 -- proto: VendingMachineEngiDrobe - entities: - - uid: 14130 + - uid: 4324 components: - type: Transform - pos: -22.5,-38.5 + rot: 3.141592653589793 rad + pos: -12.5,-20.5 parent: 2 -- proto: VendingMachineEngivend - entities: - - uid: 14131 + - uid: 4662 components: - type: Transform - pos: -26.5,-40.5 + pos: -55.5,34.5 parent: 2 -- proto: VendingMachineGames - entities: - - uid: 14132 + - uid: 4663 components: - type: Transform - pos: 0.5,42.5 + pos: -55.5,37.5 parent: 2 -- proto: VendingMachineGeneDrobe - entities: - - uid: 14133 + - uid: 4812 components: - type: Transform - pos: -12.5,23.5 + rot: 3.141592653589793 rad + pos: -4.5,-20.5 parent: 2 -- proto: VendingMachineHappyHonk - entities: - - uid: 14134 + - uid: 5116 components: - type: Transform - pos: 0.5,11.5 + pos: -9.5,-38.5 parent: 2 - - uid: 14135 + - uid: 5336 components: - type: Transform - pos: -26.5,4.5 + pos: -4.5,-31.5 parent: 2 -- proto: VendingMachineHydrobe - entities: - - uid: 14136 + - uid: 5741 components: - type: Transform - pos: 5.5,24.5 + pos: -16.5,-34.5 parent: 2 -- proto: VendingMachineJaniDrobe - entities: - - uid: 14137 + - uid: 6037 components: - type: Transform - pos: 2.5,31.5 + pos: -12.5,-41.5 parent: 2 -- proto: VendingMachineLawDrobe - entities: - - uid: 14138 + - uid: 6447 components: - type: Transform - pos: 14.5,-23.5 + pos: -51.5,37.5 parent: 2 -- proto: VendingMachineMedical - entities: - - uid: 14139 + - uid: 7484 components: - type: Transform - pos: -16.5,22.5 + pos: -8.5,-31.5 parent: 2 - - uid: 14140 + - uid: 7850 components: - type: Transform - pos: -22.5,12.5 + pos: -6.5,-38.5 parent: 2 - - uid: 14141 + - uid: 7853 components: - type: Transform - pos: -9.5,12.5 + pos: -3.5,-41.5 parent: 2 - - uid: 14142 + - uid: 7993 components: - type: Transform - pos: -15.5,15.5 + pos: -53.5,33.5 parent: 2 - - uid: 14143 + - uid: 8257 components: - type: Transform - pos: -12.5,27.5 + rot: 3.141592653589793 rad + pos: -11.5,-21.5 parent: 2 -- proto: VendingMachineMediDrobe - entities: - - uid: 14144 + - uid: 8404 components: - type: Transform - pos: -12.5,24.5 + pos: -17.5,-34.5 parent: 2 -- proto: VendingMachineNutri - entities: - - uid: 14145 + - uid: 8405 components: - type: Transform - pos: 9.5,23.5 + pos: -19.5,-34.5 parent: 2 -- proto: VendingMachineRoboDrobe - entities: - - uid: 14146 + - uid: 8725 components: - type: Transform - pos: -20.5,-10.5 + pos: -50.5,31.5 parent: 2 -- proto: VendingMachineRobotics - entities: - - uid: 14147 + - uid: 8726 components: - type: Transform - pos: -21.5,-10.5 + pos: -55.5,36.5 parent: 2 -- proto: VendingMachineSalvage - entities: - - uid: 14148 + - uid: 8727 components: - type: Transform - pos: 32.5,13.5 + pos: -54.5,37.5 parent: 2 -- proto: VendingMachineSciDrobe - entities: - - uid: 14149 + - uid: 9125 components: - type: Transform - pos: -17.5,-2.5 + pos: -54.5,33.5 parent: 2 -- proto: VendingMachineSec - entities: - - uid: 14150 + - uid: 9297 components: - type: Transform - pos: 26.5,-23.5 + pos: -18.5,-34.5 parent: 2 -- proto: VendingMachineSecDrobe - entities: - - uid: 14151 + - uid: 9425 components: - type: Transform - pos: 26.5,-22.5 + pos: -16.5,-35.5 parent: 2 -- proto: VendingMachineSeeds - entities: - - uid: 14152 + - uid: 9439 components: - type: Transform - pos: 10.5,23.5 + pos: -6.5,-41.5 parent: 2 -- proto: VendingMachineSeedsUnlocked - entities: - - uid: 14153 + - uid: 9500 components: - type: Transform - pos: 46.5,-16.5 + rot: 3.141592653589793 rad + pos: -7.5,-20.5 parent: 2 -- proto: VendingMachineSustenance - entities: - - uid: 14154 + - uid: 9534 components: - type: Transform - pos: 46.5,-15.5 + pos: -3.5,-39.5 parent: 2 - - uid: 14155 + - uid: 9569 components: - type: Transform - pos: 34.5,-10.5 + rot: 3.141592653589793 rad + pos: -18.5,-16.5 parent: 2 -- proto: VendingMachineTankDispenserEngineering - entities: - - uid: 14156 + - uid: 9672 components: - type: Transform - pos: -24.5,-45.5 + pos: 2.5,-33.5 parent: 2 -- proto: VendingMachineTankDispenserEVA - entities: - - uid: 14157 + - uid: 9710 components: - type: Transform - pos: -23.5,-38.5 + pos: -12.5,-42.5 parent: 2 - - uid: 14158 + - uid: 9785 components: - type: Transform - pos: 39.5,15.5 + pos: -52.5,37.5 parent: 2 - - uid: 14159 + - uid: 9974 components: - type: Transform - pos: 33.5,-33.5 + rot: 3.141592653589793 rad + pos: -5.5,-20.5 parent: 2 - - uid: 14160 + - uid: 9991 components: - type: Transform - pos: -28.5,68.5 + pos: -6.5,-39.5 parent: 2 - - uid: 14161 + - uid: 9994 components: - type: Transform - pos: -51.5,32.5 + pos: -6.5,-40.5 parent: 2 -- proto: VendingMachineTheater - entities: - - uid: 14162 + - uid: 10007 components: - type: Transform - pos: 0.5,14.5 + pos: -15.5,-38.5 parent: 2 - - uid: 14163 + - uid: 10021 components: - type: Transform - pos: -31.5,43.5 + pos: -3.5,-38.5 parent: 2 -- proto: VendingMachineViroDrobe - entities: - - uid: 14164 + - uid: 10025 components: - type: Transform - pos: -12.5,22.5 + pos: -9.5,-40.5 parent: 2 -- proto: VendingMachineWallMedical - entities: - - uid: 14165 + - uid: 10053 components: - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,9.5 + pos: -12.5,-39.5 parent: 2 - - type: Fixtures - fixtures: {} - - uid: 14166 + - uid: 10054 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -7.5,18.5 + pos: -9.5,-39.5 parent: 2 - - type: Fixtures - fixtures: {} - - uid: 14167 + - uid: 10055 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,16.5 + pos: -0.5,-39.5 parent: 2 - - type: Fixtures - fixtures: {} -- proto: VendingMachineWinter - entities: - - uid: 14168 + - uid: 10056 components: - type: Transform - pos: -33.5,43.5 + pos: -16.5,-42.5 parent: 2 -- proto: VendingMachineYouTool - entities: - - uid: 14169 + - uid: 10057 components: - type: Transform - pos: 3.5,46.5 + pos: -15.5,-42.5 parent: 2 - - uid: 14170 + - uid: 10117 components: - type: Transform - pos: -43.5,38.5 + rot: 1.5707963267948966 rad + pos: 21.5,-27.5 parent: 2 - - uid: 14171 + - uid: 10542 components: - type: Transform - pos: -20.5,-38.5 + pos: -57.5,37.5 parent: 2 -- proto: WallmountGeneratorAPUElectronics - entities: - - uid: 14172 + - uid: 10544 components: - type: Transform - pos: 1.3689811,-20.515684 + pos: -57.5,33.5 parent: 2 -- proto: WallmountGeneratorElectronics - entities: - - uid: 14173 + - uid: 11364 components: - type: Transform - pos: 1.0525479,-20.325802 + rot: 3.141592653589793 rad + pos: -6.5,-20.5 parent: 2 - - uid: 14174 + - uid: 11365 components: - type: Transform - pos: 0.8858813,-20.304968 + rot: 3.141592653589793 rad + pos: -9.5,-20.5 parent: 2 -- proto: WallmountTelescreen - entities: - - uid: 14175 + - uid: 11419 components: - type: Transform - pos: -34.5,30.5 + rot: 3.141592653589793 rad + pos: -8.5,-20.5 parent: 2 - - type: Fixtures - fixtures: {} - - uid: 14176 + - uid: 11452 components: - type: Transform - pos: 2.5,-7.5 + rot: 3.141592653589793 rad + pos: -10.5,-20.5 parent: 2 - - type: Fixtures - fixtures: {} - - uid: 14177 + - uid: 12336 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 49.5,-5.5 + pos: -55.5,33.5 parent: 2 - - type: Fixtures - fixtures: {} -- proto: WallmountTelevision - entities: - - uid: 14178 + - uid: 12527 components: - type: Transform - rot: 3.141592653589793 rad - pos: 26.5,-37.5 + rot: 1.5707963267948966 rad + pos: 20.5,-28.5 parent: 2 - - type: Fixtures - fixtures: {} - - uid: 14179 + - uid: 13401 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 4.5,-8.5 + rot: 3.141592653589793 rad + pos: -19.5,-16.5 parent: 2 - - type: Fixtures - fixtures: {} - - uid: 14180 + - uid: 13416 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -71.5,36.5 + rot: 3.141592653589793 rad + pos: -19.5,-15.5 parent: 2 - - type: Fixtures - fixtures: {} -- proto: WallReinforced - entities: - 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,60 +115288,92 @@ entities: - type: Transform pos: 44.5,-19.5 parent: 2 - - uid: 15603 + - uid: 15606 components: - type: Transform - pos: -17.5,-46.5 + pos: 53.5,-3.5 parent: 2 - - uid: 15604 + - uid: 15607 components: - type: Transform - pos: -18.5,-46.5 + pos: 53.5,-2.5 parent: 2 - - uid: 15605 + - uid: 15608 components: - type: Transform - pos: -20.5,-46.5 + pos: 53.5,-1.5 parent: 2 - - uid: 15606 + - uid: 16394 components: - type: Transform - pos: 53.5,-3.5 + rot: 1.5707963267948966 rad + pos: 15.5,-27.5 parent: 2 - - uid: 15607 + - uid: 16395 components: - type: Transform - pos: 53.5,-2.5 + rot: 1.5707963267948966 rad + pos: 14.5,-27.5 parent: 2 - - uid: 15608 + - uid: 16396 components: - type: Transform - pos: 53.5,-1.5 + rot: 1.5707963267948966 rad + pos: 14.5,-28.5 parent: 2 - - uid: 15609 + - uid: 16397 components: - type: Transform - pos: -17.5,-41.5 + rot: 1.5707963267948966 rad + pos: 17.5,-27.5 parent: 2 - - uid: 15610 + - uid: 16398 components: - type: Transform - pos: -17.5,-39.5 + rot: 1.5707963267948966 rad + pos: 19.5,-27.5 parent: 2 - - uid: 15611 + - uid: 16399 components: - type: Transform - pos: -18.5,-39.5 + rot: 1.5707963267948966 rad + pos: 20.5,-27.5 parent: 2 - - uid: 15612 + - uid: 16400 components: - type: Transform - pos: -17.5,-40.5 + rot: 1.5707963267948966 rad + pos: 20.5,-33.5 parent: 2 - - uid: 15613 + - uid: 16401 components: - type: Transform - pos: -20.5,-39.5 + 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 - components: - - type: Transform - pos: 24.5,-42.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 + - uid: 8086 components: - type: Transform - pos: 36.5,-5.5 + rot: 3.141592653589793 rad + pos: -3.5,-38.5 parent: 2 - - type: WarpPoint - location: Офис КМа - - uid: 16402 + - type: Fixtures + fixtures: {} +- proto: WaterCooler + entities: + - uid: 12203 components: - type: Transform - pos: 42.5,6.5 + pos: -14.5,-25.5 parent: 2 - - type: WarpPoint - location: Стыковочный док снабжения -- proto: WaterCooler - entities: - 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,45 +123035,63 @@ entities: parent: 2 - proto: WindowReinforcedDirectional entities: - - uid: 16661 + - uid: 9669 components: - type: Transform - pos: 34.5,-12.5 + rot: -1.5707963267948966 rad + pos: -13.5,-27.5 parent: 2 - - uid: 16662 + - uid: 9849 components: - type: Transform - rot: 3.141592653589793 rad - pos: 53.5,-7.5 + rot: -1.5707963267948966 rad + pos: -13.5,-26.5 parent: 2 - - uid: 16663 + - 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: 32.5,-25.5 + pos: -13.5,-25.5 parent: 2 - - uid: 16664 + - uid: 12188 components: - type: Transform rot: 1.5707963267948966 rad - pos: -23.5,-35.5 + pos: -11.5,-26.5 parent: 2 - - uid: 16665 + - uid: 12189 components: - type: Transform - pos: 36.5,-12.5 + rot: 1.5707963267948966 rad + pos: -11.5,-27.5 parent: 2 - - uid: 16666 + - uid: 16661 components: - type: Transform - rot: 3.141592653589793 rad - pos: -22.5,-39.5 + pos: 34.5,-12.5 parent: 2 - - uid: 16667 + - uid: 16662 components: - type: Transform rot: 3.141592653589793 rad - pos: -23.5,-39.5 + pos: 53.5,-7.5 + parent: 2 + - uid: 16663 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 32.5,-25.5 + parent: 2 + - uid: 16665 + components: + - type: Transform + pos: 36.5,-12.5 parent: 2 - uid: 16668 components: @@ -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 57fe6cc819a..cd5cc31b940 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: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + moles: {} - 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: - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 6666.982 - 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 37bdb4b2d85..bbf83a6b3b0 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 2ce9ae02c87..17df6cef9ca 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 fa22e6ffb46..4a65dd62b57 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,21 +45304,29 @@ entities: parent: 20154 - proto: AirlockExternalGlassAtmosphericsLocked entities: - - uid: 181 + - uid: 183 components: - type: Transform - pos: 97.5,21.5 + rot: -1.5707963267948966 rad + pos: 97.5,26.5 parent: 2 - - uid: 182 + - uid: 16992 components: - type: Transform - pos: 97.5,19.5 + rot: 1.5707963267948966 rad + pos: 97.5,15.5 parent: 2 - - uid: 183 + - uid: 21727 components: - type: Transform rot: -1.5707963267948966 rad - pos: 97.5,26.5 + 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: @@ -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: Transform + rot: 3.141592653589793 rad + pos: 96.5,15.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 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: MetaData - desc: Эта кнопка переключает гермозатвор камеры смешивания. - type: Transform pos: 96.5,28.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 544: + 21734: + - - Pressed + - Toggle + 21735: + - - Pressed + - Toggle + 21736: - - Pressed - Toggle - type: Fixtures fixtures: {} - - uid: 14626 + - uid: 21733 components: - - type: MetaData - desc: Эта кнопка переключает гермозатвор камеры смешивания. - type: Transform - rot: 3.141592653589793 rad - pos: 96.5,15.5 + rot: 1.5707963267948966 rad + pos: 97.5,30.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 545: + 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 0d58c69d0b7..7cb40d3e762 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: 3567 + - uid: 114 components: - type: Transform - pos: 12.5,13.5 + pos: 6.5,13.5 parent: 2 - type: DeviceNetwork deviceLists: - 32 - - uid: 3568 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3406 components: - type: Transform - pos: 5.5,13.5 + rot: 3.141592653589793 rad + pos: 0.5,11.5 parent: 2 - - uid: 3569 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3522 components: - type: Transform - pos: 1.5,11.5 + 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: 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 c2d2628fb94..1797ce0ecb4 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 4dbaf07aabe..9e9bc9f650c 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 e0956a76f7a..c07584e737f 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 00000000000..b6702fb4b4e --- /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 f27bab9074d..b217074ca88 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 8455f339817..e1b8e5dcdd2 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 - type: entity parent: OrganBase diff --git a/Resources/Prototypes/Body/Species/diona.yml b/Resources/Prototypes/Body/Species/diona.yml index 2c0264f85e2..2f1e49ddbc3 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 1d64142ca96..5b053f0735c 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 b32cbac91d9..5c429af01c8 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 1f59cbefe57..6777d647979 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 af243333a68..4b46381c3c9 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 9a0d745027e..7891d167c1e 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 8372e4923d3..953a8c7baed 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 82c8b30ca6a..a0dc240c440 100644 --- a/Resources/Prototypes/Body/Species/slime.yml +++ b/Resources/Prototypes/Body/Species/slime.yml @@ -35,6 +35,14 @@ enum.HumanoidVisualLayers.RFoot: limit: 1 required: false + # Corvax-Sponsors-start + enum.HumanoidVisualLayers.HeadTop: + limit: 1 + required: false + enum.HumanoidVisualLayers.Tail: + limit: 1 + required: false + # Corvax-Sponsors-end appearances: enum.HumanoidVisualLayers.Hair: layerAlpha: 0.72 @@ -46,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 9fab05ea20f..dabfebcb551 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 @@ -309,6 +314,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 293ecbc7afb..9d6b1d5cce8 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 @@ -302,6 +307,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 44369b15489..e70b6abb38a 100644 --- a/Resources/Prototypes/Body/species_appearance.yml +++ b/Resources/Prototypes/Body/species_appearance.yml @@ -66,11 +66,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 eadd6747e9c..9c0365d9acd 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 @@ -155,6 +151,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 516765e268a..8b4df9f89fa 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 9d53c1e2b4e..1f1432e7093 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 2bbcc3b13df..f377d96db9a 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 31f4d1d12c2..6228c1a2294 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 7e7bb371625..4ceaeef80cf 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 dea0f908ebc..57b0addaa68 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 1d304d021e7..ac7630132af 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 cea67fa0ee0..f155b99433d 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 ba4cc9d75b6..9a5306b5009 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 d98820e8328..00000000000 --- a/Resources/Prototypes/Corvax/SoundCollections/growl.yml +++ /dev/null @@ -1,6 +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 diff --git a/Resources/Prototypes/Corvax/Voice/speech_emote_sounds.yml b/Resources/Prototypes/Corvax/Voice/speech_emote_sounds.yml index 796802378b2..057e23f0323 100644 --- a/Resources/Prototypes/Corvax/Voice/speech_emote_sounds.yml +++ b/Resources/Prototypes/Corvax/Voice/speech_emote_sounds.yml @@ -1,72 +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 - 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 - 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 fd0ef4f2d1e..0aac5c96d5e 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 9c627741854..2fa68934fad 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml @@ -201,7 +201,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 a40740f46a2..a551ce4418a 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 44e4983fd8c..af513936dae 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -1535,3 +1535,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 00000000000..ca9cc5db598 --- /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 e1cdba065e1..b7a1e3cd30d 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 22ad9096f45..a05cf9baa49 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -348,7 +348,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 d10c37392ef..708cd59ce13 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 05ad0ba725e..b921582ff73 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 d049250b55f..ac845e55d49 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 7d0f734acc0..1a4d8defb8d 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 7683f198840..f7c553b022f 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 e4213ad31f5..27256c673e4 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 4c64bdd7860..14aea5b79c1 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 fda7b85b75b..ea44f18599c 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 df730a6567f..7cc95278697 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 0b530c68b19..98dfc20c13a 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 1c90664b3de..09c4ce87618 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 b634d50cc6c..e919cf192d9 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 f2e4bb002ca..890d21b597c 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 3b99d62124b..f81cda4e06f 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_burning @@ -1446,7 +1439,10 @@ - Passive - type: HTN rootTask: - task: SimpleHostileCompound + task: MonkeyCompound + blackboard: + NavInteract: !type:Bool + true - type: IdExaminable - type: Tag tags: @@ -1505,7 +1501,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: @@ -1629,10 +1625,6 @@ - map: [ "id" ] - map: [ "mask" ] - map: [ "head" ] - - map: [ "clownedon" ] - sprite: "Effects/creampie.rsi" - state: "creampie_human" - visible: false - type: RandomSprite getAllGroups: true available: @@ -2975,6 +2967,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 894c317d7fd..f790b4abf95 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 43578f6e1bd..973111c787a 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 c443f537945..0bdfcdfa27c 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 @@ -95,7 +94,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 6ee9de8d01e..dcddf77e1fe 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 108329c78c1..279bc5b9cad 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -9,12 +9,17 @@ id: Body components: # general + - CreamPied - DetailExaminable - Dna - Fingerprint - Grammar # Pronouns - HumanoidProfile # Age, Sex, Gender + - MeleeWeapon # Attack animation, damage and sound - NpcFactionMember + - RandomPrice + - TemperatureDamage + - TemperatureSpeed # traits - BlackAndWhiteOverlay - Clumsy @@ -58,6 +63,7 @@ - StutteringAccent - TTS # Corvax-TTS eventComponents: + - Puller - Vocal # voice sounds # for job-specific traits etc. @@ -79,16 +85,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 @@ -108,7 +118,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. @@ -129,9 +139,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 57b682caea9..7a1dbd71442 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 @@ -285,3 +284,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 6c5105b85f8..a61c2f85297 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 - type: UserInterface interfaces: @@ -284,6 +283,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 @@ -480,6 +489,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: @@ -499,6 +518,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 2e52646dac0..118c0082919 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 79beb70551c..ad3b59b0eaf 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 00000000000..5ec8b87ae00 --- /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 532d0b9cca3..70c823b765c 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 8e29fdd88f8..f114ece5467 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 97865aef325..3e9a89c0d2b 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 95e8fcd3720..43a9ac228fe 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 e125e1168fe..6e447858f99 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -835,40 +835,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 0618e308d53..64311dcde66 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 8ce9de0f3ba..574e079fed5 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 00000000000..d15b8453222 --- /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 5a6bc352c44..94dd306d8c7 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 @@ -249,7 +250,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 4 minutes of thrust volume: 1.5 @@ -284,7 +285,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 13 minutes of thrust volume: 5 @@ -301,7 +302,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 34dcd66b71c..c7cd0efe7b1 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 00000000000..402ad51ce7d --- /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 f65869d9dd2..3b4bbdf036a 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 2f6ac834ba4..6d5830e161d 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 7c7d5fc68c6..c85f975b934 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 00000000000..2853eb19e29 --- /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 00000000000..82200e80e35 --- /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 15897201b2f..95aa1153107 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 72f6b1fdf25..f423bfde50a 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 dd58b9709d3..5b250969ce0 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 8c8c3473dab..31aa9ef8d7d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -667,8 +667,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 @@ -691,10 +689,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 cafdc7cef2a..dad41766178 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 aba7531ef5e..90c33838ca2 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 e80bb909412..469de4bad35 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 038ac70d823..4311cd389e3 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 38ec6f8cc49..7ffc5943420 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 8f22f8d68c3..1410eb4e1cb 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 aeab29886f1..8a1e7fcef89 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 drawdepth: AboveFovOverlay # TSF edit @@ -35,6 +35,9 @@ min: 1 max: 1 offset: 0 + - type: Construction + graph: PosterGraph + node: start - type: entity parent: BaseSign @@ -46,6 +49,8 @@ drawdepth: WallTops sprite: Structures/Wallmounts/posters.rsi state: poster_broken + - type: Damageable + damageModifierSet: Card - type: Destructible thresholds: - trigger: @@ -57,6 +62,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 ea48166ec58..f53d6eeaecb 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 561991cd268..a12bca758d6 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 55b06f1e603..b20256ec7e7 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 1049129c7be..1b039be8eaf 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 bb33801e2d6..00000000000 --- 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 c125d991fd4..00000000000 --- 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 7bbeadeb5ba..00000000000 --- 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 c7cb09c2a4b..00000000000 --- 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 6a7a1f15c22..87f396e841d 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -329,8 +329,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 99a803f1d72..9d047a25d51 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 d191ad28602..f80d89a70f4 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -286,11 +286,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 3b8d225c367..b3468c0848e 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 3e4823d449a..b9c87343b29 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 b04f8388db8..6cb62fb0891 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 # Jumpsuit - type: loadout @@ -64,17 +64,17 @@ - type: loadout id: SeniorEngineerJumpsuit - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorEngineering + effects: + - !type:GroupLoadoutEffect + proto: SeniorEngineering equipment: jumpsuit: ClothingUniformJumpsuitSeniorEngineer - type: loadout id: SeniorEngineerJumpskirt - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorEngineering + effects: + - !type:GroupLoadoutEffect + proto: SeniorEngineering equipment: jumpsuit: ClothingUniformJumpskirtSeniorEngineer @@ -124,8 +124,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 49bb43558da..7ff040dc317 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 @@ -53,17 +53,17 @@ - type: loadout id: SeniorPhysicianJumpsuit - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorPhysician + effects: + - !type:GroupLoadoutEffect + proto: SeniorPhysician equipment: jumpsuit: ClothingUniformJumpsuitSeniorPhysician - type: loadout id: SeniorPhysicianJumpskirt - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorPhysician + effects: + - !type:GroupLoadoutEffect + proto: SeniorPhysician equipment: jumpsuit: ClothingUniformJumpskirtSeniorPhysician @@ -106,9 +106,9 @@ - type: loadout id: SeniorPhysicianLabCoat - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorPhysician + effects: + - !type:GroupLoadoutEffect + proto: SeniorPhysician equipment: outerClothing: ClothingOuterCoatLabSeniorPhysician @@ -126,9 +126,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 7eeab8ea8e3..b07be72f89e 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 21965389c45..080015f0da7 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 @@ -61,17 +61,17 @@ - type: loadout id: SeniorResearcherJumpsuit - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorResearcher + effects: + - !type:GroupLoadoutEffect + proto: SeniorResearcher equipment: jumpsuit: ClothingUniformJumpsuitSeniorResearcher - type: loadout id: SeniorResearcherJumpskirt - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorResearcher + effects: + - !type:GroupLoadoutEffect + proto: SeniorResearcher equipment: jumpsuit: ClothingUniformJumpskirtSeniorResearcher @@ -119,9 +119,9 @@ - type: loadout id: SeniorResearcherLabCoat - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorResearcher + effects: + - !type:GroupLoadoutEffect + proto: SeniorResearcher equipment: outerClothing: ClothingOuterCoatLabSeniorResearcher @@ -150,8 +150,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 523476e7752..8fcde022223 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 @@ -62,17 +72,17 @@ - type: loadout id: SeniorOfficerJumpsuit - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorOfficer + effects: + - !type:GroupLoadoutEffect + proto: SeniorOfficer equipment: jumpsuit: ClothingUniformJumpsuitSeniorOfficer - type: loadout id: SeniorOfficerJumpskirt - # effects: Corvax-MentorRoles - # - !type:GroupLoadoutEffect - # proto: SeniorOfficer + effects: + - !type:GroupLoadoutEffect + proto: SeniorOfficer equipment: jumpsuit: ClothingUniformJumpskirtSeniorOfficer @@ -114,6 +124,13 @@ equipment: outerClothing: ClothingOuterArmorBasicSlim +# Corvax-Start +- type: loadout + id: SecurityOfficerOvercoat + equipment: + outerClothing: ClothingOuterCoatSecurityOvercoat +# Corvax-End + - type: loadout id: SecurityOfficerWintercoat equipment: @@ -143,8 +160,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 b2571c917db..60ada10443d 100644 --- a/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml @@ -534,6 +534,7 @@ loadouts: - JesterHat - JesterAltHat + - ClownMitre - type: loadoutGroup id: ClownJumpsuit @@ -1246,6 +1247,7 @@ - ArmorVestSec # Corvax - ArmorVest - ArmorVestSlim + - SecurityOfficerOvercoat # Corvax - SecurityOfficerWintercoat - type: loadoutGroup diff --git a/Resources/Prototypes/NPCs/Combat/gun.yml b/Resources/Prototypes/NPCs/Combat/gun.yml index 2b5ccd9fa55..abecd18719b 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 e25284bb880..c6fce024163 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 00000000000..3e321a2d380 --- /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 bb2bb08cc42..3c1d3fe5f25 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 1abbe74f3ad..12204153c2e 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 c77ccfb3ed0..93cf9d46072 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 89692b62257..bd82eed9613 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 661350f6de6..8d4632c0f36 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 0caa9f0e1f5..7cc40dbb8f1 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 43bd9efedc0..e22993d0a37 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml @@ -123,15 +123,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 1b1ae04cd55..9c8d4e39a1a 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 b8f0216e6cf..a38be9e4cb7 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -1430,9 +1430,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 00000000000..ce7ca40ab78 --- /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 4eb368f5ab7..3ace4425160 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 2df2891b7c9..f9147efe4ff 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 fd25394e673..99c1ac30e5b 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 e30174a6ae3..29aa64cac59 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 622df1ba5e0..b90649b438f 100644 --- a/Resources/Prototypes/Research/civilianservices.yml +++ b/Resources/Prototypes/Research/civilianservices.yml @@ -103,6 +103,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 0cd255b7fd9..f274d99fb29 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 70c9dce6f79..debb68fb1f5 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 057abf0ac23..bf740ba1f67 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 26ecee52695..8c619226ff2 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 de3ea1f6a14..4ad5712ae4a 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 108a7f56e84..a32f03ed8d4 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 ccdb349d9c1..a4c339d0e74 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 37329d4096d..d4c1245c550 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 299032bf779..de3e93223b2 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 5ecd85bbc48..00000000000 --- 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 5e3c50b44c3..00000000000 --- 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 668b338dd35..00000000000 --- 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 af52c30cf1e..00000000000 --- 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 d0f6da99d50..94fc8bd3baa 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 a48758716ca..c40b26fe85e 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 c2dc4c028fe..5c7c8c1ca69 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 8b8420622af..244244a3c1a 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 179d0622e37..f8efb31941d 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 fb2b548321c..b4cd74da64a 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 dd88b6eb6f5..ec73acd82cc 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 43e151212da..2fb185c9f86 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/Cargo.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Cargo.xml index 1036c78ced9..cd05e66d7e1 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Cargo.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Cargo.xml @@ -30,16 +30,6 @@ ### Утилизатору запрещено - ## СРП Шахтёра - ### Шахтёр обязан - - - ### Шахтёр имеет право - - - ### Шахтёру запрещено - - ## СРП Грузчика ### Грузчик обязан diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Centcomm.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Centcomm.xml index ac2360930d1..7d6b17d580c 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Centcomm.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Centcomm.xml @@ -45,24 +45,4 @@ ### НШЦК запрещено - - ## СРП Офицера Синего Щита - ### Офицер Синего Щита обязан - - - ### Офицер Синего Щита имеет право - - - ### Офицеру Синего Щита запрещено - - - ## СРП Представителя NanoTrasen - ### Представитель NanoTrasen обязан - - - ### Представитель NanoTrasen имеет право - - - ### Представителю NanoTrasen запрещено - diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Command.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Command.xml index c4434cac9e2..09f3fee178d 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Command.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Command.xml @@ -1,4 +1,7 @@ + # Протокол обращения с особо ценными предметами + + # Общие СРП ### Глава отдела обязан @@ -112,16 +115,7 @@ ### Причины отстранения от должности - ## Протоколы эвакуации - ### Окончание смены - - - ### Невозможность выполнения цели - - - ### Нарушение условий содержания сингулярности - + ## Протокол эвакуации + - ### Вспышка зомби вируса - diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml new file mode 100644 index 00000000000..19d5ad76984 --- /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 bee73ba66dc..58915ea46fd 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/General.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/General.xml index e685a58c9c8..bc12f93407f 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/General.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/General.xml @@ -45,4 +45,10 @@ ### Общие запреты оформления заказов + + ## Взаимодействие с ДСО + + + ## Мобилизация + diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml index af4a9492a72..044ec54d7c6 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 55979b5966e..26fa6a80b20 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml @@ -10,26 +10,6 @@ ### Главе персонала запрещено - ## СРП Адвоката - ### Адвокат обязан - - - ### Адвокат имеет право - - - ### Адвокату запрещено - - - ## СРП Радиоведущего - ### Радиоведущий обязан - - - ### Радиоведущий имеет право - - - ### Радиоведущему запрещено - - ## СРП Сервисного работника ### Сервисный работник обязан @@ -37,9 +17,6 @@ ### Сервисный работник имеет право - ### Сервисному работнику запрещено - - ## СРП Клоуна ### Клоун обязан diff --git a/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml b/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml index a5fadbaa84c..7e7afb4285a 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 97880466d44..6816570d93f 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 00000000000..4108cb11fd0 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 00000000000..0642b3c856e 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 00000000000..0bd1b696e1d 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 00000000000..c27f0dffddc 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 3f3418b36c1..d2e6edca2c4 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 00000000000..09b5da46c9e 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 00000000000..b25916579b6 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 00000000000..6cd86793c2f 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 00000000000..0a191ee2928 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 00000000000..ed4e492f8d8 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 00000000000..3e435fc3045 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 7fca8dc1d3f..abfb0746e70 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 b612633d44f..f0616e50f31 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 68f73226fdc..23d6a1ede19 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 b612633d44f..b9e6f63cf33 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 ce11edf2363..63cca39c10a 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 c3a3dcd2311..b9e6f63cf33 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 b99dad7b10f..bbb6c68005b 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 1e23bc37364..0ff64526d56 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 f13d472e818..168f7c29d3d 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 9184e71509a..71cd9e9624c 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 17cbe45cbe6..f126b02458f 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 f13d472e818..cb808c9473d 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 ee020ded8c3..0b1a3fe2eac 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 9b67d0e8814..5240eaed454 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 f13d472e818..4023318c9b5 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 a18ba86f4bf..3d096343b12 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 95c92f59c9c..2e35f7782ac 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 f13d472e818..168f7c29d3d 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 19c0c0c357a..5f3460b6a34 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 cce4bdcef59..e94f49c3233 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 f13d472e818..cb808c9473d 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 79291740531..9dec6a4aeba 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 53a11ba7e13..044825fd5e7 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 f13d472e818..168f7c29d3d 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 e98878e7de8..2d44b189199 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 81f97d57027..57ed954385d 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 74a3ff47c08..fb88afb4227 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 b612633d44f..f0616e50f31 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 ca2cdf0d652..e71bf06b839 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 5264ee019bc..b9e6f63cf33 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 eade7edf28e..702f2842711 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 b612633d44f..f0616e50f31 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 4df42d6f1d4..4515e6f303a 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 410b18ba09a..476c8bea62d 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 f13d472e818..168f7c29d3d 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 cc301f1a04c..27d66e602d5 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 789530863d5..74ff187c52c 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 f13d472e818..cb808c9473d 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 26040fe8651..e6a73b100c6 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 b612633d44f..f0616e50f31 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 b010229b037..0e9f881d000 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 a9efa2c815a..b5c0cc48040 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 a1af823e1cd..8ea18b63700 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 56b6260f355..5829a34f2e3 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 e13a2bf15c1..93bf29acb7b 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 962130247f8..2421c4ad3f0 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 d5510a5d8b6..cb808c9473d 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 00000000000..5647e824379 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 00000000000..abf8dbbcdff 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 00000000000..1329de8c115 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 00000000000..afb2afbac9a 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 00000000000..8084ccf4673 --- /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 dabd7cb3d36..bac0bd75d95 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 af141d7eec5..cd3e95bb9a3 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 4e1c6c10dac..1d42deb94c6 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 f457d22f2f5..ca99276fce6 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 759e6b3ed97..27ff39da484 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 dfbf1399ce3..5cee1be27c7 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 4e1c6c10dac..1d42deb94c6 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 51d09dc18cf..3a2095c2274 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 4a3bb5d4b85..8c45376672f 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 4f669b4bb74..d4a9e495ffd 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 cb9b30f249b..7566e714e2c 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 d3c0f91adf1..d01869b45f2 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 bacde823612..4e665a18e8b 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 eec3320f0f7..991445e5824 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 e3340fa623b..5669e56e45c 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 7b7a9696dcf..142d5e7744e 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 e2542d772cd..84f70a58a65 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 350f6a288ba..1ca03449f8a 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 65c104d1c75..7fadd0c88b5 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 520d5c63408..00083227489 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 e2842ac2743..8dd5db16693 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 84009efde35..1797234d0de 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 b9273f213f2..4f08ace7129 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 aac777b0f95..61811311f75 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 8ecff94859b..0aca74f6b6d 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 3abdad08570..0ecd48a486b 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 17d75d29b36..00000000000 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 a6947e22780..4b453046379 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 c208aba1f8c..8edafea2ee4 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 bb043734cdc..910c02b87f2 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 36c7711a7df..7b3ddbcc1f3 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 0e30ab4d3f1..789f9d124f9 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 4d0f8d2ea25..0b3d47be055 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 c4006135e38..41e9e6d0638 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 b058f6004dc..8ec188ea166 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 e0bfcc67a0a..31c0b16dac7 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 2b35b6a700c..4401465b085 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 d54a3b6f3d7..52c71d0b6a8 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 6e4a57f3c0b..70d8a4d43db 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 d7d694fb929..dcabc77e8a0 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 1ae542d0863..9516b128d99 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 a5d0ba43f9f..20f60ab6030 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 8c1b0024bf2..98118a87a4c 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 00000000000..f1422d52a88 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 00000000000..f3ef97531c9 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 cbfcdf45d0e..073e279f0c1 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 00000000000..bb09b413daa 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 00000000000..75ac1819cca 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 00000000000..7b593f34492 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 00000000000..45eb0e38540 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 00000000000..a9c507fe65c --- /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 00000000000..47991017abb 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 00000000000..fffbd61b5cb 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 00000000000..78137bbfcce --- /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 1df5da9a2bf..dead0a28feb 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 00000000000..ac38cdbac02 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 00000000000..5c43e233050 --- /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 ac544fb5bd8..28f2c33515f 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 00000000000..02fc551ed7c 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 6409354fa8d..93fa4f1f55d 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 44e3df3e91b..00000000000 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 dada5727bf6..00000000000 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 dada5727bf6..00000000000 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 bb7425405cf..00000000000 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 8e0b3f15076..00000000000 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 cf93432a5e7..00000000000 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 d693b3696de..00000000000 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 688877a32d0..00000000000 --- 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 51f05a47738..00000000000 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 19ac240da3a..00000000000 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 6cd2eb37ccb..00000000000 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 e1ea113ca3d..00000000000 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 6d0ea667805..00000000000 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 2bcb3cc9eba..00000000000 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 2bcb3cc9eba..00000000000 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 6901e6c33b1..00000000000 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 6901e6c33b1..00000000000 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 e5833360bdb..984030280f1 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 00000000000..03ae539d6f8 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 00000000000..2e13f71cfa0 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 00000000000..c475dea254c 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 00000000000..6df91ff4a29 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 00000000000..acd1d829677 --- /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 00000000000..2e1284ebeb7 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 00000000000..d32bb7534c5 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 00000000000..6635d990ece 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 00000000000..a76a07d169e --- /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 00000000000..6bf8b7b2527 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 00000000000..5aaef8a35bd 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 00000000000..9df16839891 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 00000000000..6cfc8c71ed7 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 00000000000..b764b37fe43 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 708d514397c..7ae9fa16286 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 00000000000..0b2ba41f2ff 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 00000000000..cec43f8bd71 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 6c11b869211..5ae1530e5d3 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 00000000000..e6677544810 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 00000000000..5fc8309c430 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 345a622f3f8..59c4c75ec8c 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 00000000000..14c04d0ddde 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 00000000000..e00c0e7a558 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 00000000000..aeae5c35d9e 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 00000000000..443e1e7ae1e 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 00000000000..116628a3ed5 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 00000000000..2e2d20d3975 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 99437cf31d7..ecd1f06fee9 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 00000000000..14c04d0ddde 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 00000000000..e00c0e7a558 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 00000000000..aeae5c35d9e 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 00000000000..443e1e7ae1e 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 00000000000..ad78a82d19a 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 00000000000..979cb6add83 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 99437cf31d7..ecd1f06fee9 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 00000000000..14c04d0ddde 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 00000000000..e00c0e7a558 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 00000000000..aeae5c35d9e 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 00000000000..443e1e7ae1e 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 00000000000..ad78a82d19a 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 00000000000..908cd70ce97 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 bc260937b38..79fea601586 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 00000000000..4171a36f1e3 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 00000000000..96fef170863 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 00000000000..05118ceae9d 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 00000000000..b6f305736ac 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 00000000000..07fb9994ba9 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 00000000000..d8f296bd8bc 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 b2f8b619d53..756d7257dd9 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 00000000000..14c04d0ddde 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 00000000000..e00c0e7a558 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 00000000000..aeae5c35d9e 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 00000000000..443e1e7ae1e 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 00000000000..ad78a82d19a 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 00000000000..64e78630870 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 bc260937b38..79fea601586 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 00000000000..c81e4b9868e 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 00000000000..52220c89543 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 00000000000..c42dce067da 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 00000000000..cd0d10099f3 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 c075eb9a7b7..931132780c8 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 00000000000..a7d8b595072 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 a8ce0b5a28b..0f47b7ebe81 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 00000000000..ff4e662e733 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 00000000000..63df9585b96 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 00000000000..8ecb49826fa 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 00000000000..b8d7b7f8093 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 00000000000..87df57230ab 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 00000000000..7814561c83a 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 a9dff4eaf13..d47e1e011d6 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 00000000000..f40c14ac47c 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 00000000000..278f80945f9 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 00000000000..c06374e80f1 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 00000000000..54c1753e487 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 00000000000..a65b568dccf 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 00000000000..1062dad6d5f 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 8c67ffd7c9a..53f656df6ae 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, @@ -48,17 +43,22 @@ "delays": [[1],[1],[1],[1]] }, { - "name": "equipped-SUITSTORAGE-possum", + "name": "equipped-SUITSTORAGE-hamster", "directions": 4, "delays": [[1],[1],[1],[1]] }, { - "name": "equipped-SUITSTORAGE-pig", + "name": "equipped-SUITSTORAGE-kangaroo", "directions": 4, "delays": [[1],[1],[1],[1]] }, { - "name": "equipped-SUITSTORAGE-hamster", + "name": "equipped-SUITSTORAGE-possum", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-pig", "directions": 4, "delays": [[1],[1],[1],[1]] }, @@ -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 00000000000..8b05d65cb11 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 00000000000..637c1f007cb 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 00000000000..c675dfeb0e3 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 00000000000..5425aea0d97 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 00000000000..1a64b21a639 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 00000000000..d7fd9a572e9 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 760edc5a670..e32c18d4795 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 00000000000..4a3a086d629 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 00000000000..dd289f8c520 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 00000000000..c406019ccb7 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 00000000000..c97800296bd 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 00000000000..0006eb20b33 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 00000000000..247117f21e8 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 f4db0728d24..e32c18d4795 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 00000000000..4a3a086d629 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 00000000000..dd289f8c520 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 00000000000..c406019ccb7 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 00000000000..c97800296bd 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 00000000000..0006eb20b33 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 00000000000..ce27f54c82e 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 f8ac0731881..e32c18d4795 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 00000000000..8b05d65cb11 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 00000000000..637c1f007cb 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 00000000000..c675dfeb0e3 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 00000000000..5425aea0d97 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 00000000000..1a64b21a639 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 00000000000..b528543af31 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 f4db0728d24..e32c18d4795 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 00000000000..8b05d65cb11 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 00000000000..637c1f007cb 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 00000000000..c675dfeb0e3 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 00000000000..5425aea0d97 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 00000000000..1a64b21a639 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 00000000000..afc9525d813 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 dff0f2dd0e1..e32c18d4795 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 00000000000..f40c14ac47c 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 00000000000..278f80945f9 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 00000000000..c06374e80f1 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 00000000000..54c1753e487 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 00000000000..a65b568dccf 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 00000000000..1532b8a6bad 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 5990e60d2b4..53f656df6ae 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, @@ -48,17 +43,22 @@ "delays": [[1],[1],[1],[1]] }, { - "name": "equipped-SUITSTORAGE-possum", + "name": "equipped-SUITSTORAGE-hamster", "directions": 4, "delays": [[1],[1],[1],[1]] }, { - "name": "equipped-SUITSTORAGE-pig", + "name": "equipped-SUITSTORAGE-kangaroo", "directions": 4, "delays": [[1],[1],[1],[1]] }, { - "name": "equipped-SUITSTORAGE-hamster", + "name": "equipped-SUITSTORAGE-possum", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-pig", "directions": 4, "delays": [[1],[1],[1],[1]] }, @@ -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 00000000000..f40c14ac47c 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 00000000000..278f80945f9 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 00000000000..c06374e80f1 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 00000000000..54c1753e487 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 00000000000..a65b568dccf 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 00000000000..0d1e43896f8 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 1cfa40a9f9e..53f656df6ae 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 00000000000..ff4e662e733 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 00000000000..63df9585b96 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 00000000000..8ecb49826fa 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 00000000000..b8d7b7f8093 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 00000000000..87df57230ab 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 00000000000..5d7acb4d1ce 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 62f7319206a..2b323dee74e 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 00000000000..ff4e662e733 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 00000000000..63df9585b96 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 00000000000..8ecb49826fa 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 00000000000..b8d7b7f8093 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 00000000000..87df57230ab 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 00000000000..8dc354095e6 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 62f7319206a..2b323dee74e 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 00000000000..0e8b3d4bd27 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 00000000000..aaf9fc78f13 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 00000000000..69ab788a113 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 00000000000..a000c08a62d 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 00000000000..866db3b674b 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 00000000000..77d9a4a379c 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 2d3199f75cd..f7ddcc62359 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 00000000000..ff4e662e733 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 00000000000..63df9585b96 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 00000000000..8ecb49826fa 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 00000000000..b8d7b7f8093 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 00000000000..87df57230ab 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 00000000000..33638d35eea 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 62f7319206a..2b323dee74e 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 00000000000..7b8dd4c3afe 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 00000000000..5177acf42f8 --- /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 00000000000..5e345c10e74 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 00000000000..171ad181625 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 00000000000..c51709c6ad0 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 00000000000..bdf4d8ab35b 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 00000000000..8c8fdb1e0ca 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 00000000000..d3bf22c77d6 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 00000000000..23c81a161ad 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 00000000000..a9711a6798f --- /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 00000000000..cc389b9a9bf --- /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 c2657c18a5e..1410baa8237 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 00000000000..0f7d2400e38 --- /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 7a1e37693e1..c7b4de25538 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 a06a48885bf..30e26f0ed45 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 50e9b054af8..a860f5e15ee 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 5c582fe7550..3c0b66c9097 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 8daa67ae23e..0b7ff8bd30b 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 ff808bbc8bb..4a6f70cea14 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 da76b044c1e..0780db8b746 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 5bbd100773c..eea1060c57a 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 fd97a4b39d6..5e5cd4c8d71 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 8ae607cdcd4..699a1155d48 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 42114c515b2..0fc82598d5f 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 c8424057d02..5f7c2cfeefd 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 69066fc8031..b2421af6d02 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 0b14b77aec5..3cc26d19ff4 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 e0ec34938b7..509c9e55a30 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 4c59d3a28cc..00000000000 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 c41e1484ee8..89db9a5bff7 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 00000000000..12d689adf9f 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 00000000000..4133de5276b 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 42ad3ab990d..4a59465e532 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 00000000000..e9d698667da 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 00000000000..e9d698667da 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 1aee0099ef5..4ca570e0710 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 00000000000..40ab6caf127 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 00000000000..40ab6caf127 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 50271d1e334..0483760670a 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 00000000000..ee5e372ef44 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 00000000000..f457d016c31 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 00000000000..6791d3b8134 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 00000000000..30e76da9fae 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 00000000000..7c1bb333d34 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 00000000000..e44e4191e07 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 00000000000..d3bc54f284a 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 00000000000..938cc9c1611 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 00000000000..48da90174ea 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 00000000000..aafe16a4f5c 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 00000000000..87e7dd783c5 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 00000000000..d94929d4cba 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 00000000000..a7ce5a73a9f 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 00000000000..88a6f988dbf 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 00000000000..b564c698662 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 00000000000..ed23006740e 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 00000000000..038a8aeaea3 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 823edb53a42..fa24bd0897d 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" }, @@ -130,6 +166,21 @@ }, { "name": "paper" + }, + { + "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 dad56301e11..3136118b533 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 00000000000..2c8c3dd1067 --- /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()