From 5bd9d21cb605b3a8ccb6ad46ded392a6b850ab58 Mon Sep 17 00:00:00 2001 From: salarua Date: Fri, 27 Mar 2026 10:41:17 -0700 Subject: [PATCH 001/247] Change chat straight quotes to curly quotes (#43350) --- Resources/Locale/en-US/chat/emotes.ftl | 2 +- Resources/Locale/en-US/chat/managers/chat-manager.ftl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Resources/Locale/en-US/chat/emotes.ftl b/Resources/Locale/en-US/chat/emotes.ftl index cabb808728..c71fade285 100644 --- a/Resources/Locale/en-US/chat/emotes.ftl +++ b/Resources/Locale/en-US/chat/emotes.ftl @@ -58,7 +58,7 @@ chat-emote-msg-salute = salutes. chat-emote-msg-gasp = gasps. chat-emote-msg-deathgasp = seizes up and falls limp, {POSS-ADJ($entity)} eyes dead and lifeless... chat-emote-msg-deathgasp-monkey = lets out a faint chimper as {SUBJECT($entity)} collapses and stops moving... -chat-emote-msg-deathgasp-scurret = lets out a final 'wa' and falls still... +chat-emote-msg-deathgasp-scurret = lets out a final ‘wa’ and falls still... chat-emote-msg-buzz = buzzes! chat-emote-msg-weh = wehs! chat-emote-msg-hew = hews! diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 93f149bed9..5cc28824ea 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -22,11 +22,11 @@ chat-manager-server-wrap-message = [bold]{$message}[/bold] chat-manager-sender-announcement = Central Command chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12] {$message}[/bold][/font] -chat-manager-entity-say-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent]{$message}[/BubbleContent]"[/font] -chat-manager-entity-say-bold-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent][bold]{$message}[/bold][/BubbleContent]"[/font] +chat-manager-entity-say-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]“[BubbleContent]{$message}[/BubbleContent]”[/font] +chat-manager-entity-say-bold-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]“[BubbleContent][bold]{$message}[/bold][/BubbleContent]”[/font] -chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers,"[BubbleContent]{$message}[/BubbleContent]"[/italic][/font] -chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, "[BubbleContent]{$message}[/BubbleContent]"[/italic][/font] +chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers,“[BubbleContent]{$message}[/BubbleContent]”[/italic][/font] +chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, “[BubbleContent]{$message}[/BubbleContent]”[/italic][/font] # THE() is not used here because the entity and its name can technically be disconnected if a nameOverride is passed... chat-manager-entity-me-wrap-message = [italic]{ PROPER($entity) -> From bc29aeac3bf1ab6f62e50ad82dc295779a5e9ca6 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 27 Mar 2026 17:55:09 +0000 Subject: [PATCH 002/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 024213b7f9..ef642a5314 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +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. @@ -4033,3 +4026,10 @@ 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 From 9adb10d791460b7123a8484440806d4dfb0a4beb Mon Sep 17 00:00:00 2001 From: Moony Date: Fri, 27 Mar 2026 12:08:47 -0700 Subject: [PATCH 003/247] GameTest part 1 (#43182) * Ports much of GameTest, minus all the WIP stuff. * remark. * EnsureCVar now adds test properties. * Some cleanup and functionality. * Ignore broken test. Needs fixed eventually. Also explicit context config. * TrackingIssue attribute. * oops * Pair config attribute. * Remove SystemAttribute in favor of using the EntitySystemManager dependency collection. * Ensure idleness. * More tests for tests. * More specific failure catching tests. * Reverse attribute resolution order so suite-wide attributes happen first. * Get rid of AffectedProperties again because I need to refactor PoolSettings for that. * Poke * Final cleanup pass. * Update Content.IntegrationTests/Fixtures/Attributes/RunOnSideAttribute.cs Co-authored-by: Tayrtahn * Update Content.IntegrationTests/Fixtures/Attributes/Side.cs Co-authored-by: Tayrtahn * Update Content.IntegrationTests/NUnit/Utilities/ITestExtensions.cs Co-authored-by: Tayrtahn * Fixes. * shut up nunit. * Make TrackingIssue a bit strict on purpose, so people don't put junk here. * Update Content.IntegrationTests/Tests/GameTestTests/DisconnectedDependencyTest.cs Co-authored-by: Tayrtahn * Address. --------- Co-authored-by: Tayrtahn --- .../Attributes/EnsureCVarAttribute.cs | 74 +++++ .../Fixtures/Attributes/IGameTestModifier.cs | 20 ++ .../Attributes/IGameTestPairConfigModifier.cs | 25 ++ .../Attributes/PairConfigAttribute.cs | 53 ++++ .../Fixtures/Attributes/RunOnSideAttribute.cs | 79 ++++++ .../Fixtures/Attributes/Side.cs | 27 ++ .../Attributes/SidedDependencyAttribute.cs | 23 ++ .../Attributes/TrackingIssueAttribute.cs | 53 ++++ .../Fixtures/GameTest.CVars.cs | 83 ++++++ .../Fixtures/GameTest.CommonPoolSettings.cs | 12 + .../Fixtures/GameTest.Entities.cs | 228 +++++++++++++++ .../Fixtures/GameTest.Pair.cs | 36 +++ Content.IntegrationTests/Fixtures/GameTest.cs | 265 ++++++++++++++++++ .../NUnit/Constraints/CompConstraint.cs | 33 +++ .../Constraints/CompConstraintExtensions.cs | 48 ++++ .../NUnit/Constraints/CompExistsConstraint.cs | 30 ++ .../NUnit/Constraints/ConstraintHelpers.cs | 66 +++++ .../NUnit/Constraints/LifeStageConstraint.cs | 136 +++++++++ .../NUnit/Operators/CompOperator.cs | 31 ++ .../NUnit/Utilities/IResolvesToEntity.cs | 19 ++ .../NUnit/Utilities/ITestExtensions.cs | 28 ++ .../Tests/GameTestTests/ContraintTests.cs | 86 ++++++ .../Tests/GameTestTests/DependencyTests.cs | 54 ++++ .../DisconnectedDependencyTest.cs | 46 +++ .../Tests/GameTestTests/EnsureCVarTest.cs | 53 ++++ .../GameTestTests/EnsureCVarsTestCVars.cs | 14 + ...ameTestPairConfigModifierAttributeTests.cs | 23 ++ .../Tests/GameTestTests/RunOnSideTests.cs | 43 +++ .../Utility/TestProperties.cs | 9 + 29 files changed, 1697 insertions(+) create mode 100644 Content.IntegrationTests/Fixtures/Attributes/EnsureCVarAttribute.cs create mode 100644 Content.IntegrationTests/Fixtures/Attributes/IGameTestModifier.cs create mode 100644 Content.IntegrationTests/Fixtures/Attributes/IGameTestPairConfigModifier.cs create mode 100644 Content.IntegrationTests/Fixtures/Attributes/PairConfigAttribute.cs create mode 100644 Content.IntegrationTests/Fixtures/Attributes/RunOnSideAttribute.cs create mode 100644 Content.IntegrationTests/Fixtures/Attributes/Side.cs create mode 100644 Content.IntegrationTests/Fixtures/Attributes/SidedDependencyAttribute.cs create mode 100644 Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs create mode 100644 Content.IntegrationTests/Fixtures/GameTest.CVars.cs create mode 100644 Content.IntegrationTests/Fixtures/GameTest.CommonPoolSettings.cs create mode 100644 Content.IntegrationTests/Fixtures/GameTest.Entities.cs create mode 100644 Content.IntegrationTests/Fixtures/GameTest.Pair.cs create mode 100644 Content.IntegrationTests/Fixtures/GameTest.cs create mode 100644 Content.IntegrationTests/NUnit/Constraints/CompConstraint.cs create mode 100644 Content.IntegrationTests/NUnit/Constraints/CompConstraintExtensions.cs create mode 100644 Content.IntegrationTests/NUnit/Constraints/CompExistsConstraint.cs create mode 100644 Content.IntegrationTests/NUnit/Constraints/ConstraintHelpers.cs create mode 100644 Content.IntegrationTests/NUnit/Constraints/LifeStageConstraint.cs create mode 100644 Content.IntegrationTests/NUnit/Operators/CompOperator.cs create mode 100644 Content.IntegrationTests/NUnit/Utilities/IResolvesToEntity.cs create mode 100644 Content.IntegrationTests/NUnit/Utilities/ITestExtensions.cs create mode 100644 Content.IntegrationTests/Tests/GameTestTests/ContraintTests.cs create mode 100644 Content.IntegrationTests/Tests/GameTestTests/DependencyTests.cs create mode 100644 Content.IntegrationTests/Tests/GameTestTests/DisconnectedDependencyTest.cs create mode 100644 Content.IntegrationTests/Tests/GameTestTests/EnsureCVarTest.cs create mode 100644 Content.IntegrationTests/Tests/GameTestTests/EnsureCVarsTestCVars.cs create mode 100644 Content.IntegrationTests/Tests/GameTestTests/IGameTestPairConfigModifierAttributeTests.cs create mode 100644 Content.IntegrationTests/Tests/GameTestTests/RunOnSideTests.cs create mode 100644 Content.IntegrationTests/Utility/TestProperties.cs diff --git a/Content.IntegrationTests/Fixtures/Attributes/EnsureCVarAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/EnsureCVarAttribute.cs new file mode 100644 index 0000000000..50da744ecf --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/EnsureCVarAttribute.cs @@ -0,0 +1,74 @@ +#nullable enable +using System.Reflection; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using Robust.Shared.Configuration; + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Ensures the given CVar, on the given side (or both), is the given value. +/// Attribute version of , and stores the old value the same way. +/// +/// This only works with fixtures. +/// The side to set the CVar on, or both. +/// The type the CVar is defined on. +/// The name of the static field defining the CVar. +/// The value to set the CVar to. +/// +/// +/// [Test] +/// [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.FlavorText), true)] +/// public async Task MyTest() +/// { +/// // CVar is set for you inside the test, and automatically un-set on teardown. +/// } +/// +/// +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] +public sealed class EnsureCVarAttribute(Side side, Type definitionType, string fieldName, object value) : Attribute, IGameTestModifier, IApplyToTest +{ + public const string ClientEnsuredCVarsProperty = "ClientEnsuredCVars"; + public const string ServerEnsuredCVarsProperty = "ServerEnsuredCVars"; + + Task IGameTestModifier.ApplyToTest(GameTest test) + { + var cvar = LookupCVar(); + + test.PreTestAddOverride(side, cvar.Name, value); + + return Task.CompletedTask; + } + + private CVarDef LookupCVar() + { + var field = definitionType.GetField(fieldName, BindingFlags.Static | BindingFlags.Public); + if (field is null) + throw new ArgumentException($"Couldn't find a public, static field named {fieldName} on {definitionType}"); + + var obj = field.GetValue(null); + + if (obj is not CVarDef cvar) + { + throw new ArgumentException( + $"Expected a CVar definition on {definitionType}.{fieldName}, but it was a {obj?.GetType().FullName ?? "null"}"); + } + + if (value.GetType() != cvar.DefaultValue.GetType()) + throw new NotSupportedException($"Cannot set {cvar.Name} to {value}, it's the wrong type."); + + return cvar; + } + + void IApplyToTest.ApplyToTest(Test test) + { + var cvar = LookupCVar(); + + if ((side & Side.Client) != 0) + test.Properties.Add(ClientEnsuredCVarsProperty, $"{cvar.Name} = {value}"); + + if ((side & Side.Server) != 0) + test.Properties.Add(ServerEnsuredCVarsProperty, $"{cvar.Name} = {value}"); + } +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/IGameTestModifier.cs b/Content.IntegrationTests/Fixtures/Attributes/IGameTestModifier.cs new file mode 100644 index 0000000000..70cd904f3f --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/IGameTestModifier.cs @@ -0,0 +1,20 @@ +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Marks an attribute as a modifier for fixtures. +/// These attributes can be applied to both test methods and fixtures. +/// +/// +/// GameTest modifiers are encouraged to also implement IApplyToTest and add properties to the test +/// indicating their presence. +/// +public interface IGameTestModifier +{ + /// + /// Method called by GameTest on itself when applying modifiers. + /// + /// The test being modified + /// Async task to await. + Task ApplyToTest(GameTest test); +} + diff --git a/Content.IntegrationTests/Fixtures/Attributes/IGameTestPairConfigModifier.cs b/Content.IntegrationTests/Fixtures/Attributes/IGameTestPairConfigModifier.cs new file mode 100644 index 0000000000..e47b99b146 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/IGameTestPairConfigModifier.cs @@ -0,0 +1,25 @@ +#nullable enable + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Interface used for pair configuration attributes. +/// This allows such attributes to modify the pair settings, and also describe what parts of pairs they modify +/// so odd configuration choices can be spotted. +/// +public interface IGameTestPairConfigModifier +{ + /// + /// Whether this modifier is exclusive and should conflict with other exclusive modifiers. + /// Essentially, fail immediately if other IGameTestPairConfigModifier attributes are present if this is set. + /// + bool Exclusive { get; } + + /// + /// Called when GameTest needs its modified by the modifier. + /// + /// The test we're applying to. + /// The settings object to modify. + void ApplyToPairSettings(GameTest test, ref PoolSettings settings); +} + diff --git a/Content.IntegrationTests/Fixtures/Attributes/PairConfigAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/PairConfigAttribute.cs new file mode 100644 index 0000000000..d197539997 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/PairConfigAttribute.cs @@ -0,0 +1,53 @@ +#nullable enable +using System.Reflection; + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Configures the test pair using settings from the given type (by default the current test) and static property member. +/// +/// The type to look up the member on, if any. +/// The static property to read the settings from. +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +public sealed class PairConfigAttribute(Type? sourceType, string sourceMember) : Attribute, IGameTestPairConfigModifier +{ + public bool Exclusive => true; + + public readonly Type? SourceType = sourceType; + public readonly string SourceMember = sourceMember; + + private const BindingFlags PropertyBindingFlags = BindingFlags.Static + | BindingFlags.Public + | BindingFlags.NonPublic + | BindingFlags.FlattenHierarchy; + + public PairConfigAttribute(string sourceMember) : this(null, sourceMember) + { + } + + public void ApplyToPairSettings(GameTest test, ref PoolSettings settings) + { + var sourceType = SourceType ?? test.GetType(); + + var property = sourceType.GetProperty(SourceMember, PropertyBindingFlags); + + if (property is null) + { + if (sourceType.GetField(SourceMember, PropertyBindingFlags) is not null) + { + throw new ArgumentException( + $"Couldn't find static property {SourceMember} on {sourceType.Name}, but could find a field. Only properties are allowed."); + } + + throw new ArgumentException($"Couldn't find static property {SourceMember} on {sourceType.Name}"); + } + + if (!property.PropertyType.IsAssignableTo(typeof(PoolSettings))) + { + throw new ArgumentException( + $"{sourceType.Name}.{SourceMember} is not assignable to {nameof(PoolSettings)} and cannot be used."); + } + + settings = (PoolSettings)property.GetValue(null)!; + } +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/RunOnSideAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/RunOnSideAttribute.cs new file mode 100644 index 0000000000..75a22e9334 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/RunOnSideAttribute.cs @@ -0,0 +1,79 @@ +#nullable enable +using Content.IntegrationTests.NUnit.Utilities; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using NUnit.Framework.Internal.Commands; + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Ensures a test method runs on the given side (client or server, not neither nor both). +/// +/// +/// This only works for fixtures. +/// +/// +[AttributeUsage(AttributeTargets.Method)] +public sealed class RunOnSideAttribute : Attribute, IWrapTestMethod, IImplyFixture, IApplyToTest +{ + public const string RunOnSideProperty = "RanOnSide"; + + /// + /// Which side to run the inner test code on, if not the test thread. + /// + public Side RunOnSide { get; } + + public RunOnSideAttribute(Side side) + { + RunOnSide = side; + if (side is not Side.Client and not Side.Server) + throw new NotSupportedException("Test run-on-side can only be the client or server, not both or neither."); + } + + TestCommand ICommandWrapper.Wrap(TestCommand command) + { + return new SidedTestCommand(command, RunOnSide); + } + + private sealed class SidedTestCommand : DelegatingTestCommand + { + private readonly Side _side; + + public SidedTestCommand(TestCommand inner, Side side) : base(inner) + { + _side = side; + } + + public override TestResult Execute(TestExecutionContext context) + { + innerCommand.Test.EnsureFixtureIsGameTest(typeof(RunOnSideAttribute), out var gt); + + if (_side is not Side.Client and not Side.Server) + throw new NotSupportedException($"Sided tests need to specify a specific side. {Test}"); + + if (_side is Side.Client) + { + gt.Client.WaitAssertion(() => + { + context.CurrentResult = innerCommand.Execute(context); + }) + .Wait(); + } + else + { + gt.Server.WaitAssertion(() => + { + context.CurrentResult = innerCommand.Execute(context); + }) + .Wait(); + } + + return context.CurrentResult; + } + } + + public void ApplyToTest(Test test) + { + test.Properties.Add(RunOnSideProperty, RunOnSide.ToString()); + } +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/Side.cs b/Content.IntegrationTests/Fixtures/Attributes/Side.cs new file mode 100644 index 0000000000..06f1e90acb --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/Side.cs @@ -0,0 +1,27 @@ +#nullable enable +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// A flag enum representing a side of a testpair. +/// +[Flags] +public enum Side : byte +{ + /// + /// Bitflag representing the client side of a testpair. + /// + Client = 1, + /// + /// Bitflag representing the server side of a testpair. + /// + Server = 2, + + /// + /// A value indicating no side was specified. You shouldn't use this outside of checking for it as an error. + /// + Neither = 0, + /// + /// A value indicating both sides were specified. + /// + Both = Client | Server, +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/SidedDependencyAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/SidedDependencyAttribute.cs new file mode 100644 index 0000000000..00fe7a952f --- /dev/null +++ b/Content.IntegrationTests/Fixtures/Attributes/SidedDependencyAttribute.cs @@ -0,0 +1,23 @@ +#nullable enable + +namespace Content.IntegrationTests.Fixtures.Attributes; + +/// +/// Marks a field on a fixture as needing to be populated with an IoC dependency from the given side. +/// +/// +[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] +public sealed class SidedDependencyAttribute : Attribute +{ + public SidedDependencyAttribute(Side side) + { + Side = side; + + if (side is not Side.Client and not Side.Server) + { + throw new NotSupportedException($"Expected either the client or the server as a side, got {side}."); + } + } + + public Side Side { get; } +} diff --git a/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs new file mode 100644 index 0000000000..02f9e4f7e4 --- /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\-\$\#]*\/[a-z\-\$\#]*\/(issues|pulls)\/\d*$", + RegexOptions.Compiled | RegexOptions.NonBacktracking | RegexOptions.IgnoreCase); + + public TrackingIssueAttribute([StringSyntax(StringSyntaxAttribute.Uri)] string url) : base(url) + { + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) + throw new ArgumentException($"Expected a valid URL for {nameof(TrackingIssueAttribute)}, got {url}"); + + // Assert the domain is reasonable. + if (!_validDomains.Contains(uri.Host, StringComparer.InvariantCultureIgnoreCase)) + { + throw new ArgumentException( + $"Didn't recognize the domain used for the tracking issue, got {uri.Host}. We support: {string.Join(", ", _validDomains)}"); + } + + // Assert that the URL is reasonable. + if (!GithubStyleIssueMatch.IsMatch(uri.AbsolutePath)) + { + throw new ArgumentException( + $"Didn't recognize the provided github link, it should point to a specific pull request or issue. Got {uri.AbsolutePath}"); + } + } +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.CVars.cs b/Content.IntegrationTests/Fixtures/GameTest.CVars.cs new file mode 100644 index 0000000000..6ecde6fb6d --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.CVars.cs @@ -0,0 +1,83 @@ +#nullable enable +using System.Collections.Generic; +using Content.IntegrationTests.Fixtures.Attributes; +using Robust.Shared.Configuration; + +namespace Content.IntegrationTests.Fixtures; + +// REMARK: You may be wondering why this doesn't bother storing the old CVars. +// This is because TestPair actually has some not-well-known functionality to +// automatically restore CVars to what they were pre-test for you. +// +// So instead of rolling that twice, this lets TestPair handle it. +public abstract partial class GameTest +{ + [SidedDependency(Side.Server)] private readonly IConfigurationManager _serverCfg = default!; + [SidedDependency(Side.Client)] private readonly IConfigurationManager _clientCfg = default!; + + private readonly Dictionary _clientCVarOverrides = new(); + private readonly Dictionary _serverCVarOverrides = new(); + + /// + /// Adds a setup-time override for a given cvar, for use by s. + /// + public void PreTestAddOverride(Side side, string cVar, object value) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (_setupDone) + throw new NotSupportedException("Cannot use PreTest functions after test SetUp."); + + if (side is Side.Neither) + throw new NotSupportedException($"Must specify a side, or both, for {nameof(PreTestAddOverride)}"); + + if ((side & Side.Server) != 0) + _serverCVarOverrides.Add(cVar, value); + + if ((side & Side.Client) != 0) + _clientCVarOverrides.Add(cVar, value); + } + + private async Task DoPreTestOverrides() + { + foreach (var (cvar, value) in _clientCVarOverrides) + { + await OverrideCVarByName(Side.Client, cvar, value, false); + } + + foreach (var (cvar, value) in _serverCVarOverrides) + { + await OverrideCVarByName(Side.Server, cvar, value, false); + } + + await Pair.RunUntilSynced(); + } + + /// + /// Sets a given CVar for the provided side. + /// + /// Does its own cleanup, you do not need to set the CVar back yourself. + public async Task OverrideCVar(Side side, CVarDef cvar, T value, bool sync = true) + where T: notnull + { + await OverrideCVarByName(side, cvar.Name, value, sync); + } + + private async Task OverrideCVarByName(Side side, string cVar, object value, bool sync) + { + if (side is Side.Client) + { + _clientCfg.SetCVar(cVar, value); + } + else if (side is Side.Server) + { + _serverCfg.SetCVar(cVar, value); + } + else + { + throw new NotSupportedException($"Expected a specific side, got {side}."); + } + + if (sync) + await Pair.RunUntilSynced(); + } +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.CommonPoolSettings.cs b/Content.IntegrationTests/Fixtures/GameTest.CommonPoolSettings.cs new file mode 100644 index 0000000000..c6525192c5 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.CommonPoolSettings.cs @@ -0,0 +1,12 @@ +namespace Content.IntegrationTests.Fixtures; + +public abstract partial class GameTest +{ + /// + /// All-default-settings PoolSettings, with the client and server disconnected. + /// + protected static PoolSettings PsDisconnected => new() + { + Connected = false, + }; +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.Entities.cs b/Content.IntegrationTests/Fixtures/GameTest.Entities.cs new file mode 100644 index 0000000000..2afdeec8ef --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.Entities.cs @@ -0,0 +1,228 @@ +#nullable enable +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; + +namespace Content.IntegrationTests.Fixtures; + +public abstract partial class GameTest +{ + /// + /// Contains all server entities spawned using GameTest proxy methods. + /// + private readonly List _serverEntitiesToClean = new(); + + /// + /// Contains all client entities spawned using GameTest proxy methods. + /// + private readonly List _clientEntitiesToClean = new(); + + private async Task CleanUpEntities() + { + await Task.WhenAll( + Server.WaitAssertion(() => + { + foreach (var junk in _serverEntitiesToClean) + { + if (!SEntMan.Deleted(junk)) + SEntMan.DeleteEntity(junk); + } + }), + Client.WaitAssertion(() => + { + foreach (var junk in _clientEntitiesToClean) + { + if (!CEntMan.Deleted(junk)) + CEntMan.DeleteEntity(junk); + } + }) + ); + } + + /// + /// Returns a string representation of an entity for the server. + /// + public string SToPrettyString(EntityUid uid) + { + return Pair.Server.EntMan.ToPrettyString(uid); + } + + /// + /// Returns a string representation of an entity for the client. + /// + public string CToPrettyString(EntityUid uid) + { + return Pair.Client.EntMan.ToPrettyString(uid); + } + + /// + /// Converts a server EntityUid into the client-side equivalent entity. + /// + public EntityUid ToClientUid(EntityUid serverUid) + { + return Pair.ToClientUid(serverUid); + } + + /// + /// Converts a client EntityUid into the server-side equivalent entity. + /// + public EntityUid ToServerUid(EntityUid clientUid) + { + return Pair.ToServerUid(clientUid); + } + + /// + /// Retrieves the given component from an entity on the server. + /// + public T SComp(EntityUid target) + where T : IComponent + { + return SEntMan.GetComponent(target); + } + + /// + /// Attempts to retrieve the given component from an entity on the server. + /// + public bool STryComp(EntityUid? target, [NotNullWhen(true)] out T? component) + where T : IComponent + { + return SEntMan.TryGetComponent(target, out component); + } + + /// + /// Retrieves the given component from an entity on the client. + /// + public T CComp(EntityUid target) + where T : IComponent + { + return CEntMan.GetComponent(target); + } + + /// + /// Attempts to retrieve the given component from an entity on the server. + /// + public bool CTryComp(EntityUid? target, [NotNullWhen(true)] out T? component) + where T : IComponent + { + return SEntMan.TryGetComponent(target, out component); + } + + /// + /// Pairs an EntityUid with the given component, from the server. + /// + public Entity SEntity(EntityUid target) + where T : IComponent + { + return new(target, SEntMan.GetComponent(target)); + } + + /// + /// Pairs an EntityUid with the given component, from the client. + /// + public Entity CEntity(EntityUid target) + where T : IComponent + { + return new(target, CEntMan.GetComponent(target)); + } + + /// + /// Spawns an entity on the server. + /// + /// This tracks the entity for post-test cleanup. + public EntityUid SSpawn(string? id) + { + var res = SEntMan.Spawn(id); + _serverEntitiesToClean.Add(res); + return res; + } + + /// + /// Spawns an entity on the server at a location. + /// + /// This tracks the entity for post-test cleanup. + public EntityUid SSpawnAtPosition(string? id, EntityCoordinates coordinates) + { + var res = SEntMan.SpawnAtPosition(id, coordinates); + _serverEntitiesToClean.Add(res); + return res; + } + + /// + /// Spawns an entity on the client. + /// + /// This tracks the entity for post-test cleanup. + public EntityUid CSpawn(string? id) + { + var res = CEntMan.Spawn(id); + _clientEntitiesToClean.Add(res); + return res; + } + + /// + /// Spawns an entity on the server at a location. + /// + /// This tracks the entity for post-test cleanup. + public EntityUid CSpawnAtPosition(string? id, EntityCoordinates coordinates) + { + var res = CEntMan.SpawnAtPosition(id, coordinates); + _clientEntitiesToClean.Add(res); + return res; + } + + /// + /// Asynchronously spawns an entity on the server. + /// + public async Task Spawn(string? id) + { + var ent = EntityUid.Invalid; + + await Server.WaitPost(() => ent = SSpawn(id)); + + return ent; + } + + /// + /// Asynchronously spawns an entity on the server at the given position. + /// + public async Task SpawnAtPosition(string? id, EntityCoordinates coords) + { + var ent = EntityUid.Invalid; + + await Server.WaitPost(() => ent = SSpawnAtPosition(id, coords)); + + return ent; + } + + /// + /// Deletes an entity on the server immediately. + /// + public void SDeleteNow(EntityUid id) + { + SEntMan.DeleteEntity(id); + } + + /// + /// Deletes an entity on the client immediately. + /// + public void CDeleteNow(EntityUid id) + { + CEntMan.DeleteEntity(id); + } + + /// + /// Queues an entity for deletion at the end of the tick on the server. + /// + public void SQueueDel(EntityUid id) + { + SEntMan.QueueDeleteEntity(id); + } + + /// + /// Queues an entity for deletion at the end of the tick on the client. + /// + public void CQueueDel(EntityUid id) + { + CEntMan.QueueDeleteEntity(id); + } +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.Pair.cs b/Content.IntegrationTests/Fixtures/GameTest.Pair.cs new file mode 100644 index 0000000000..8d3a88bb7c --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.Pair.cs @@ -0,0 +1,36 @@ +namespace Content.IntegrationTests.Fixtures; + +public abstract partial class GameTest +{ + /// + /// Runs the client and server for the given number of ticks, in lockstep. + /// + /// + /// Do not use this as a barrier for client-server synchronization, use . + /// + public Task RunTicksSync(int ticks) + { + return Pair.RunTicksSync(ticks); + } + + /// + /// Runs the pairs just long enough for PVS to send entities, ensuring the client's current tick is what the + /// server's was at call time. + /// + public async Task RunUntilSynced() + { + await Pair.RunUntilSynced(); + } + + /// + /// Runs the test pair for a number of (simulated) seconds. + /// + /// + /// Does not actually take N seconds to evaluate, the game ticks as fast as possible. + /// Do not use this as a barrier for client-server synchronization, use . + /// + public Task RunSeconds(float seconds) + { + return Pair.RunSeconds(seconds); + } +} diff --git a/Content.IntegrationTests/Fixtures/GameTest.cs b/Content.IntegrationTests/Fixtures/GameTest.cs new file mode 100644 index 0000000000..55903cf4a2 --- /dev/null +++ b/Content.IntegrationTests/Fixtures/GameTest.cs @@ -0,0 +1,265 @@ +#nullable enable +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Threading; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.IntegrationTests.NUnit.Constraints; +using Content.IntegrationTests.Pair; +using Content.IntegrationTests.Utility; +using NUnit.Framework.Interfaces; +using Robust.Client.Timing; +using Robust.Shared.GameObjects; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Robust.Shared.Utility; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.Fixtures; + +/// +/// +/// A test fixture with an integrated test pair, +/// proxy methods for efficient test writing, utilities for ensuring tests clean up correctly, +/// and dependency injection (). +/// +/// +/// Tests using GameTest support some additional class and method level attributes, namely +/// . +/// Attributes can be used to control how the test runs. +/// +/// +/// +/// +[TestFixture] +[FixtureLifeCycle(LifeCycle.InstancePerTestCase)] +[Property(TestProperties.TestFrameKind, nameof(GameTest))] +[SuppressMessage("Structure", "NUnit1028:The non-test method is public")] +public abstract partial class GameTest +{ + /// + /// Set if the test manually marks itself dirty. + /// + private bool _pairDestroyed; + + /// + /// Tests-testing-tests assistant to run right before the pair is returned. + /// + public event Action? PreFinalizeHook; + + /// + /// The main thread of the game server. + /// + public Thread ServerThread { get; private set; } = null!; // NULLABILITY: This is always set during test setup. + /// + /// The main thread of the game client. + /// + public Thread ClientThread { get; private set; } = null!; // NULLABILITY: This is always set during test setup. + + /// + /// Settings for the client/server pair. + /// By default, this gets you a client and server that have connected together. + /// + /// + /// Always return a new instance whenever this is read. In other words, no backing field please. Arrow syntax only. + /// + public virtual PoolSettings PoolSettings => new() { Connected = true }; + + /// + /// The client and server pair. + /// + public TestPair Pair { get; private set; } = default!; // NULLABILITY: This is always set during test setup. + + /// + /// The game server instance. + /// + public RobustIntegrationTest.ServerIntegrationInstance Server => Pair.Server; + + /// + /// The game client instance. + /// + public RobustIntegrationTest.ClientIntegrationInstance Client => Pair.Client; + + /// + /// The test player's server session, if any. + /// + public ICommonSession? ServerSession => Pair.Player; + + /// + /// The server-side entity manager. + /// + [SidedDependency(Side.Server)] + public IEntityManager SEntMan = null!; + + /// + /// The client-side entity manager. + /// + [SidedDependency(Side.Client)] + public IEntityManager CEntMan = null!; + + /// + /// The server-side prototype manager. + /// + [SidedDependency(Side.Server)] + public IPrototypeManager SProtoMan = null!; + + /// + /// The client-side prototype manager. + /// + [SidedDependency(Side.Client)] + public IPrototypeManager CProtoMan = null!; + + /// + /// The server-side game-timing manager. + /// + [SidedDependency(Side.Server)] + public IGameTiming SGameTiming = null!; + + /// + /// The client-side game-timing manager. + /// + [SidedDependency(Side.Client)] + public IClientGameTiming CGameTiming = null!; + + /// + /// The test map we're using, if any. + /// + public TestMapData? TestMap => Pair.TestMap; + + private bool _setupDone = false; + + /// + /// Primary setup task for the fixture. + /// Custom setup must run after this. + /// + [SetUp] + public virtual async Task DoSetup() + { + _pairDestroyed = false; + var testContext = TestContext.CurrentContext; + + + var test = testContext.Test; + + var settings = PoolSettings; + + var pairAttribs = test.Method!.GetCustomAttributes(false); + var pairSuiteAttribs = test.Method!.TypeInfo.GetCustomAttributes(true); + + if (pairAttribs.Length > 1 && pairAttribs.Any(x => x.Exclusive)) + { + throw new InvalidOperationException( + "More than one exclusive pair config attribute is present on the test member."); + } + + if (pairSuiteAttribs.Length > 1 && pairSuiteAttribs.Any(x => x.Exclusive)) + { + throw new InvalidOperationException( + "More than one exclusive pair config attribute is present on the test fixture."); + } + + foreach (var attribute in pairSuiteAttribs.Concat(pairAttribs)) + { + attribute.ApplyToPairSettings(this, ref settings); + } + + Pair = await PoolManager.GetServerClient(settings, new NUnitTestContextWrap(testContext, TestContext.Out)); + + Task.WaitAll( + Server.WaitPost(() => ServerThread = Thread.CurrentThread), + Client.WaitPost(() => ClientThread = Thread.CurrentThread) + ); + + await Pair.ReallyBeIdle(5); // Arbitrary setup time wait. + + InjectDependencies(this); + + var attribs = test.Method!.GetCustomAttributes(false); + var suiteAttribs = test.Method!.TypeInfo.GetCustomAttributes(true); + + foreach (var attribute in suiteAttribs.Concat(attribs)) + { + await attribute.ApplyToTest(this); + } + + _setupDone = true; + + await DoPreTestOverrides(); + + await Pair.RunUntilSynced(); + } + + /// + /// Injects dependencies into the target object. + /// + /// + /// This is called on the GameTest itself automatically. Don't call it twice on the same object. + /// + /// The object to inject into. + public void InjectDependencies(object target) + { + foreach (var field in target.GetType().GetAllFields()) + { + if (field.GetCustomAttribute() is { } depAttrib) + { + // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression + if (depAttrib.Side is Side.Server) + { + field.SetValue(target, Server.EntMan.EntitySysManager.DependencyCollection.ResolveType(field.FieldType)); + } + else + { + // Must be initially connected for this... + if (Client.Session is not null) + field.SetValue(target, Client.EntMan.EntitySysManager.DependencyCollection.ResolveType(field.FieldType)); + else + field.SetValue(target, Client.InstanceDependencyCollection.ResolveType(field.FieldType)); + } + } + } + } + + /// + /// Primary teardown task for the fixture. + /// Custom teardown must run before this. + /// + [TearDown] + public virtual async Task DoTeardown() + { + try + { + // In some cool future we might be able to make this only throw out the pair + // if the test threw exceptions. But that'd require fixing all of them to do cleanup properly on failure. + // + // So not yet. + if (TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Failed) + { + _pairDestroyed = true; // Blow it up, we failed and it might be screwed. + return; + } + + // Roll forward til sync for teardown. + await Pair.RunUntilSynced(); + + await CleanUpEntities(); + + // And other teardown logic will go here. Eventually. + + } + catch (Exception) + { + _pairDestroyed = true; + throw; + } + finally + { + PreFinalizeHook?.Invoke(); + + if (!_pairDestroyed) + await Pair.CleanReturnAsync(); + else + await Pair.DisposeAsync(); + } + } +} diff --git a/Content.IntegrationTests/NUnit/Constraints/CompConstraint.cs b/Content.IntegrationTests/NUnit/Constraints/CompConstraint.cs new file mode 100644 index 0000000000..c474d0c2d5 --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/CompConstraint.cs @@ -0,0 +1,33 @@ +using NUnit.Framework.Constraints; +using NUnit.Framework.Internal; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +/// +/// A prefix constraint like , for entity components. +/// +/// +public sealed class CompConstraint(Type tComp, IIntegrationInstance instance, IConstraint baseConstraint) + : PrefixConstraint(baseConstraint, $"component {tComp.Name}") +{ + public override ConstraintResult ApplyTo(TActual actual) + { + if (!ConstraintHelpers.TryActualAsEnt(actual, instance, out var ent, out var error)) + { + if (error) + { + throw new NotImplementedException( + $"The input type {typeof(TActual)} to {nameof(CompExistsConstraint)} is not a supported entity id."); + } + + return new ConstraintResult(this, actual, ConstraintStatus.Failure); + } + + if (!instance.EntMan.TryGetComponent(ent, tComp, out var comp)) + return new ConstraintResult(this, actual, ConstraintStatus.Failure); + + var baseResult = Reflect.InvokeApplyTo(constraint: baseConstraint, tComp, comp); + return new ConstraintResult(this, baseResult.ActualValue, baseResult.Status); + } +} diff --git a/Content.IntegrationTests/NUnit/Constraints/CompConstraintExtensions.cs b/Content.IntegrationTests/NUnit/Constraints/CompConstraintExtensions.cs new file mode 100644 index 0000000000..1586c52e03 --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/CompConstraintExtensions.cs @@ -0,0 +1,48 @@ +#nullable enable +using Content.IntegrationTests.NUnit.Operators; +using NUnit.Framework.Constraints; +using Robust.Shared.GameObjects; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +/// +/// Provides Has.Comp<T>(side), +/// a constraint that allows you to check for the presence of, or operate on, a component. +/// +/// +/// +/// // Assert that the server sided entity myEntity has ItemComponent on the server. +/// Assert.That(myEntity, Has.Comp<ItemComponent>(Server)); +/// +/// +public static class CompConstraintExtensions +{ + extension(Has) + { + public static ResolvableConstraintExpression Comp(IIntegrationInstance instance) + where T : IComponent + { + return new ConstraintExpression().Comp(instance); + } + + public static ResolvableConstraintExpression Comp(Type t, IIntegrationInstance instance) + { + return new ConstraintExpression().Comp(t, instance); + } + } + + extension(ConstraintExpression expr) + { + public ResolvableConstraintExpression Comp(IIntegrationInstance instance) + where T : IComponent + { + return expr.Append(new CompOperator(typeof(T), instance)); + } + + public ResolvableConstraintExpression Comp(Type t, IIntegrationInstance instance) + { + return expr.Append(new CompOperator(t, instance)); + } + } +} diff --git a/Content.IntegrationTests/NUnit/Constraints/CompExistsConstraint.cs b/Content.IntegrationTests/NUnit/Constraints/CompExistsConstraint.cs new file mode 100644 index 0000000000..eaff1eae8a --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/CompExistsConstraint.cs @@ -0,0 +1,30 @@ +#nullable enable +using NUnit.Framework.Constraints; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +/// +/// Constraint for whether a component exists. +/// +/// +public sealed class CompExistsConstraint(Type component, IIntegrationInstance instance) : Constraint +{ + public override ConstraintResult ApplyTo(TActual actual) + { + if (!ConstraintHelpers.TryActualAsEnt(actual, instance, out var ent, out var error)) + { + if (error) + { + throw new NotImplementedException( + $"The input type {typeof(TActual)} to {nameof(CompExistsConstraint)} is not a supported entity id."); + } + + return new ConstraintResult(this, actual, ConstraintStatus.Failure); + } + + return new ConstraintResult(this, actual, instance.EntMan.HasComponent(ent, component)); + } + + public override string Description => $"has the component {component.Name}"; +} diff --git a/Content.IntegrationTests/NUnit/Constraints/ConstraintHelpers.cs b/Content.IntegrationTests/NUnit/Constraints/ConstraintHelpers.cs new file mode 100644 index 0000000000..f7f810eda4 --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/ConstraintHelpers.cs @@ -0,0 +1,66 @@ +#nullable enable +using System.Diagnostics.CodeAnalysis; +using Content.IntegrationTests.NUnit.Utilities; +using Robust.Shared.GameObjects; +using Robust.Shared.Toolshed.TypeParsers; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +public static class ConstraintHelpers +{ + /// + /// A constraint implementation helper to convert TActual into an entityuid. + /// + /// The input value to try to get an entity uid from. + /// The integration test instance to resolve the entity from. + /// The resulting entity uid. + /// Whether TActual is recognized to begin with. + /// The type to cast out of. + public static bool TryActualAsEnt(TActual t, IIntegrationInstance instance, [NotNullWhen(true)] out EntityUid? ent, out bool validType) + { + if (t is EntityUid u) + { + ent = u; + validType = false; + return true; + } + + if (t is IAsType asTy) + { + ent = asTy.AsType(); + validType = false; + return true; + } + + if (t is IResolvesToEntity resolvable) + { + if (instance is IServerIntegrationInstance) + { + ent = resolvable.SEntity; + } + else if (instance is IClientIntegrationInstance) + { + ent = resolvable.CEntity; + } + else + { + throw new NotSupportedException($"{t.GetType()} is not a valid kind of IIntegrationInstance"); + } + + validType = false; + return ent is not null; + } + + if (t is null) + { + ent = null; + validType = false; + return false; + } + + ent = null; + validType = true; // Dunno what this type is! + return false; + } +} diff --git a/Content.IntegrationTests/NUnit/Constraints/LifeStageConstraint.cs b/Content.IntegrationTests/NUnit/Constraints/LifeStageConstraint.cs new file mode 100644 index 0000000000..0539a6b54e --- /dev/null +++ b/Content.IntegrationTests/NUnit/Constraints/LifeStageConstraint.cs @@ -0,0 +1,136 @@ +#nullable enable +using NUnit.Framework.Constraints; +using Robust.Shared.GameObjects; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Constraints; + +/// +/// A constraint for an entity's lifestage. +/// +/// +public sealed class LifeStageConstraint(EntityLifeStage stage, IIntegrationInstance instance) : Constraint +{ + public override ConstraintResult ApplyTo(TActual actual) + { + if (!ConstraintHelpers.TryActualAsEnt(actual, instance, out var ent, out var error)) + { + if (error) + { + throw new NotImplementedException( + $"The input type {typeof(TActual)} to {nameof(CompExistsConstraint)} is not a supported entity id."); + } + + return new ConstraintResult(this, actual, ConstraintStatus.Failure); + } + + var lifestage = instance.EntMan.GetComponentOrNull(ent.Value)?.EntityLifeStage; + + return new ConstraintResult(this, + lifestage, + lifestage == stage || (lifestage is null && stage is EntityLifeStage.Deleted)); + } + + public override string Description => stage switch + { + EntityLifeStage.PreInit => "preinitialized", + EntityLifeStage.Initializing => "initializing", + EntityLifeStage.Initialized => "initialized", + EntityLifeStage.MapInitialized => "map initialized", + EntityLifeStage.Terminating => "terminating", + EntityLifeStage.Deleted => "deleted", + _ => throw new ArgumentOutOfRangeException(nameof(stage), stage, null), + }; +} + +/// +/// Provides constraints for testing if an entity is in the given lifestage. +/// +/// +/// +/// // Assert that the server sided entity myEntity is MapInitialized. +/// Assert.That(myEntity, Is.MapInitialized(Server)); +/// +/// +public static class LifeStageConstraintExtensions +{ + extension(Is) + { + public static LifeStageConstraint LifeStage(EntityLifeStage stage, IIntegrationInstance instance) + { + return new LifeStageConstraint(stage, instance); + } + + public static LifeStageConstraint PreInit(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.PreInit, instance); + } + + public static LifeStageConstraint Initializing(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.Initializing, instance); + } + + public static LifeStageConstraint Initialized(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.Initialized, instance); + } + + public static LifeStageConstraint MapInitialized(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.MapInitialized, instance); + } + + public static LifeStageConstraint Terminating(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.Terminating, instance); + } + + public static LifeStageConstraint Deleted(IIntegrationInstance instance) + { + return Is.LifeStage(EntityLifeStage.Deleted, instance); + } + } + + extension(ConstraintExpression expr) + { + public LifeStageConstraint LifeStage(EntityLifeStage stage, IIntegrationInstance instance) + { + var c = new LifeStageConstraint(stage, instance); + + expr.Append(c); + + return c; + } + + public LifeStageConstraint PreInit(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.PreInit, instance); + } + + public LifeStageConstraint Initializing(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.Initializing, instance); + } + + public LifeStageConstraint Initialized(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.Initialized, instance); + } + + public LifeStageConstraint MapInitialized(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.MapInitialized, instance); + } + + public LifeStageConstraint Terminating(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.Terminating, instance); + } + + public LifeStageConstraint Deleted(IIntegrationInstance instance) + { + return expr.LifeStage(EntityLifeStage.Deleted, instance); + } + } +} diff --git a/Content.IntegrationTests/NUnit/Operators/CompOperator.cs b/Content.IntegrationTests/NUnit/Operators/CompOperator.cs new file mode 100644 index 0000000000..c52bcac37e --- /dev/null +++ b/Content.IntegrationTests/NUnit/Operators/CompOperator.cs @@ -0,0 +1,31 @@ +using Content.IntegrationTests.NUnit.Constraints; +using NUnit.Framework.Constraints; +using Robust.UnitTesting; + +namespace Content.IntegrationTests.NUnit.Operators; + +/// +/// An operator for use by nunit constraint resolution. +/// +/// +public sealed class CompOperator : SelfResolvingOperator +{ + private readonly Type _tComp; + private readonly IIntegrationInstance _instance; + + public CompOperator(Type tComp, IIntegrationInstance instance) + { + _tComp = tComp; + _instance = instance; + + left_precedence = right_precedence = 1; + } + + public override void Reduce(ConstraintBuilder.ConstraintStack stack) + { + if (RightContext is null or BinaryOperator) + stack.Push(new CompExistsConstraint(_tComp, _instance)); + else + stack.Push(new CompConstraint(_tComp, _instance, stack.Pop())); + } +} diff --git a/Content.IntegrationTests/NUnit/Utilities/IResolvesToEntity.cs b/Content.IntegrationTests/NUnit/Utilities/IResolvesToEntity.cs new file mode 100644 index 0000000000..bf039c597c --- /dev/null +++ b/Content.IntegrationTests/NUnit/Utilities/IResolvesToEntity.cs @@ -0,0 +1,19 @@ +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.NUnit.Utilities; + +/// +/// An interface for objects that NUnit constraints should treat as a sided entity. +/// +public interface IResolvesToEntity +{ + /// + /// The server-sided entity, if any. + /// + EntityUid? SEntity { get; } + /// + /// The client-sided entity, if any. + /// + EntityUid? CEntity { get; } +} + diff --git a/Content.IntegrationTests/NUnit/Utilities/ITestExtensions.cs b/Content.IntegrationTests/NUnit/Utilities/ITestExtensions.cs new file mode 100644 index 0000000000..878748949f --- /dev/null +++ b/Content.IntegrationTests/NUnit/Utilities/ITestExtensions.cs @@ -0,0 +1,28 @@ +using Content.IntegrationTests.Fixtures; +using NUnit.Framework.Interfaces; + +namespace Content.IntegrationTests.NUnit.Utilities; + +public static class ITestExtensions +{ + extension(T test) + where T : ITest + { + /// + /// Ensures the given fixture is a , and if not gives a nice error message. + /// + /// The caller's type, usually an attribute. + /// The . + /// Thrown when the given test isn't a + public void EnsureFixtureIsGameTest(Type callingType, out GameTest gt) + { + if (test.Fixture is not GameTest gameTest) + { + throw new NotSupportedException( + $"The fixture {test.Fixture?.GetType()} needs to be a GameTest for {callingType.Name} to work."); + } + + gt = gameTest; + } + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/ContraintTests.cs b/Content.IntegrationTests/Tests/GameTestTests/ContraintTests.cs new file mode 100644 index 0000000000..34a6d6dd9c --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/ContraintTests.cs @@ -0,0 +1,86 @@ +#nullable enable +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.IntegrationTests.NUnit.Constraints; +using Content.IntegrationTests.NUnit.Operators; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +public sealed class ConstraintsTests : GameTest +{ + [Test] + [TestOf(typeof(CompExistsConstraint))] + [Description("Ensures that a freshly spawned entity matches a constraint stating it has MetaData.")] + [RunOnSide(Side.Server)] + public void CompPositive() + { + var ent = SSpawn(null); + + Assert.That(ent, Has.Comp(Server)); + } + + [Test] + [TestOf(typeof(CompOperator))] + [Description("Ensures that NUnit property access works on Comp constraints.")] + [RunOnSide(Side.Server)] + public void CompPropertyAccess() + { + var ent = SSpawn(null); + + Assert.That(ent, + Has + .Comp(Server) + .Property(nameof(MetaDataComponent.EntityDeleted)) + .EqualTo(false) + ); + } + + [Test] + [TestOf(typeof(CompExistsConstraint))] + [Description("Ensures that a freshly spawned entity does not match a constraint stating it has some odd component.")] + [RunOnSide(Side.Server)] + public void CompNegative() + { + var ent = SSpawn(null); + + // Arbitrary pick. + Assert.That(ent, Has.No.Comp(Server)); + } + + [Test] + [TestOf(typeof(LifeStageConstraint))] + [Description("Ensures that a freshly deleted entity is deleted to constraints.")] + [RunOnSide(Side.Server)] + public void DeletedPositive() + { + var ent = SSpawn(null); + + SDeleteNow(ent); + + Assert.That(ent, Is.Deleted(Server)); + } + + [Test] + [TestOf(typeof(LifeStageConstraint))] + [RunOnSide(Side.Server)] + [Description("Entities that never existed are currently considered deleted by constraints.")] + public void DeletedNeverExisted() + { + // We'll never spawn this many ents in tests without it taking all damn day. + var ent = new EntityUid(int.MaxValue / 2); + + Assert.That(ent, Is.Deleted(Server), "Entites that never existed still count as deleted."); + } + + [Test] + [TestOf(typeof(LifeStageConstraint))] + [Description("Entities that are alive do not count as deleted.")] + [RunOnSide(Side.Server)] + public void DeletedNegative() + { + var ent = SSpawn(null); + + Assert.That(ent, Is.Not.Deleted(Server)); + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/DependencyTests.cs b/Content.IntegrationTests/Tests/GameTestTests/DependencyTests.cs new file mode 100644 index 0000000000..5ce2b486fd --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/DependencyTests.cs @@ -0,0 +1,54 @@ +#nullable enable +using System.Collections.Generic; +using Content.Client.GameTicking.Managers; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.IntegrationTests.NUnit.Constraints; +using Content.Server.GameTicking; +using Content.Shared.GameTicking; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(SidedDependencyAttribute))] +public sealed class DependencyTests : GameTest +{ + [SidedDependency(Side.Server)] private readonly SharedGameTicker _sGameTicker = null!; + [SidedDependency(Side.Client)] private readonly SharedGameTicker _cGameTicker = null!; + [SidedDependency(Side.Server)] private readonly EntityQuery _sXformQuery = default!; + + [Test] + [Description("Asserts that sided dependencies actually grab from the right sides.")] + public void DependenciesRespectSides() + { + using (Assert.EnterMultipleScope()) + { + Assert.That(!ReferenceEquals(SEntMan, CEntMan), "Server and client entity managers should be distinct"); + Assert.That(SEntMan, Is.EqualTo(Server.EntMan).Using(ReferenceEqualityComparer.Instance)); + Assert.That(CEntMan, Is.EqualTo(Client.EntMan).Using(ReferenceEqualityComparer.Instance)); + } + } + + [Test] + [Description("Asserts that system dependencies actually grab from the right sides.")] + public void SystemDependenciesRespectSides() + { + using (Assert.EnterMultipleScope()) + { + Assert.That(!ReferenceEquals(_sGameTicker, _cGameTicker), + "Server and client gametickers should be distinct"); + Assert.That(_sGameTicker, Is.TypeOf()); + Assert.That(_cGameTicker, Is.TypeOf()); + } + } + + [Test] + [Description("Asserts that query dependencies function")] + public async Task QueryDependencies() + { + var ent = await Spawn(null); + + Assert.That(_sXformQuery.HasComp(ent), Is.True); + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/DisconnectedDependencyTest.cs b/Content.IntegrationTests/Tests/GameTestTests/DisconnectedDependencyTest.cs new file mode 100644 index 0000000000..d71c56c313 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/DisconnectedDependencyTest.cs @@ -0,0 +1,46 @@ +#nullable enable +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC.Exceptions; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(SidedDependencyAttribute))] +public sealed class DisconnectedDependencyTest : GameTest +{ + [Test] + [PairConfig(nameof(PsDisconnected))] + [Description($""" + Ensures that GameTest can be started up even when the client {nameof(EntitySystemManager)} isn't initialized yet. + No body is necessary, if this fails then the bug is firmly in GameTest itself. + """)] + public void EnsureGameTestSetupWorksDisconnected() + { + // Nothin' + } + + [Test] + [PairConfig(nameof(PsDisconnected))] + [Description(""" + Ensures dependency injection that relies on client side systems fails as expected when the client is detached. + """)] + public void ClientSystemDependencyFails() + { + var creature = new Creature(); + + Assert.Throws(() => + { + InjectDependencies(creature); + }); + } + + private sealed class Creature + { +#pragma warning disable CS0414 // Field is assigned but its value is never used + [SidedDependency(Side.Client)] private readonly MapSystem _mapSys = null!; +#pragma warning restore CS0414 // Field is assigned but its value is never used + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarTest.cs b/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarTest.cs new file mode 100644 index 0000000000..942254807f --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarTest.cs @@ -0,0 +1,53 @@ +#nullable enable +using System.Linq; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Robust.Shared.Configuration; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(EnsureCVarAttribute))] +[Description("Ensures EnsureCVar actually sets CVars as expected.")] +[EnsureCVar(Side.Server, typeof(EnsureCVarsTestCVars), nameof(EnsureCVarsTestCVars.Foo), true)] +public sealed class EnsureCVarTest : GameTest +{ + [SidedDependency(Side.Server)] private readonly IConfigurationManager _sCfg = default!; + [SidedDependency(Side.Client)] private readonly IConfigurationManager _cCfg = default!; + + [Test] + [Description("Ensure Foo is set and Bar is not.")] + public void FooIsSet() + { + using (Assert.EnterMultipleScope()) + { + Assert.That(_sCfg.GetCVar(EnsureCVarsTestCVars.Foo), Is.True); + Assert.That(_cCfg.GetCVar(EnsureCVarsTestCVars.Foo), + Is.EqualTo(EnsureCVarsTestCVars.Foo.DefaultValue), + "Foo is not replicated and should not be set on the client."); + + Assert.That(_sCfg.GetCVar(EnsureCVarsTestCVars.Bar), + Is.EqualTo(EnsureCVarsTestCVars.Bar.DefaultValue)); + } + } + + [Test] + [EnsureCVar(Side.Server, typeof(EnsureCVarsTestCVars), nameof(EnsureCVarsTestCVars.Bar), 42)] + [Description("Ensure Foo and Bar are set.")] + public void BarIsSet() + { + var props = TestContext.CurrentContext.Test.Properties; + using (Assert.EnterMultipleScope()) + { + Assert.That(_sCfg.GetCVar(EnsureCVarsTestCVars.Bar), + Is.EqualTo(42)); + Assert.That(_cCfg.GetCVar(EnsureCVarsTestCVars.Bar), + Is.EqualTo(EnsureCVarsTestCVars.Bar.DefaultValue), + "Bar is not replicated and should not be set on the client."); + + Assert.That(props[EnsureCVarAttribute.ServerEnsuredCVarsProperty].Count(), + Is.EqualTo(1), + "Expected EnsureCVar to appropriately mark its target test."); + } + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarsTestCVars.cs b/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarsTestCVars.cs new file mode 100644 index 0000000000..3d7afccd57 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/EnsureCVarsTestCVars.cs @@ -0,0 +1,14 @@ +#nullable enable +using Robust.Shared.Configuration; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[CVarDefs] +public sealed class EnsureCVarsTestCVars +{ + public static readonly CVarDef Foo = + CVarDef.Create("tests.ensure_cvars.foo", false, CVar.SERVER); + + public static readonly CVarDef Bar = + CVarDef.Create("tests.ensure_cvars.bar", 3, CVar.SERVER); +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/IGameTestPairConfigModifierAttributeTests.cs b/Content.IntegrationTests/Tests/GameTestTests/IGameTestPairConfigModifierAttributeTests.cs new file mode 100644 index 0000000000..080c143b8a --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/IGameTestPairConfigModifierAttributeTests.cs @@ -0,0 +1,23 @@ +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(IGameTestPairConfigModifier))] +public sealed class IGameTestPairConfigModifierAttributeTests : GameTest +{ + [Test] + public void Control() + { + Assert.That(Pair.Settings.Connected, Is.True); + } + + [Test] + [PairConfig(nameof(PsDisconnected))] + [Description("Ensures pair settings apply.")] + public void PairConfigWorks() + { + Assert.That(Pair.Settings.Connected, Is.False); + } +} diff --git a/Content.IntegrationTests/Tests/GameTestTests/RunOnSideTests.cs b/Content.IntegrationTests/Tests/GameTestTests/RunOnSideTests.cs new file mode 100644 index 0000000000..1a3de26514 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameTestTests/RunOnSideTests.cs @@ -0,0 +1,43 @@ +#nullable enable +using System.Threading; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; + +namespace Content.IntegrationTests.Tests.GameTestTests; + +[TestOf(typeof(GameTest))] +[TestOf(typeof(RunOnSideAttribute))] +[Description("Asserts that RunOnSide actually does as expected and runs the test on the given side.")] +public sealed class RunOnSideTests : GameTest +{ + [Test] + [Description("Ensures that the default scenario is the test thread.")] + public void Control() + { + Assert.That(Thread.CurrentThread, Is.Not.EqualTo(ServerThread).And.Not.EqualTo(ClientThread)); + } + + [Test] + [RunOnSide(Side.Server)] + public void TestServerSide() + { + Assert.That(Thread.CurrentThread, Is.EqualTo(ServerThread)); + } + + [Test] + [RunOnSide(Side.Client)] + public void TestClientSide() + { + Assert.That(Thread.CurrentThread, Is.EqualTo(ClientThread)); + } + + [Test] + [RunOnSide(Side.Server)] + [Description("Ensures that RunOnSide appropriately adds a property.")] + [Ignore("TestContext on the game threads is broken.")] + [TrackingIssue("https://github.com/space-wizards/RobustToolbox/issues/6449")] + public void TestProperty() + { + Assert.That(TestContext.CurrentContext.Test.Properties.Get(RunOnSideAttribute.RunOnSideProperty), Is.Not.Null); + } +} diff --git a/Content.IntegrationTests/Utility/TestProperties.cs b/Content.IntegrationTests/Utility/TestProperties.cs new file mode 100644 index 0000000000..c8ed338a82 --- /dev/null +++ b/Content.IntegrationTests/Utility/TestProperties.cs @@ -0,0 +1,9 @@ +namespace Content.IntegrationTests.Utility; + +public static class TestProperties +{ + /// + /// Name of the property describing what kind of test frame a test is running under. + /// + public const string TestFrameKind = "TestFrameKind"; +} From ef1169a079aae6a685200bd3df6c5ca2d57b8781 Mon Sep 17 00:00:00 2001 From: Simon <63975668+Simyon264@users.noreply.github.com> Date: Fri, 27 Mar 2026 23:45:52 +0100 Subject: [PATCH 004/247] Minor fixes to server api ban endpoint (#43355) minor fixes to server api ban endpoint --- Content.Server/Administration/ServerApi.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 1990de8a6f..34b41e0ec1 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -340,7 +340,7 @@ public sealed partial class ServerApi : IPostInjectInit var bans = await _db.GetBansAsync(userId: located.UserId, address: null, hwId: null, - modernHWIds: located.LastModernHWIds, + modernHWIds: null, includeUnbanned: false); if (bans.Count > 0) { @@ -370,6 +370,11 @@ public sealed partial class ServerApi : IPostInjectInit { info.AddAddress(player.Channel.RemoteEndPoint.Address); } + else + { + // We fallback into using the located player ip. + info.AddAddress(located.LastAddress); + } _bans.CreateServerBan(info); await RespondOk(context); From ab9adaa14e7fc6524174c2c6ff7e1ead216b70b0 Mon Sep 17 00:00:00 2001 From: Velken <8467292+Velken@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:47:19 -0300 Subject: [PATCH 005/247] [FIX] Moved random manifest back server (#43361) * moved random manifest back server * fix ordering --- Content.Server/Silicons/Laws/IonLawSystem.cs | 5 ++-- .../Systems/StationRecordsSystem.cs | 24 ++++++++++++++++ .../SharedStationRecordsSystem.cs | 28 ------------------- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/Content.Server/Silicons/Laws/IonLawSystem.cs b/Content.Server/Silicons/Laws/IonLawSystem.cs index 9d928b37a0..b8bc955825 100644 --- a/Content.Server/Silicons/Laws/IonLawSystem.cs +++ b/Content.Server/Silicons/Laws/IonLawSystem.cs @@ -1,4 +1,5 @@ -using Content.Shared.Dataset; +using Content.Server.StationRecords.Systems; +using Content.Shared.Dataset; using Content.Shared.Silicons.Laws; using Content.Shared.Station; using Content.Shared.StationRecords; @@ -16,7 +17,7 @@ public sealed class IonLawSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedStationSystem _stationSystem = default!; - [Dependency] private readonly SharedStationRecordsSystem _stationRecordsSystem = default!; + [Dependency] private readonly StationRecordsSystem _stationRecordsSystem = default!; [Dependency] private readonly ILogManager _logManager = default!; private ISawmill _sawmill = default!; diff --git a/Content.Server/StationRecords/Systems/StationRecordsSystem.cs b/Content.Server/StationRecords/Systems/StationRecordsSystem.cs index cf58aea47a..2e6e7fcb96 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.Roles; 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 sealed class StationRecordsSystem : SharedStationRecordsSystem return false; } + /// + /// Gets a random record from the station's record entries. + /// + /// The EntityId of the station from which you want to get the record. + /// The resulting entry. + /// Type to get from the record set. + /// True if a record was obtained. False otherwise. + public bool TryGetRandomRecord(Entity ent, [NotNullWhen(true)] out T? entry) + { + entry = default; + + if (!Resolve(ent.Owner, ref ent.Comp)) + return false; + + if (ent.Comp.Records.Keys.Count == 0) + return false; + + var key = _random.Pick(ent.Comp.Records.Keys); + + return ent.Comp.Records.TryGetRecordEntry(key, out entry); + } /// /// Get the name for a record, or an empty string if it has no record. diff --git a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs index cd86aef943..e04de09d65 100644 --- a/Content.Shared/StationRecords/SharedStationRecordsSystem.cs +++ b/Content.Shared/StationRecords/SharedStationRecordsSystem.cs @@ -1,14 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using Robust.Shared.Random; -using Robust.Shared.Timing; -using Content.Shared.Random.Helpers; namespace Content.Shared.StationRecords; public abstract class SharedStationRecordsSystem : EntitySystem { - [Dependency] protected readonly IGameTiming Timing = default!; - public StationRecordKey? Convert((NetEntity, uint)? input) { return input == null ? null : Convert(input.Value); @@ -103,27 +98,4 @@ public abstract class SharedStationRecordsSystem : EntitySystem return null; } - - /// - /// Gets a random record from the station's record entries. - /// - /// The EntityId of the station from which you want to get the record. - /// The resulting entry. - /// Type to get from the record set. - /// True if a record was obtained. False otherwise. - public bool TryGetRandomRecord(Entity ent, [NotNullWhen(true)] out T? entry) - { - entry = default; - - if (!Resolve(ent.Owner, ref ent.Comp)) - return false; - - if (ent.Comp.Records.Keys.Count == 0) - return false; - - var random = SharedRandomExtensions.PredictedRandom(Timing, GetNetEntity(ent.Owner)); - var key = random.Pick(ent.Comp.Records.Keys); - - return ent.Comp.Records.TryGetRecordEntry(key, out entry); - } } From c186f922966c2704f8fcd1c9b91691bd4b65137b Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 27 Mar 2026 23:01:34 +0000 Subject: [PATCH 006/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ef642a5314..9dd07d2c84 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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! @@ -4033,3 +4026,10 @@ 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 From e47ce26377fa69141d56561b5dfc9fb761d34dee Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 27 Mar 2026 20:46:15 -0400 Subject: [PATCH 007/247] Remove `[Virtual]` from some classes that shouldn't have it (#43347) * Remove [Virtual] from some classes that shouldn't have it * Back to virtual with you --- .../Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs | 2 +- Content.Client/UserInterface/Controls/RadialMenu.cs | 1 - Content.Client/UserInterface/Controls/SlotControl.cs | 1 - .../Systems/Inventory/Controls/ItemSlotUIContainer.cs | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Content.Client/Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs b/Content.Client/Stylesheets/Stylesheets/NanotrasenStylesheet.Palettes.cs index df190154f8..097d54d581 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/UserInterface/Controls/RadialMenu.cs b/Content.Client/UserInterface/Controls/RadialMenu.cs index 959a60ef4f..0cc207dd89 100644 --- a/Content.Client/UserInterface/Controls/RadialMenu.cs +++ b/Content.Client/UserInterface/Controls/RadialMenu.cs @@ -228,7 +228,6 @@ public class RadialMenu : BaseWindow /// Base class for radial menu buttons. Excludes all actions except clicks and alt-clicks /// from interactions. /// -[Virtual] public abstract class RadialMenuButtonBase : BaseButton { /// diff --git a/Content.Client/UserInterface/Controls/SlotControl.cs b/Content.Client/UserInterface/Controls/SlotControl.cs index 2b43f2397d..d55b4acf55 100644 --- a/Content.Client/UserInterface/Controls/SlotControl.cs +++ b/Content.Client/UserInterface/Controls/SlotControl.cs @@ -9,7 +9,6 @@ using Robust.Shared.Prototypes; namespace Content.Client.UserInterface.Controls { - [Virtual] public abstract class SlotControl : Control, IEntityControl { public static int DefaultButtonSize = 64; diff --git a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs index 8094a77b6b..cd18c3e696 100644 --- a/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs +++ b/Content.Client/UserInterface/Systems/Inventory/Controls/ItemSlotUIContainer.cs @@ -11,7 +11,6 @@ public interface IItemslotUIContainer public bool TryAddButton(SlotControl control); } -[Virtual] public abstract class ItemSlotUIContainer : GridContainer, IItemslotUIContainer where T : SlotControl { private readonly Dictionary _buttons = new(); From c10a2ae2da7082b17df437ab26b64ed3352743e0 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Fri, 27 Mar 2026 23:09:19 -0400 Subject: [PATCH 008/247] Update RT to 275.1.0 (#43368) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index dad56301e1..a12555988a 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit dad56301e115f79f03852e3a8dfe485f0db667c3 +Subproject commit a12555988ae75a6c77b916df5c448b9a5d19f519 From b582f2e156a454d860a210a4d02fa885da6a64f6 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 28 Mar 2026 05:15:41 +0100 Subject: [PATCH 009/247] cleanup GasPrototype and some other atmos code (#43318) * cleanup * fix some localizations * fix typo * review and test fix * rename * minus one --------- Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> --- .../Consoles/AtmosAlarmEntryContainer.xaml.cs | 5 +- .../AtmosMonitoringEntryContainer.xaml.cs | 5 +- .../EntitySystems/AtmosphereSystem.Gases.cs | 2 +- .../Overlays/GasTileVisibleGasOverlay.cs | 12 +- .../Atmos/UI/GasAnalyzerBoundUserInterface.cs | 52 +++-- .../Atmos/UI/GasAnalyzerWindow.xaml.cs | 33 ++-- .../Medical/Cryogenics/CryoPodWindow.xaml.cs | 17 +- .../Tests/Atmos/ConstantsTest.cs | 3 - .../Tests/Atmos/SharedGasSpecificHeatsTest.cs | 4 +- .../EntitySystems/AtmosphereSystem.Gases.cs | 2 +- .../EntitySystems/AtmosphereSystem.LINDA.cs | 2 +- .../Atmos/EntitySystems/GasAnalyzerSystem.cs | 23 ++- Content.Shared/Atmos/Atmospherics.cs | 20 +- .../Atmos/Components/GasAnalyzerComponent.cs | 170 ++++++++-------- .../SharedAtmosphereSystem.Gases.cs | 10 +- .../SharedGasTileOverlaySystem.cs | 3 +- .../Atmos/Prototypes/GasPrototype.cs | 187 ++++++++++-------- .../Effects/Atmos/CreateGasEntityEffect.cs | 2 +- .../Medical/Cryogenics/CryoPodComponent.cs | 4 +- .../en-US/atmos/gas-analyzer-component.ftl | 1 - Resources/Locale/en-US/atmos/gases.ftl | 20 +- Resources/Locale/en-US/gases/gases.ftl | 9 - Resources/Prototypes/Atmospherics/gases.yml | 90 +++++---- 23 files changed, 335 insertions(+), 341 deletions(-) delete mode 100644 Resources/Locale/en-US/gases/gases.ftl diff --git a/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs b/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs index f0e4b13356..e192a66fcc 100644 --- a/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs +++ b/Content.Client/Atmos/Consoles/AtmosAlarmEntryContainer.xaml.cs @@ -1,6 +1,7 @@ using Content.Client.Stylesheets; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.Monitor; using Content.Shared.FixedPoint; using Content.Shared.Temperature; @@ -22,6 +23,7 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer private readonly IEntityManager _entManager; private readonly IResourceCache _cache; + private readonly SharedAtmosphereSystem _atmosphere; private Dictionary _alarmStrings = new Dictionary() { @@ -37,6 +39,7 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer _entManager = IoCManager.Resolve(); _cache = IoCManager.Resolve(); + _atmosphere = _entManager.System(); NetEntity = uid; Coordinates = coordinates; @@ -149,7 +152,7 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer foreach ((var gas, (var mol, var percent, var alert)) in keyValuePairs) { FixedPoint2 gasPercent = percent * 100f; - var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation")); + var gasAbbreviation = Loc.GetString(_atmosphere.GetGas(gas).Abbreviation); var gasLabel = new Label() { diff --git a/Content.Client/Atmos/Consoles/AtmosMonitoringEntryContainer.xaml.cs b/Content.Client/Atmos/Consoles/AtmosMonitoringEntryContainer.xaml.cs index 0ce0c9c880..cf9a89d1a5 100644 --- a/Content.Client/Atmos/Consoles/AtmosMonitoringEntryContainer.xaml.cs +++ b/Content.Client/Atmos/Consoles/AtmosMonitoringEntryContainer.xaml.cs @@ -1,6 +1,7 @@ using Content.Client.Stylesheets; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.FixedPoint; using Content.Shared.Temperature; using Robust.Client.AutoGenerated; @@ -19,12 +20,14 @@ public sealed partial class AtmosMonitoringEntryContainer : BoxContainer private readonly IEntityManager _entManager; private readonly IResourceCache _cache; + private readonly SharedAtmosphereSystem _atmosphere; public AtmosMonitoringEntryContainer(AtmosMonitoringConsoleEntry data) { RobustXamlLoader.Load(this); _entManager = IoCManager.Resolve(); _cache = IoCManager.Resolve(); + _atmosphere = _entManager.System(); Data = data; @@ -132,7 +135,7 @@ public sealed partial class AtmosMonitoringEntryContainer : BoxContainer var gasPercent = (FixedPoint2)0f; gasPercent = percent * 100f; - var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation")); + var gasAbbreviation = Loc.GetString(_atmosphere.GetGas(gas).Abbreviation); var gasLabel = new Label() { diff --git a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index d950108922..de544fba99 100644 --- a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -51,7 +51,7 @@ public sealed partial class AtmosphereSystem // though this isnt the hottest code path so it should be fine // the gc can eat a little as a treat var tmp = new float[moles.Length]; - NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp); + NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); // Adjust heat capacity by speedup, because this is primarily what // determines how quickly gases heat up/cool. return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); diff --git a/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs b/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs index 37298b95fd..4d909c9fc8 100644 --- a/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs +++ b/Content.Client/Atmos/Overlays/GasTileVisibleGasOverlay.cs @@ -72,17 +72,7 @@ public sealed class GasTileVisibleGasOverlay : Overlay { var gasPrototype = _atmosphereSystem.GetGas(_gasTileOverlaySystem.VisibleGasId[i]); - SpriteSpecifier overlay; - - if (!string.IsNullOrEmpty(gasPrototype.GasOverlaySprite) && - !string.IsNullOrEmpty(gasPrototype.GasOverlayState)) - overlay = new SpriteSpecifier.Rsi(new(gasPrototype.GasOverlaySprite), gasPrototype.GasOverlayState); - else if (!string.IsNullOrEmpty(gasPrototype.GasOverlayTexture)) - overlay = new SpriteSpecifier.Texture(new(gasPrototype.GasOverlayTexture)); - else - continue; - - switch (overlay) + switch (gasPrototype.GasOverlaySprite) { case SpriteSpecifier.Rsi animated: var rsi = _resourceCache.GetResource(animated.RsiPath).RSI; diff --git a/Content.Client/Atmos/UI/GasAnalyzerBoundUserInterface.cs b/Content.Client/Atmos/UI/GasAnalyzerBoundUserInterface.cs index 3a5df3f9a8..c8f2091d13 100644 --- a/Content.Client/Atmos/UI/GasAnalyzerBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasAnalyzerBoundUserInterface.cs @@ -1,41 +1,33 @@ -using Robust.Client.GameObjects; using Robust.Client.UserInterface; -using static Content.Shared.Atmos.Components.GasAnalyzerComponent; +using Content.Shared.Atmos.Components; -namespace Content.Client.Atmos.UI +namespace Content.Client.Atmos.UI; + +public sealed class GasAnalyzerBoundUserInterface : BoundUserInterface { - public sealed class GasAnalyzerBoundUserInterface : BoundUserInterface + [ViewVariables] + private GasAnalyzerWindow? _window; + + public GasAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { - [ViewVariables] - private GasAnalyzerWindow? _window; + } - public GasAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - } + protected override void Open() + { + base.Open(); - protected override void Open() - { - base.Open(); + _window = this.CreateWindowCenteredLeft(); + _window.OnClose += Close; + } - _window = this.CreateWindowCenteredLeft(); - _window.OnClose += Close; - } + protected override void ReceiveMessage(BoundUserInterfaceMessage message) + { + if (_window == null) + return; - protected override void ReceiveMessage(BoundUserInterfaceMessage message) - { - if (_window == null) - return; - if (message is not GasAnalyzerUserMessage cast) - return; - _window.Populate(cast); - } + if (message is not GasAnalyzerUserMessage cast) + return; - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - if (disposing) - _window?.Dispose(); - } + _window.Populate(cast); } } diff --git a/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs b/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs index 63b4e6b0c6..94a9f23745 100644 --- a/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs +++ b/Content.Client/Atmos/UI/GasAnalyzerWindow.xaml.cs @@ -1,6 +1,8 @@ using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.Atmos; +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.Temperature; using Robust.Client.Graphics; using Robust.Client.UserInterface; @@ -8,7 +10,6 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.XAML; -using static Content.Shared.Atmos.Components.GasAnalyzerComponent; using Direction = Robust.Shared.Maths.Direction; namespace Content.Client.Atmos.UI @@ -16,25 +17,17 @@ namespace Content.Client.Atmos.UI [GenerateTypedNameReferences] public sealed partial class GasAnalyzerWindow : DefaultWindow { + private readonly SharedAtmosphereSystem _atmosphere; private NetEntity _currentEntity = NetEntity.Invalid; public GasAnalyzerWindow() { RobustXamlLoader.Load(this); + _atmosphere = IoCManager.Resolve().System(); } public void Populate(GasAnalyzerUserMessage msg) { - if (msg.Error != null) - { - CTopBox.AddChild(new Label - { - Text = Loc.GetString("gas-analyzer-window-error-text", ("errorText", msg.Error)), - FontColorOverride = Color.Red - }); - return; - } - if (msg.NodeGasMixes.Length == 0) { CTopBox.AddChild(new Label @@ -329,31 +322,31 @@ namespace Content.Client.Atmos.UI for (var j = 0; j < gasMix.Gases.Length; j++) { - var gas = gasMix.Gases[j]; - var color = Color.FromHex($"#{gas.Color}", Color.White); + var gasEntry = gasMix.Gases[j]; + var gasProto = _atmosphere.GetGas(gasEntry.Gas); // Add to the table tableKey.AddChild(new Label { - Text = Loc.GetString(gas.Name) + Text = Loc.GetString(gasProto.Name) }); tableVal.AddChild(new Label { Text = Loc.GetString("gas-analyzer-window-molarity-text", - ("mol", $"{gas.Amount:0.00}")), + ("mol", $"{gasEntry.Amount:0.00}")), Align = Label.AlignMode.Right, }); tablePercent.AddChild(new Label { Text = Loc.GetString("gas-analyzer-window-percentage-text", - ("percentage", $"{(gas.Amount / totalGasAmount * 100):0.0}")), + ("percentage", $"{(gasEntry.Amount / totalGasAmount * 100):0.0}")), Align = Label.AlignMode.Right }); // Add to the gas bar //TODO: highlight the currently hover one - gasBar.AddEntry(gas.Amount, color, tooltip: Loc.GetString("gas-analyzer-window-molarity-percentage-text", - ("gasName", gas.Name), - ("amount", $"{gas.Amount:0.##}"), - ("percentage", $"{(gas.Amount / totalGasAmount * 100):0.#}"))); + gasBar.AddEntry(gasEntry.Amount, gasProto.Color, tooltip: Loc.GetString("gas-analyzer-window-molarity-percentage-text", + ("gasName", Loc.GetString(gasProto.Name)), + ("amount", $"{gasEntry.Amount:0.##}"), + ("percentage", $"{(gasEntry.Amount / totalGasAmount * 100):0.#}"))); } dataContainer.AddChild(gasBar); diff --git a/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs b/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs index 51ff5c1a00..211225119b 100644 --- a/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs +++ b/Content.Client/Medical/Cryogenics/CryoPodWindow.xaml.cs @@ -2,9 +2,8 @@ using System.Linq; using System.Numerics; using Content.Client.UserInterface.Controls; using Content.Shared.Atmos; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Damage.Components; -using Content.Shared.Damage.Systems; using Content.Shared.EntityConditions.Conditions; using Content.Shared.FixedPoint; using Content.Shared.Medical.Cryogenics; @@ -20,6 +19,7 @@ public sealed partial class CryoPodWindow : FancyWindow { [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + private readonly SharedAtmosphereSystem _atmosphere = default!; public event Action? OnEjectPatientPressed; public event Action? OnEjectBeakerPressed; @@ -29,6 +29,7 @@ public sealed partial class CryoPodWindow : FancyWindow { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); + _atmosphere = _entityManager.System(); EjectPatientButton.OnPressed += _ => OnEjectPatientPressed?.Invoke(); EjectBeakerButton.OnPressed += _ => OnEjectBeakerPressed?.Invoke(); Inject1.OnPressed += _ => OnInjectPressed?.Invoke(1); @@ -71,16 +72,16 @@ public sealed partial class CryoPodWindow : FancyWindow { var totalGasAmount = msg.GasMix.Gases.Sum(gas => gas.Amount); - foreach (var gas in msg.GasMix.Gases) + foreach (var gasEntry in msg.GasMix.Gases) { - var color = Color.FromHex($"#{gas.Color}", Color.White); - var percent = gas.Amount / totalGasAmount * 100; - var localizedName = Loc.GetString(gas.Name); + var gasProto = _atmosphere.GetGas(gasEntry.Gas); + var percent = gasEntry.Amount / totalGasAmount * 100; + var localizedName = Loc.GetString(gasProto.Name); var tooltip = Loc.GetString("gas-analyzer-window-molarity-percentage-text", ("gasName", localizedName), - ("amount", $"{gas.Amount:0.##}"), + ("amount", $"{gasEntry.Amount:0.##}"), ("percentage", $"{percent:0.#}")); - GasMixChart.AddEntry(gas.Amount, color, tooltip: tooltip); + GasMixChart.AddEntry(gasEntry.Amount, gasProto.Color, tooltip: tooltip); } } diff --git a/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs b/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs index 6481e377c9..4112acac50 100644 --- a/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs @@ -34,9 +34,6 @@ public sealed class ConstantsTest // enum mapping gases to their Id Assert.That(Enum.GetValues(), Has.Length.EqualTo(Atmospherics.TotalNumberOfGases), $"Gas enum size is not equal to TotalNumberOfGases."); - // localized abbreviations for UI purposes - Assert.That(Atmospherics.GasAbbreviations, Has.Count.EqualTo(Atmospherics.TotalNumberOfGases), - $"GasAbbreviations size is not equal to TotalNumberOfGases."); // the ID for each gas has to correspond to a value in the Gas enum (converted to a string) foreach (var gas in gasProtos) diff --git a/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs b/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs index ac80f4a105..406f7fa10d 100644 --- a/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs @@ -62,12 +62,12 @@ public sealed class SharedGasSpecificHeatsTest var clientSpecificHeats = Array.Empty(); await Server.WaitPost(delegate { - serverSpecificHeats = _sAtmos.GasSpecificHeats; + serverSpecificHeats = _sAtmos.GasMolarHeatCapacities; }); await Client.WaitPost(delegate { - clientSpecificHeats = _cAtmos.GasSpecificHeats; + clientSpecificHeats = _cAtmos.GasMolarHeatCapacities; }); Assert.That(serverSpecificHeats, diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 639a71ec60..1fae93ef94 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -38,7 +38,7 @@ namespace Content.Server.Atmos.EntitySystems } Span tmp = stackalloc float[moles.Length]; - NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp); + NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); // Adjust heat capacity by speedup, because this is primarily what // determines how quickly gases heat up/cool. return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs index ad19770bfe..7ece546e4c 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -229,7 +229,7 @@ namespace Content.Server.Atmos.EntitySystems 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; diff --git a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs index a3fe2400d0..9dcb4d13a2 100644 --- a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs @@ -8,7 +8,6 @@ using Content.Shared.Interaction; using Content.Shared.NodeContainer; using JetBrains.Annotations; using Robust.Server.GameObjects; -using static Content.Shared.Atmos.Components.GasAnalyzerComponent; namespace Content.Server.Atmos.EntitySystems; @@ -101,6 +100,7 @@ public sealed class GasAnalyzerSystem : EntitySystem _popup.PopupEntity(Loc.GetString("gas-analyzer-shutoff"), user.Value, user.Value); entity.Comp.Enabled = false; + entity.Comp.User = null; Dirty(entity); _appearance.SetData(entity.Owner, GasAnalyzerVisuals.Enabled, entity.Comp.Enabled); RemCompDeferred(entity.Owner); @@ -139,15 +139,15 @@ public sealed class GasAnalyzerSystem : EntitySystem return false; // check if the user has walked away from what they scanned - if (component.Target.HasValue) + if (component.Target.HasValue && component.User.HasValue) { // Listen! Even if you don't want the Gas Analyzer to work on moving targets, you should use // this code to determine if the object is still generally in range so that the check is consistent with the code // in OnAfterInteract() and also consistent with interaction code in general. - if (!_interactionSystem.InRangeUnobstructed((component.User, null), (component.Target.Value, null))) + if (!_interactionSystem.InRangeUnobstructed((component.User.Value, null), (component.Target.Value, null))) { - if (component.User is { } userId && component.Enabled) - _popup.PopupEntity(Loc.GetString("gas-analyzer-object-out-of-range"), userId, userId); + if (component.Enabled) + _popup.PopupEntity(Loc.GetString("gas-analyzer-object-out-of-range"), component.User.Value, component.User.Value); component.Target = null; } @@ -256,18 +256,17 @@ public sealed class GasAnalyzerSystem : EntitySystem { var gases = new List(); + if (mixture == null) + return []; + for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) { - var gas = _atmo.GetGas(i); + var gas = (Gas)i; - if (mixture?[i] <= UIMinMoles) + if (mixture[i] <= UIMinMoles) continue; - if (mixture != null) - { - var gasName = Loc.GetString(gas.Name); - gases.Add(new GasEntry(gasName, mixture[i], gas.Color)); - } + gases.Add(new GasEntry(gas, mixture[i])); } var gasesOrdered = gases.OrderByDescending(gas => gas.Amount); diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs index 10d303cd1d..02d4a7deb8 100644 --- a/Content.Shared/Atmos/Atmospherics.cs +++ b/Content.Shared/Atmos/Atmospherics.cs @@ -170,22 +170,6 @@ namespace Content.Shared.Atmos /// public const float SpaceHeatCapacity = 7000f; - /// - /// Dictionary of chemical abbreviations for - /// - public static Dictionary GasAbbreviations = new Dictionary() - { - [Gas.Ammonia] = Loc.GetString("gas-ammonia-abbreviation"), - [Gas.CarbonDioxide] = Loc.GetString("gas-carbon-dioxide-abbreviation"), - [Gas.Frezon] = Loc.GetString("gas-frezon-abbreviation"), - [Gas.Nitrogen] = Loc.GetString("gas-nitrogen-abbreviation"), - [Gas.NitrousOxide] = Loc.GetString("gas-nitrous-oxide-abbreviation"), - [Gas.Oxygen] = Loc.GetString("gas-oxygen-abbreviation"), - [Gas.Plasma] = Loc.GetString("gas-plasma-abbreviation"), - [Gas.Tritium] = Loc.GetString("gas-tritium-abbreviation"), - [Gas.WaterVapor] = Loc.GetString("gas-water-vapor-abbreviation"), - }; - #region Excited Groups /// @@ -235,8 +219,8 @@ namespace Content.Shared.Atmos public const float SuperSaturationEnds = SuperSaturationThreshold / 3; public const float OxygenBurnRateBase = 1.4f; - public const float PlasmaMinimumBurnTemperature = (100f+T0C); - public const float PlasmaUpperTemperature = (1370f+T0C); + public const float PlasmaMinimumBurnTemperature = 100f + T0C; + public const float PlasmaUpperTemperature = 1370f + T0C; public const float PlasmaOxygenFullburn = 10f; public const float PlasmaBurnRateDelta = 9f; diff --git a/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs b/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs index 115cb54892..cc3914fe67 100644 --- a/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs +++ b/Content.Shared/Atmos/Components/GasAnalyzerComponent.cs @@ -1,101 +1,103 @@ using Robust.Shared.GameStates; -using Robust.Shared.Map; using Robust.Shared.Serialization; namespace Content.Shared.Atmos.Components; +/// +/// Used for gas analyzers, an item that shows players the gas contents of an atmos +/// device they use it on or of the tile they are standing on. +/// [RegisterComponent, NetworkedComponent] public sealed partial class GasAnalyzerComponent : Component { - [ViewVariables] + /// + /// The target entity currently being analyzed. + /// + [DataField] public EntityUid? Target; - [ViewVariables] - public EntityUid User; + /// + /// The current user of the gas analyzer. + /// + [DataField] + public EntityUid? User; - [DataField("enabled"), ViewVariables(VVAccess.ReadWrite)] + /// + /// Is the analyzer currently active? + /// + [DataField] public bool Enabled; - - [Serializable, NetSerializable] - public enum GasAnalyzerUiKey - { - Key, - } - - /// - /// Atmospheric data is gathered in the system and sent to the user - /// - [Serializable, NetSerializable] - public sealed class GasAnalyzerUserMessage : BoundUserInterfaceMessage - { - public string DeviceName; - public NetEntity DeviceUid; - public bool DeviceFlipped; - public string? Error; - public GasMixEntry[] NodeGasMixes; - public GasAnalyzerUserMessage(GasMixEntry[] nodeGasMixes, string deviceName, NetEntity deviceUid, bool deviceFlipped, string? error = null) - { - NodeGasMixes = nodeGasMixes; - DeviceName = deviceName; - DeviceUid = deviceUid; - DeviceFlipped = deviceFlipped; - Error = error; - } - } - - /// - /// Contains information on a gas mix entry, turns into a tab in the UI - /// - [Serializable, NetSerializable] - public struct GasMixEntry - { - /// - /// Name of the tab in the UI - /// - public readonly string Name; - public readonly float Volume; - public readonly float Pressure; - public readonly float Temperature; - public readonly GasEntry[]? Gases; - - public GasMixEntry(string name, float volume, float pressure, float temperature, GasEntry[]? gases = null) - { - Name = name; - Volume = volume; - Pressure = pressure; - Temperature = temperature; - Gases = gases; - } - } - - /// - /// Individual gas entry data for populating the UI - /// - [Serializable, NetSerializable] - public struct GasEntry - { - public readonly string Name; - public readonly float Amount; - public readonly string Color; - - public GasEntry(string name, float amount, string color) - { - Name = name; - Amount = amount; - Color = color; - } - - public override string ToString() - { - // e.g. "Plasma: 2000 mol" - return Loc.GetString( - "gas-entry-info", - ("gasName", Name), - ("gasAmount", Amount)); - } - } } +/// +/// Atmospheric data is gathered in the system and sent to the user. +/// +[Serializable, NetSerializable] +public sealed class GasAnalyzerUserMessage(GasMixEntry[] nodeGasMixes, string deviceName, NetEntity deviceUid, bool deviceFlipped) : BoundUserInterfaceMessage +{ + public string DeviceName = deviceName; + public NetEntity DeviceUid = deviceUid; + public bool DeviceFlipped = deviceFlipped; + public GasMixEntry[] NodeGasMixes = nodeGasMixes; +} + +/// +/// Contains information on a gas mix entry, turns into a tab in the UI. +/// +[Serializable, NetSerializable] +public readonly record struct GasMixEntry(string Name, float Volume, float Pressure, float Temperature, GasEntry[]? Gases = null) +{ + /// + /// Name of the tab in the UI. + /// + public readonly string Name = Name; + /// + /// Volume of this gas mixture. + /// + public readonly float Volume = Volume; + /// + /// Pressure of this gas mixture. + /// + public readonly float Pressure = Pressure; + /// + /// Temperature of this gas mixture. + /// + public readonly float Temperature = Temperature; + /// + /// The gases contained in this gas mixture. + /// The gases below a certain mol threshold are not included. + /// + public readonly GasEntry[]? Gases = Gases; +} + +/// +/// Individual gas entry data for populating the UI. +/// +[Serializable, NetSerializable] +public readonly record struct GasEntry(Gas Gas, float Amount) +{ + /// + /// The gas this entry represents. + /// + public readonly Gas Gas = Gas; + /// + /// The gas amount in mol. + /// + public readonly float Amount = Amount; +} + +/// +/// Key for the GasAnalyzerBoundUserInterface. +/// +[Serializable, NetSerializable] +public enum GasAnalyzerUiKey +{ + Key, +} + +/// +/// Individual gas entry data for populating the UI +/// [Serializable, NetSerializable] public enum GasAnalyzerVisuals : byte { diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs index c7267393dd..99b2942f13 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs @@ -16,10 +16,10 @@ public abstract partial class SharedAtmosphereSystem */ /// - /// Cached array of gas specific heats. + /// Cached array of molar heat capacities of the gases. /// - public float[] GasSpecificHeats => _gasSpecificHeats; - private float[] _gasSpecificHeats = new float[Atmospherics.TotalNumberOfGases]; + public float[] GasMolarHeatCapacities => _gasMolarHeatCapacities; + private float[] _gasMolarHeatCapacities = new float[Atmospherics.TotalNumberOfGases]; /// /// Mask used to determine if a gas is flammable or not. @@ -69,7 +69,7 @@ public abstract partial class SharedAtmosphereSystem GasReagents[idx] = gasPrototype.Reagent; } - Array.Resize(ref _gasSpecificHeats, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4)); + Array.Resize(ref _gasMolarHeatCapacities, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4)); for (var i = 0; i < GasPrototypes.Length; i++) { @@ -81,7 +81,7 @@ public abstract partial class SharedAtmosphereSystem If you would like the unscaled specific heat, you'd need to multiply by HeatScale again. TODO ATMOS: please just make this 2 separate arrays instead of invoking multiplication every time. */ - _gasSpecificHeats[i] = GasPrototypes[i].SpecificHeat / HeatScale; + _gasMolarHeatCapacities[i] = GasPrototypes[i].MolarHeatCapacity / HeatScale; // """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; diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs index 23ea8fa8fa..e9c697c53c 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs @@ -31,8 +31,7 @@ public abstract class SharedGasTileOverlaySystem : EntitySystem for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) { var gasPrototype = _atmosphere.GetGas(i); - if (!string.IsNullOrEmpty(gasPrototype.GasOverlayTexture) || - (!string.IsNullOrEmpty(gasPrototype.GasOverlaySprite) && !string.IsNullOrEmpty(gasPrototype.GasOverlayState))) + if (gasPrototype.GasOverlaySprite != null) visibleGases.Add(i); } VisibleGasId = visibleGases.ToArray(); diff --git a/Content.Shared/Atmos/Prototypes/GasPrototype.cs b/Content.Shared/Atmos/Prototypes/GasPrototype.cs index 86304dfdda..bb92d0307d 100644 --- a/Content.Shared/Atmos/Prototypes/GasPrototype.cs +++ b/Content.Shared/Atmos/Prototypes/GasPrototype.cs @@ -1,103 +1,122 @@ -using Content.Shared.Chemistry.Reagent; +using Content.Shared.CCVar; +using Content.Shared.Chemistry.Reagent; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Utility; -namespace Content.Shared.Atmos.Prototypes +namespace Content.Shared.Atmos.Prototypes; + +/// +/// Prototype defining a gas for atmospherics. +/// +/// +/// The total number of gases is hardcoded in a bunch of places. +/// If you add any new ones, make sure to also adjust the constants in accordingly. +/// +[Prototype] +public sealed partial class GasPrototype : IPrototype { - [Prototype] - public sealed partial class GasPrototype : IPrototype - { - [DataField("name")] public string Name { get; set; } = ""; + // TODO: Add interfaces for gas behaviours e.g. breathing, burning - // TODO: Control gas amount necessary for overlay to appear - // TODO: Add interfaces for gas behaviours e.g. breathing, burning + /// + [IdDataField] + public string ID { get; private set; } = default!; - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; + /// + /// The name of the gas as shown to the player. + /// + [DataField(required: true)] + public LocId Name; - /// - /// Specific heat for gas. - /// - [DataField("specificHeat")] - public float SpecificHeat { get; private set; } + /// + /// The abbreviation of the name. For example O₂ for Oxygen. + /// Used for UI purposes. + /// + [DataField(required: true)] + public LocId Abbreviation; - /// - /// Heat capacity ratio for gas - /// - [DataField("heatCapacityRatio")] - public float HeatCapacityRatio { get; private set; } = 1.4f; + /// + /// The molar heat capacity of this gas, in J/(K * mol). + /// Describes how much heat energy is needed to heat up this gas by one Kelvin. + /// Or in other words, the higher this number is the more energy this gas can store. + /// + /// + /// This will be divided by the cvar. + /// + [DataField] + public float MolarHeatCapacity; - /// - /// Molar mass of gas - /// - [DataField("molarMass")] - public float MolarMass { get; set; } = 1f; + /// + /// Heat capacity ratio for gas. + /// TODO: Make gas pumps do proper adiabatic compression so that this is actually used. + /// + [DataField] + public float HeatCapacityRatio = 1.4f; + + /// + /// Molar mass of the gas. + /// TODO: This is not used anywhere, do we even need this? + /// + [DataField] + public float MolarMass = 1f; - /// - /// Minimum amount of moles for this gas to be visible. - /// - [DataField("gasMolesVisible")] - public float GasMolesVisible { get; private set; } = 0.25f; + /// + /// Minimum amount of moles for this gas to be visible. + /// + [DataField] + public float GasMolesVisible = 0.25f; - /// - /// Visibility for this gas will be max after this value. - /// - public float GasMolesVisibleMax => GasMolesVisible * GasVisibilityFactor; + /// + /// Visibility for this gas will be max after this value. + /// + [ViewVariables] + public float GasMolesVisibleMax => GasMolesVisible * GasVisibilityFactor; - [DataField("gasVisbilityFactor")] - public float GasVisibilityFactor = Atmospherics.FactorGasVisibleMax; + /// + /// Multiplier that decides when a gas will be at maximum visibility. + /// + [DataField] + public float GasVisibilityFactor = Atmospherics.FactorGasVisibleMax; - /// - /// If this reagent is in gas form, this is the path to the overlay that will be used to make the gas visible. - /// - [DataField("gasOverlayTexture")] - public string GasOverlayTexture { get; private set; } = string.Empty; + /// + /// Sprite to show in the gas overlay if this gas is present on a tile. + /// If null the gas will be invisible. + /// + [DataField] + public SpriteSpecifier? GasOverlaySprite; - /// - /// If this reagent is in gas form, this will be the path to the RSI sprite that will be used to make the gas visible. - /// - [DataField("gasOverlayState")] - public string GasOverlayState { get; set; } = string.Empty; + /// + /// The reagent that this gas will turn into when inhaled or condensed. + /// + [DataField] + public ProtoId? Reagent; - /// - /// State for the gas RSI overlay. - /// - [DataField("gasOverlaySprite")] - public string GasOverlaySprite { get; set; } = string.Empty; + /// + /// The color of the gas used for UI purposes. + /// + [DataField] + public Color Color = Color.White; - /// - /// Path to the tile overlay used when this gas appears visible. - /// - [DataField("overlayPath")] - public string OverlayPath { get; private set; } = string.Empty; + /// + /// The price per mole when this gas is sold at cargo. + /// The final price will also depend on the purity of the gas mixture. + /// + [DataField] + public float PricePerMole = 0; - /// - /// The reagent that this gas will turn into when inhaled. - /// - [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer))] - public string? Reagent { get; private set; } = default!; + /// + /// Whether the gas is considered to be flammable. + /// This is used generically across Atmospherics to determine + /// if things like hotspots are allowed to ignite if an + /// oxidizer is present. + /// + [DataField] + public bool IsFuel; - [DataField("color")] public string Color { get; private set; } = string.Empty; - - [DataField("pricePerMole")] - public float PricePerMole { get; set; } = 0; - - /// - /// Whether the gas is considered to be flammable. - /// This is used generically across Atmospherics to determine - /// if things like hotspots are allowed to ignite if an - /// oxidizer is present. - /// - [DataField] - public bool IsFuel; - - /// - /// Whether the gas is considered to be an oxidizer. - /// Same reasoning as but for oxidizers. - /// - [DataField] - public bool IsOxidizer; - } + /// + /// Whether the gas is considered to be an oxidizer. + /// Same reasoning as but for oxidizers. + /// + [DataField] + public bool IsOxidizer; } diff --git a/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs b/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs index aa5132e596..9614933247 100644 --- a/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs +++ b/Content.Shared/EntityEffects/Effects/Atmos/CreateGasEntityEffect.cs @@ -30,6 +30,6 @@ public sealed partial class CreateGas : EntityEffectBase return Loc.GetString("entity-effect-guidebook-create-gas", ("chance", Probability), ("moles", Moles), - ("gas", gasProto.Name)); + ("gas", Loc.GetString(gasProto.Name))); } } diff --git a/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs b/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs index 386d0989d9..6dbe11776d 100644 --- a/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs +++ b/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs @@ -127,7 +127,7 @@ public enum CryoPodUiKey : byte [Serializable, NetSerializable] public sealed class CryoPodUserMessage : BoundUserInterfaceMessage { - public GasAnalyzerComponent.GasMixEntry GasMix; + public GasMixEntry GasMix; public HealthAnalyzerUiState Health; public FixedPoint2? BeakerCapacity; public List? Beaker; @@ -135,7 +135,7 @@ public sealed class CryoPodUserMessage : BoundUserInterfaceMessage public bool HasDamage; public CryoPodUserMessage( - GasAnalyzerComponent.GasMixEntry gasMix, + GasMixEntry gasMix, HealthAnalyzerUiState health, FixedPoint2? beakerCapacity, List? beaker, diff --git a/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl b/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl index a2cb5301b2..57ed361248 100644 --- a/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl +++ b/Resources/Locale/en-US/atmos/gas-analyzer-component.ftl @@ -11,7 +11,6 @@ gas-analyzer-window-tab-title-capitalized = {CAPITALIZE($title)} gas-analyzer-window-refresh-button = Refresh gas-analyzer-window-no-data = No Data gas-analyzer-window-no-gas-text = No Gases -gas-analyzer-window-error-text = Error: {$errorText} gas-analyzer-window-volume-text = Volume: gas-analyzer-window-volume-val-text = {$volume} L gas-analyzer-window-pressure-text = Pressure: diff --git a/Resources/Locale/en-US/atmos/gases.ftl b/Resources/Locale/en-US/atmos/gases.ftl index 5c540c46df..b5d9ed6aae 100644 --- a/Resources/Locale/en-US/atmos/gases.ftl +++ b/Resources/Locale/en-US/atmos/gases.ftl @@ -1,10 +1,18 @@ -gas-ammonia-abbreviation = NH₃ -gas-carbon-dioxide-abbreviation = CO₂ -gas-frezon-abbreviation = F -gas-nitrogen-abbreviation = N₂ -gas-nitrous-oxide-abbreviation = N₂O +gas-oxygen = Oxygen gas-oxygen-abbreviation = O₂ +gas-nitrogen = Nitrogen +gas-nitrogen-abbreviation = N₂ +gas-carbon-dioxide = Carbon Dioxide +gas-carbon-dioxide-abbreviation = CO₂ +gas-plasma = Plasma gas-plasma-abbreviation = P +gas-tritium = Tritium gas-tritium-abbreviation = T +gas-water-vapor = Water Vapor gas-water-vapor-abbreviation = H₂O -gas-unknown-abbreviation = X +gas-ammonia = Ammonia +gas-ammonia-abbreviation = NH₃ +gas-nitrous-oxide = Nitrous Oxide +gas-nitrous-oxide-abbreviation = N₂O +gas-frezon = Frezon +gas-frezon-abbreviation = F diff --git a/Resources/Locale/en-US/gases/gases.ftl b/Resources/Locale/en-US/gases/gases.ftl deleted file mode 100644 index e41aa4fc99..0000000000 --- a/Resources/Locale/en-US/gases/gases.ftl +++ /dev/null @@ -1,9 +0,0 @@ -gases-oxygen = Oxygen -gases-nitrogen = Nitrogen -gases-co2 = Carbon Dioxide -gases-plasma = Plasma -gases-tritium = Tritium -gases-water-vapor = Water Vapor -gases-ammonia = Ammonia -gases-n2o = Nitrous Oxide -gases-frezon = Frezon diff --git a/Resources/Prototypes/Atmospherics/gases.yml b/Resources/Prototypes/Atmospherics/gases.yml index f27bab9074..2108b68e53 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 + 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 From 067bd9f43c4dc78d46c9c3644f300b29dd48c335 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sat, 28 Mar 2026 06:29:55 -0400 Subject: [PATCH 010/247] Fix `TrackingIssueAttribute` blocking orgs/repos containing numbers (#43369) Fix TrackingIssueAttribute blocking orgs/repos containing numbers --- .../Fixtures/Attributes/TrackingIssueAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs b/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs index 02f9e4f7e4..4acbe8e87b 100644 --- a/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs +++ b/Content.IntegrationTests/Fixtures/Attributes/TrackingIssueAttribute.cs @@ -28,7 +28,7 @@ public sealed class TrackingIssueAttribute : PropertyAttribute "github.com" ]; - private static readonly Regex GithubStyleIssueMatch = new(@"^\/[a-z\-\$\#]*\/[a-z\-\$\#]*\/(issues|pulls)\/\d*$", + 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) From 8e87fd3e58ccbdf4631569b70d9a18a3307d127d Mon Sep 17 00:00:00 2001 From: Gentleman-Bird Date: Sat, 28 Mar 2026 11:01:23 -0400 Subject: [PATCH 011/247] Fix: Make clockwork glass grindable (#43371) make cGlass grindable --- .../Prototypes/Entities/Objects/Materials/Sheets/glass.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml index 532d0b9cca..70c823b765 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml @@ -500,7 +500,7 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - type: Extractable - grindableSolutionName: brassglass + grindableSolutionName: cglass - type: SolutionContainerManager solutions: cglass: From 937b5f955d657a7960f93b14d99a5c0c06beb67f Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 28 Mar 2026 15:15:29 +0000 Subject: [PATCH 012/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9dd07d2c84..7ef90b977a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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) @@ -4033,3 +4026,10 @@ 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 From e0eae8628f7a4a80efeb627f494fbbb1f8ace88b Mon Sep 17 00:00:00 2001 From: Minerva <218184747+mnva0@users.noreply.github.com> Date: Sat, 28 Mar 2026 11:49:03 -0400 Subject: [PATCH 013/247] Makes singularities respect reduced motion option (#43362) * Makes singularities respect reduced motion option * This seems better * Apply suggestion from @slarticodefast * Apply suggestion from @slarticodefast * Apply suggestion from @slarticodefast --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Client/Singularity/SingularityOverlay.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Content.Client/Singularity/SingularityOverlay.cs b/Content.Client/Singularity/SingularityOverlay.cs index 39b0758bf1..fd888fb6dd 100644 --- a/Content.Client/Singularity/SingularityOverlay.cs +++ b/Content.Client/Singularity/SingularityOverlay.cs @@ -1,9 +1,11 @@ +using System.Numerics; +using Content.Shared.CCVar; using Content.Shared.Singularity.Components; using Robust.Client.Graphics; using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.Prototypes; -using System.Numerics; namespace Content.Client.Singularity { @@ -13,6 +15,7 @@ namespace Content.Client.Singularity [Dependency] private readonly IEntityManager _entMan = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; private SharedTransformSystem? _xformSystem = null; /// @@ -28,6 +31,8 @@ namespace Content.Client.Singularity private readonly ShaderInstance _shader; + private bool _reducedMotion; + public SingularityOverlay() { IoCManager.InjectDependencies(this); @@ -35,6 +40,8 @@ namespace Content.Client.Singularity _shader.SetParameter("maxDistance", MaxDistance * EyeManager.PixelsPerMeter); _entMan.EventBus.SubscribeEvent(EventSource.Local, this, OnProjectFromScreenToMap); ZIndex = 101; // Should be drawn after the placement overlay so admins placing items near the singularity can tell where they're going. + + _configManager.OnValueChanged(CCVars.ReducedMotion, (b) => { _reducedMotion = b; }, invokeImmediately: true); } private readonly Vector2[] _positions = new Vector2[MaxCount]; @@ -44,6 +51,8 @@ namespace Content.Client.Singularity protected override bool BeforeDraw(in OverlayDrawArgs args) { + if (_reducedMotion) + return false; if (args.Viewport.Eye == null) return false; if (_xformSystem is null && !_entMan.TrySystem(out _xformSystem)) @@ -102,6 +111,8 @@ namespace Content.Client.Singularity /// private void OnProjectFromScreenToMap(ref PixelToMapEvent args) { // Mostly copypasta from the singularity shader. + if (_reducedMotion) + return; if (args.Viewport.Eye == null) return; var maxDistance = MaxDistance * EyeManager.PixelsPerMeter; From 54bbdde221d392146977a98e8138f86131867e69 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 28 Mar 2026 16:02:24 +0000 Subject: [PATCH 014/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7ef90b977a..91cb9a8c82 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4033,3 +4026,11 @@ 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 From a87a4d3fb3f27983e01d15d1aae053a26d8609c6 Mon Sep 17 00:00:00 2001 From: Marchy <89603088+M4rchy-S@users.noreply.github.com> Date: Sat, 28 Mar 2026 23:06:30 +0100 Subject: [PATCH 015/247] "Reduce motion of visual effects" now works for blood loss effect (#41878) * "Reduce motion of visual effects" now works for blood loss effect * Improve strenght * Make motion less strong * Stop motion effect * cleanup and rebalancing * fix warning --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Client/Drunk/DrunkOverlay.cs | 37 +++++++++++++++++++-------- Content.Client/Drunk/DrunkSystem.cs | 6 +++++ Resources/Textures/Shaders/drunk.swsl | 29 +++++++++++++-------- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Content.Client/Drunk/DrunkOverlay.cs b/Content.Client/Drunk/DrunkOverlay.cs index 692232776a..44d82f3794 100644 --- a/Content.Client/Drunk/DrunkOverlay.cs +++ b/Content.Client/Drunk/DrunkOverlay.cs @@ -1,8 +1,8 @@ +using Content.Shared.CCVar; using Content.Shared.Drunk; -using Content.Shared.StatusEffect; -using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; +using Robust.Shared.Configuration; using Robust.Shared.Enums; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -11,19 +11,23 @@ namespace Content.Client.Drunk; public sealed class DrunkOverlay : Overlay { - private static readonly ProtoId Shader = "Drunk"; + private static readonly ProtoId DrunkShader = "Drunk"; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IEntitySystemManager _sysMan = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IConfigurationManager _configManager = default!; + private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffectsSystem; public override OverlaySpace Space => OverlaySpace.WorldSpace; public override bool RequestScreenTexture => true; private readonly ShaderInstance _drunkShader; public float CurrentBoozePower = 0.0f; + // Starting phase for the rotation effect. + // Needed so it doesn't always look the same for 0 motion. + public float Phase = 0f; private const float VisualThreshold = 10.0f; private const float PowerDivisor = 250.0f; @@ -37,12 +41,22 @@ public sealed class DrunkOverlay : Overlay private const float BoozePowerScale = 8f; - private float _visualScale = 0; + private float _visualScale = 0f; + private float _timeScale = 1f; + private float _distortionScale = 1f; public DrunkOverlay() { IoCManager.InjectDependencies(this); - _drunkShader = _prototypeManager.Index(Shader).InstanceUnique(); + _statusEffectsSystem = _entityManager.System(); + _drunkShader = _prototypeManager.Index(DrunkShader).InstanceUnique(); + _configManager.OnValueChanged(CCVars.ReducedMotion, OnReducedMotionChanged, invokeImmediately: true); + } + + private void OnReducedMotionChanged(bool reducedMotion) + { + _timeScale = reducedMotion ? 0.0f : 1.0f; + _distortionScale = reducedMotion ? 4.0f : 1.0f; // Make the offset stronger to compensate the lack of motion. } protected override void FrameUpdate(FrameEventArgs args) @@ -53,15 +67,14 @@ public sealed class DrunkOverlay : Overlay if (playerEntity == null) return; - var statusSys = _sysMan.GetEntitySystem(); - if (!statusSys.TryGetMaxTime(playerEntity.Value, out var status)) + if (!_statusEffectsSystem.TryGetMaxTime(playerEntity.Value, out var status)) return; var time = status.Item2; - var power = time == null ? MaxBoozePower : (float) Math.Min((time - _timing.CurTime).Value.TotalSeconds, MaxBoozePower); + var power = time == null ? MaxBoozePower : (float)Math.Min((time - _timing.CurTime).Value.TotalSeconds, MaxBoozePower); - CurrentBoozePower += BoozePowerScale * (power - CurrentBoozePower) * args.DeltaSeconds / (power+1); + CurrentBoozePower += BoozePowerScale * (power - CurrentBoozePower) * args.DeltaSeconds / (power + 1); } protected override bool BeforeDraw(in OverlayDrawArgs args) @@ -82,8 +95,12 @@ public sealed class DrunkOverlay : Overlay return; var handle = args.WorldHandle; + _drunkShader.SetParameter("SCREEN_TEXTURE", ScreenTexture); _drunkShader.SetParameter("boozePower", _visualScale); + _drunkShader.SetParameter("timeScale", _timeScale); + _drunkShader.SetParameter("distortionScale", _distortionScale); + _drunkShader.SetParameter("phase", Phase); handle.UseShader(_drunkShader); handle.DrawRect(args.WorldBounds, Color.White); handle.UseShader(null); diff --git a/Content.Client/Drunk/DrunkSystem.cs b/Content.Client/Drunk/DrunkSystem.cs index c5fab75e7d..2e1f8157aa 100644 --- a/Content.Client/Drunk/DrunkSystem.cs +++ b/Content.Client/Drunk/DrunkSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.StatusEffectNew; using Robust.Client.Graphics; using Robust.Client.Player; using Robust.Shared.Player; +using Robust.Shared.Random; namespace Content.Client.Drunk; @@ -10,6 +11,7 @@ public sealed class DrunkSystem : SharedDrunkSystem { [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly IRobustRandom _random = default!; private DrunkOverlay _overlay = default!; @@ -29,7 +31,10 @@ public sealed class DrunkSystem : SharedDrunkSystem private void OnStatusApplied(Entity entity, ref StatusEffectAppliedEvent args) { if (!_overlayMan.HasOverlay()) + { + _overlay.Phase = _random.NextFloat(MathF.Tau); // random starting phase for movement effect _overlayMan.AddOverlay(_overlay); + } } private void OnStatusRemoved(Entity entity, ref StatusEffectRemovedEvent args) @@ -47,6 +52,7 @@ public sealed class DrunkSystem : SharedDrunkSystem private void OnPlayerAttached(Entity entity, ref StatusEffectRelayedEvent args) { _overlayMan.AddOverlay(_overlay); + } private void OnPlayerDetached(Entity entity, ref StatusEffectRelayedEvent args) diff --git a/Resources/Textures/Shaders/drunk.swsl b/Resources/Textures/Shaders/drunk.swsl index c2657c18a5..1410baa823 100644 --- a/Resources/Textures/Shaders/drunk.swsl +++ b/Resources/Textures/Shaders/drunk.swsl @@ -1,22 +1,29 @@ uniform sampler2D SCREEN_TEXTURE; uniform highp float boozePower; -const highp float TimeScale = 0.5; -const highp float DistortionScale = 0.01; +// How fast to do the rotating motion. +// 1 for normal effect. +// 0 for for no motion. +uniform highp float timeScale; +// Starting phase for the rotation effect. +// Needed so it doesn't always look the same for 0 motion. +uniform highp float phase; +// Multiplier for the amplitude of the offset. 1 for normal strength. +uniform highp float distortionScale; void fragment() { - highp float mod = mix(0.0, DistortionScale, boozePower); + highp float amplitudeMod = mix(0.0, distortionScale * 0.01, boozePower); highp vec2 coord = FRAGCOORD.xy * SCREEN_PIXEL_SIZE.xy; - highp float time = TIME * TimeScale; + highp float time = TIME * timeScale * 0.5 + phase; - highp vec2 offset = vec2((mod * 1.5) * sin(time * 1.5), (mod * 2.0) * cos(time * 1.5 - 0.2)); + highp vec2 offset = vec2((amplitudeMod * 1.5) * sin(time * 1.5), (amplitudeMod * 2.0) * cos(time * 1.5 - 0.2)); highp vec4 tex1 = zTextureSpec(SCREEN_TEXTURE, coord + offset); - - if (boozePower > 0.5) { - offset = vec2((mod * 2.0 - DistortionScale) * sin(time * 0.333 - 0.2), (mod * 2.0 - DistortionScale) * cos(time * 0.333)); - tex1 = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.3, boozePower*2.0-1.0)); + + if (boozePower > 0.25) { + offset = vec2((amplitudeMod * 2.0) * sin(time * 0.333 - 0.2), (amplitudeMod * 2.0) * cos(time * 0.333)); + tex1 = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.3, boozePower * 2.0 - 0.5)); } - - offset = vec2((mod * 1.0) * sin(time * 1.0 + 0.1), (mod * 1.0) * cos(time * 1.0)); + + offset = vec2(amplitudeMod * sin(time * 1.0 + 0.1), amplitudeMod * cos(time * 1.0)); COLOR = mix(tex1, zTextureSpec(SCREEN_TEXTURE, coord + offset), mix(0.0, 0.5, boozePower)); } From 9598347e81efcac3baeb704e980f8621f8ca696a Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 28 Mar 2026 22:20:27 +0000 Subject: [PATCH 016/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 91cb9a8c82..f56657827b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4034,3 +4027,11 @@ 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 From 1f24878cf6d5e7d7cdcaeabfffa67fcd0ec25103 Mon Sep 17 00:00:00 2001 From: InsoPL Date: Sun, 29 Mar 2026 04:23:40 +0200 Subject: [PATCH 017/247] Heat distortion shader (#42973) * revert of the revert * tests * changes * more fun * test * ccvvvar * works but bad * now its better * more fixes * more cleanup * cleaning * last fixes before move to glasses activ * x * glasses only * working * fix toolbox * cleanup * ThermalByte added * small fix * small optimalisations * float bux fix * comments add * more comments * more comments * last fix * revert cvar delete * wrong blue shades * cvar refactor * Update Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> * Update Content.Client/Atmos/Overlays/GasTileDangerousTemperatureOverlay.cs Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> * tweak to TryGetTemperature comment * Factors are now const * renames * Interface for ThermalByte * tile color vaccum and more comments * saving yeeted * integration test * rename and cleanup * fix * cleanup * switch * UT fix (hopefully) * small bug+ rename * vaccum limit + space is now invalid * typo * typo * fix * init, works * move method around * renames and split * renames * heatblur * cleanup * more docs * resource cache * No dynamic perlin noise generation [no fun allowed] and new blur softening method * magic numbers and rename * misc cleanup * removed init values because display compat mode broken * misc cleanup --------- Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> --- .../GasTileHeatBlurOverlaySystem.cs | 30 ++ .../Atmos/Overlays/GasTileHeatBlurOverlay.cs | 261 ++++++++++++++++++ Resources/Prototypes/Shaders/shaders.yml | 5 + .../Effects/HeatBlur/perlin_noise.png | Bin 0 -> 77641 bytes .../Textures/Effects/HeatBlur/soft_circle.png | Bin 0 -> 1616 bytes Resources/Textures/Shaders/heatBlur.swsl | 34 +++ Tools/generate_heat_distortion_textures.py | 54 ++++ 7 files changed, 384 insertions(+) create mode 100644 Content.Client/Atmos/EntitySystems/GasTileHeatBlurOverlaySystem.cs create mode 100644 Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs create mode 100644 Resources/Textures/Effects/HeatBlur/perlin_noise.png create mode 100644 Resources/Textures/Effects/HeatBlur/soft_circle.png create mode 100644 Resources/Textures/Shaders/heatBlur.swsl create mode 100644 Tools/generate_heat_distortion_textures.py diff --git a/Content.Client/Atmos/EntitySystems/GasTileHeatBlurOverlaySystem.cs b/Content.Client/Atmos/EntitySystems/GasTileHeatBlurOverlaySystem.cs new file mode 100644 index 0000000000..a80208620b --- /dev/null +++ b/Content.Client/Atmos/EntitySystems/GasTileHeatBlurOverlaySystem.cs @@ -0,0 +1,30 @@ +using Content.Client.Atmos.Overlays; +using JetBrains.Annotations; +using Robust.Client.Graphics; + +namespace Content.Client.Atmos.EntitySystems; + +/// +/// System responsible for rendering heat distortion using . +/// +[UsedImplicitly] +public sealed class GasTileHeatBlurOverlaySystem : EntitySystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + private GasTileHeatBlurOverlay _gasTileHeatBlurOverlay = default!; + + public override void Initialize() + { + base.Initialize(); + + _gasTileHeatBlurOverlay = new GasTileHeatBlurOverlay(); + _overlayMan.AddOverlay(_gasTileHeatBlurOverlay); + } + + public override void Shutdown() + { + base.Shutdown(); + _overlayMan.RemoveOverlay(); + } +} diff --git a/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs b/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs new file mode 100644 index 0000000000..2f540b6360 --- /dev/null +++ b/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs @@ -0,0 +1,261 @@ +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 floatBounds = worldToViewportLocal.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/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml index 057abf0ac2..bf740ba1f6 100644 --- a/Resources/Prototypes/Shaders/shaders.yml +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -115,3 +115,8 @@ id: Hologram kind: source path: "/Textures/Shaders/hologram.swsl" + +- type: shader + id: HeatBlur + kind: source + path: "/Textures/Shaders/heatBlur.swsl" diff --git a/Resources/Textures/Effects/HeatBlur/perlin_noise.png b/Resources/Textures/Effects/HeatBlur/perlin_noise.png new file mode 100644 index 0000000000000000000000000000000000000000..f1422d52a8818317dd84ebc86cd1be4e68c7b68c GIT binary patch literal 77641 zcmV)iK%&2iP)q4FV7BtiksKwn+{B;TAp%m=oYDi_gKTx;g# z%uf|B7jLBt^(y;aY*?(>9;mtLJ>{gTV`g*8?RibblI;bqm*&nfD$cGh(kuC$e0M&m zT5Rl@xpDf^JZCkb>{tGQnX5xhCdb>UXIRev!PuTuS``ZDAP3aI5Lj&R({s z2iGUg|N8ao|MRQal#MjHKE}_6shmQ+XP#<$=YC`R)YokO9LMIM?N+Q@(-`4@lKeo8&PpYok~B;%Xuy3Rq_Fwd_z)HTrqd&G0V%T^VA>%o^5YI z`!+FhI*%?@hi^+;(+-%P5CD`9zgP^eE;=IO_6Ktm!x zYttlK!l2kGj&b*=9@UUr636bTa|N0#7qL+=QpDDC_NMWyKpUIY{U@RtX##(ikLmhm zueZ6a$ay0!&(!$&{>DCRr2&5Sl&i+F{&srg0eRGYfcYFd3~+ulBxL3Ts{v-`^k-YY zXdgR1!bm3yt0xrfi!L@zuTj13Mo#Sid;-uc?2K?aQo_u4XYgEKy-wn=O>6_Ez=$78_i@ zz+S-SWBoKehspGz>0#f*_l6L%+H(EY*{?8`iPEEbf~w_TsW26gDg&EFqnuHjp_-@z z*C!2zH7`77;z&s%061UiXXkAE4S3JFNe_$FoQqB5kTUgT;e2g?U(I)MXX2VCo9{?~ zvuVC#J*k{`b~Mt~Cd)xpUycMg-`Ty?9B*^L=~K1S>Sbe_9AnoU=&RosakYx~PyNl0 z|35MbK|>ZEN@*t*1gQxd~+p8}})o3+$E*BEe&6IVP_hg#q1ZGB-r zF&kX&sKVITJoTjpRjeP=eLlW6IT=!`nF!0~Tc|)MoexfMcOT>@~<|FGbEMF7;q}M;605Av9 znZ&r5(af>7fx9(XW17c_gt$hHeyRAJdrcE&jZ^vP?An0;4e(FhYJ6LGOo#dhB15ek+E+ZPHWzfK%(rk>!4s3QW7 zd^oI8w*L79pmmmw-!dPS9Ot?MZnTFn8oZXkv~BY29*c=%TWze-cO)80hav#1k#5#j z(vT;2T@^`c$ga7xvBfoUy|#I!Tw#TJ-We#RoOC20<=>o>#{J!W9bYGIC7AUNmZxr*Nt@c$}+Q~5D}yn^^M zdpP!US0C-#O--}&KYs>4nOk!mui>k0TPKu`I~d~fq;3+Qf)^6QNlu)R^ah5MCi zR@uz<97XLtyUxZm0COYBhD2I0=9t%wovMyn^1*Cy#DeWJ%NaHoU+&YB>DyQ6-!3N8 z#-n$B%EvzSo5!i=seNGQ4d~Y|Y3j^0icd{>y!M$`js|Xf*ch_#f-I0Zj zq`Y-xAmzWi?)0G_uIZlpuAZ>rHw56Omhrk;yS3kesLD&`g9EFE1Tmm*Mb-D_)%yK) z(ZlgiDe?W>V|9OgneTWV#o@9e2}djezy(lA+gvblT=6(((Gg}3v(4G%-cuZ$env1_ zP{;XrO(jQ)p8{|t>}S>Jk4m`I0MZyWzMX@1-hg}}gh~{c&pmbZqX;gu!HscqZfd1% zh;4IM`=as)Yp(&?Ex@2I*CQ0TY;u&X*aHT*iBNy{SvJP7;i1%4J1_+`gne zLlVVob9LzSHlWV>g&VseP03D2gd5;@HnnRrd*?CQ<_L|;(+2q2x^90^z5vVSpD5q% zoBF8#+*2_?J#G(`9UHs9CO*Q5$2fMDAqh0+t|8Fk4hNVz?>W)8@6^g{xtcSNPxPI z^IOu7Yu`u$Q`Hx6j_Y4@9p_6L*Z9$1$1$qsroC-I_(?CvYtF|+0-E){0*OxY89yJI z7!knrTyK3_R2+Ue0A%un04Ro&m|9yLvytMlveOl_YRc!lVY=$i-x4?~P<9Q*5tc16 zVjo#CQF_}wL*Ljt>Q(!q8V+7hzO>DXjZwq|ZQeA3M8J}4ns2MG0b-VGcz*-rtd<$c znl>R_I@+2C1%?mICanQTk?qd7}-#JsruhnQH!d-qHffy z`sw`rG_@{^(KcTkf%@fwm;%%gfGN1~n*vh$=4%EM*HpG{)>R~cHI!)!{!GZ}#Q9Bw zRHTX7+Y*2|{`kPY)r;w40KT>V=yN3CmLRo_=cYlUj=8pr#oQZQ_a}`<4K(HPP0md*W(|#f1S<0=Rjtt+jKX#o~$2OdL^T*Vvx0+EU<; zW8HO!MW_EW;Ga0=bSt?bOn*KBz!y`-2mxF82CO!TlNu-z0<_(&?UKd`HiEg%1b*Dy z#L!w2FvmBN;&QG{@V_a5)Bsq6aYRC`&l-~>18i;O7wS&Htnw509*e#K$e$Kh_gnu` z{jvORaxCeb*HSN10NxS+c8+$q9DgbfoEt)xNWs*L*KU2d&2^S1iI}yzzXH&zoM-n` zO|!T&(&6+vA6j3h{3ZZrF&4nqXtgLSiUhhiCnY8ZTm5TbW;3qKo^sh*Y%coC=Ce)R z5RFzaSy-VbAVctXUm@!3Kww~!n&gHAobmG=uPeQ3KU`D2X&%XbCQTfjes;|@AZ3FB zarA`|C}mqB11$lVcFlV%H*WiqI%c|+zwCG;EMFQ>WnWdU=BAosxzI|=iL~Zs&lH|~ z34Ca6O8%{$C`B_2MmN#<7(|q`P4Ld)R(}fvH#X~MOsu{ zC8DF6LYp7TZq@kHIm;WjKIw$x}dZIl_p6TX*{8K(rxtiFh}?3We1cYrqL?rw&!GxUQ24E)4kJ z0DlJFZ4aH-H}ki|jji)dKC#*ozD;doUL3%mzbuY+;Jo=h@6UVgKhmCYzgW{y0mLL! z3{3^E*`y}m%<c2AxHjnkuY6~{1Li8v`~ zas!ElYC@aXY;1f}8iJUby321yZjcac0Hh%$t*G2k;Ln;k+KxI?{<%7vuLJ%JAXF^c z^_o3MvELBpmH?nW1paTcR=UshPhtHY!5{Z$+J|}^Sz|!2$ljm-28iIV0>Cz|VQBci z1y7|r1w%<&C|gp1#f!mKMRsJvIvW624b$n__=@XbGiGCxt~7`3eXExd*l9oWg#nnF z!`5m5M(J<;e1i-qzvn$CZoeDyb>^|Aw;`Ks9_v|7Z?k4g3RMqay@1sak_GqiaI)dd zXXnh;Da^-|7dIr}CkX(X_dn#j`jziDB!J}y1L!$M{l~`y{As@#5upDIKn3vxOeL@j zTw+nK={CX|&<&`$qHOfB`L5uTq%~8M#zut&tZdoFZt76)8yz>2NYK3I#w9RJ0E6o^ z4To>hz z2_ptB=4>ur+w|K8{M9=DBmQBJ>ys^^o^fC`GRekg4Er;2>=&u-=RG6=>c;_peFDHF z1+gUo>KqMsP1nt1@6MJ4maG}LMyTfFS}l1}l6-LU7>F^w^V$YH*giD5weh8Cnrt1G zOW0(Zd}g+%F-k z`x@B^5Y4}mWp>Q-O|RTOXyClIR?gVjcM=95(6jt!b0^q;d z9H#yxDz7C0z)9%z1_(Cd0KeG;2CT{s#l#S}R5M_@n$2Ngh=s4-lejp!!fpY99W(z} z4Dp!hc6Kz4Nr55L(~=&x-mQ){hRlbCaJu!-_Kl=@DIQPB9NNr&Jd+7LzvKsb*dMHhHerWN}rbc&>%{F>^+d@N?~diXQ=N zrKwxC)@Qu?t2iZXPpk+4PTUmWhK0BDn`WfIwOyC#nhgMd?shiHfv*C>4GEY!m28{5 z0%g>R>oOadt%(4z0%m&IIa|xkb89@2R_6_9&3lP>x%DuYSZ=Ga={mEaA@IuXdA-Ta zmQ1N!JJ-~|XhrZvAaP*C>f620=VLef_D}>o1#mb0w|%g2_3QR_Q-|&$2(K&PM}6-2 zdp!LsVBQMrP17&77Uo;)i;Hi{zdr=v$)-5R*?k6h&D^ve0?t++Hbw^W$6oizfU}Ri z&$*8IHa<1Yy3sTBF;T#f2mspY8rSxGUV9t(H>9i?;~H%u0uKBc2{^|rx6dn)&>O&g zx}UE3ZERBd&o$PF&vp0Bps9DI=F9rQ(Z@cXp6}@!OWzdMi&PHYk`L6;)|&%LMUamC z4|)2>-velJ$X!>WK6RLTd)9XSfaM?SyDIno{1PDEK%*Eqp;0M8l&E`^wVrt3hq0obkJQwCmR!Wxm*U2pX^{ZsYZDAV*;Df!$^Q5T54{xee2UxU{hd?Ig2EzApvN!s`~`~Xsi0ze?GCI zJdX+YDAzO;cw7IXFPr>1N*rf7Vm9xxW=hShuhfXAD%1rJny;Y zqJaOlP?UbW?xZOYHIL=;6To)`$m(Y!H$bUK!aQcz<}a|hhk%9zD6)&^>D5STlINX~ z0Y(B6@T2~g)i%!Ti9te(i3KWM-6 zWnO1`T&^*DSs!=gli4D_2#7h~2nK@{hzf`^xJeGQSJ{n$pL3?D-@H}&lCR2kmr(PX zBLU~#+2XD-B9Ope0kUf}j6nVr0dV9-UArLxP2q3F#l~H4L2*N1j*luoR3l=|@rLYd z$xy3D^`mKP0?X8Ew!(VT5%=5bXrBTYRijU3Q^)E0ZC`Il0P0d}{tx+`5VyyQ2%wKM zudo-k#EjXj$jQ`=dp_;`)qr@LBB(b-vYasuuV!NX8OAY zxtsVU@-b~|3I~&I9y9Q7?Nvlj>1qj$o1?}iFl&lG6<~YqmH@c?b>i~qyc+Ot#Km3t zuY>^)vmbp?dD1lCxo+!Yn&Wyy1KPig@@eKn^2dqWGxW>Z;lN+*<+s5M_F^QU ziGlLh&2N0b{lss0RC<0B02f$Q+!!boR3_W&Hwonet2Tmx3G+YMg~tg<=UV65wm8|| zZ06y4B4X@40d~_QH_hl4#9C2cc2n$1w*n#8%ou55Ivnvzb~(S*S`I|hTJE~@vGuFf zm1-5oZivoPVQZzjbKw6J_|LH-g$+SLz05}ifOtQFKhyDTKKWAMpGbi6kJ-N=kt`k! zL3BF)`fq?}lw&t+vo?i^iL+sBO;b@MPO&7eFXmvFpg(yBpcX0x8G@>5}v4@$@UQ6XS|$k-((FIM@IsjzX>C5`j)bl zkpR?BKLKEc&w}Hc5sp&@od|#fhSXpj^GrpHHaeS`es^ulG@XA5ER_v24$2Q^C!5bC z0e~WzPH>#_zm;a)H^qVb9HW|=syXL_ia*N%R@VsnO|5R?FyB)Hb$tfst9gz9Y=D0& ztR5YxH8!!jKG)cj|NKi1W?R!2Tj0eW<^Ym`SxW}k+6m~DFDR@Jvum#3wKcIF>u{u* z_j&*22H`ROW;|{b3p@sG>THu79d*E#qyZb&j2p6oqQKLQ#}^hXYt9N7Rdc{*$&y&v z@5oF`BwD#?#H>*`vX)}@UxB~6=KN^t?%Y>?H6#FiP|bc`W4YJZ-ZbfLb8r2)m8NgC z_9dHC?^ED}PyX(KV!pe6Gmjk*-^8TN6K6m3g{}1+de@mdW6AzXfW;L3NcFc7R;P;O zI~||gxrdJd!-@OQr$cJ!yZP4(!A3nPj1I$OIT0IUwu9w=YGNght{Pya8Vt-Y2Q z9)0YuLDGJqZnl>EcffNI#d>fKYeY`316F7BmQAHVq{3tVu|~vfNdS*y)bX6l8X!An zwm6;m-pXby16fzVPYA$98j_~g+7zU-k;TNV%if($Psw!J6IZJZFtc+8R8ya7?(W>J z&-Ta>f>wWuJ?lwNz`r2?n6E9Ev^}Ef<<6(3AE=sA`!(b9ebM6>L1=tr{rDWaIcL53 zXXqPJk7IEBpMM9OifB&8@lDX#H!L*-fUT_vMk6YDojt9MXp;h4HohSNZNjpoQl!Jh z1Vs@ix9>@xdf$Ly(wp>P!)Xa8>UPEKeA@t4yGPj6+eX#sb&b7gfDP!goMf?MggE)} zL|Rgy>~T4`%?noF>gWDQ0Op!$UpGWy+Nt`i%M}&}JXUc@M8N^)H}OaHIybKc|C@SL zSdjKb;lW4bj{v4(1mBhb%yIbr%2-I}-yotMXZ*bWq)EGk!(w-!=K$YxNKHn-Y<7cDTH1*2i~@Kjw1lE34_vx236dQk+yC zDw~+SDUY_&06x3lJhb4Rv09jdF?WEk2!PYC>}R6xr~P&M=KR!uUH`cC38m>fNgP%+ zIkM@~^P1E7&ldr;f!h#(lYBU40zU$+bILiCY)6~td7D5j;G&&wV_VyAZF6=t`rMu~ z;9=)%t<ngcI(VJ^BR(f zzy23M0!g=+D$bLD>_04>jY1Nj9LFCcuqRm+hz_Z<-hK#Ct%18E; z>qtNX1P1=>#{sSH%=Rt#JKLEr4N*#Kx*TbG(pQNM8w_EXsB(MbZ}Z@e7==Fn`5u6& zP$_cEnt{Mvn6b~zj{w+jVK&X?rU_$Vq3telS`wfH0aP`Hn}nHbr2Fld4frc?R3NSF zV0zNi`UX6h51Xc$Y-0M}APw7mPfdf3abU#!ndfahU5~+WN{_0S#&%~D%lkC1Y1FvJ ze18J|>gm79Q@20O9;IAyae4BPmil80{9gjQRBk)+^2Xk?YYk?rx&IJ=%@eO`)(i|9 z0-%cO=)r%5dE00)Fv?Fy0NB3&sA}w1Ryn5mdFgiCwR|b zDo{&K&i_WH{wDJYb3`3jSnOW{PkR%KbaR{JPR*#9cg|VURQt$uJtZSdAKIesC$L4` zZk^N&a80Gp*~0Sv4Qap)k6AO=0%#%|iI6KDEyy_A)EJdNji5cLXEOVca5a9g7$wkm zuT~UUeS-`+!8O%BG5Z__;XC%^?@+slUGtvv1I2xZj+(oz2_CT~2FQ}15hmvY#xOMwD!#PTh^PBzKyxc zDYd==|D=B-YA;wE-F2pyU26#v?u}cM`JigYt*Ndz&5jWVC5;X?t_5lb9<7cf3e?>{ z`q}Qk>OVLq?R^4oHE)h@$npe#rf1VLKcZr%uu%1BM=(`?X>zaWwP`)XlYjmUkU8R% zJJ4v1ZO5}FK0gIuKN?WJO-Y-}k^nyqV4Kwzp4xa;2<_%8lEwVRcYHbfHwmd&uk*hF z0wu~uM*>nKxRG0Om>TbGL)$#HxIJ=BpV^|s`E|SJP7vsx=xo5u5prh(>pSPXeJR_X z#JCZ+=gh7|#5Ogw1=OVb?b8Vstpb^3|GAeEimk<(Ca$$9iWJ8LM#|5We6IO9osJBo_3)U13En?4 z_W}RfB>8YdINP|p{d2i_j?)v(_6aN1Mw&N$aCvsy+*psf*&nt4Dc;E*H+EiEz&}A> z5x#~1Ca^-VJTDbbHDARi^-zW#il{cd2z7iHzr35)O(ccYOR_QDV^}){hAudf1j zEK)Tg2Nn#x8sJm`U;)J6u4IxLmD}8A<67I<8jb`^8*qHHW=fPf?j!0;nHm6hWG~st z&J`%KqGfxFa}xMZQBMghpQldOSe#$!#e85kDBx%QIe^&ayQ(eM{8T)YJ?vQ7lI*$_ zw)d{ds=UPUX^$A_J5sla7h9L*RYM}NW*kvab8mVE-g7m??D#fBAoYKRYf6X8hsHmL zwK&ejeZK!fCh3g9K(gkfnp9$1Pd1+YQs8vjZSbuyU`UCn&K=8b^*E53*VtSfKZ$@^ zm)V7O<6nPld}<0gfpcmAE?@ANou@dw3H~l-DQ8mSp6|@xO^##pPy6Hia=`XqiSmKj zwXrV&qXPQowe>C8G3#Njq4I;}J<~hyo#SmEo!7SWQoY`iB`1kUw8F>$+Aw{a@AFmv zGa%NG6u7Vrp9Vbvdv)&s7yIb;ZSY4)46%vr)WycMg`r~85P%8%=a(E?67V#xxDFB$ zw!Q-=%-06|(Vr$?7#Ts^m|yJE76<-KlWT<4J8Sq3^i@u~7_eAiOj;k2F#nXOSU6&$ z;)2I2uTsNC0Dj9ycOTb04_hiW>>m23B*EmkaLd`)tRPRW{7~_$AVr#h^*a9WnTh_B!?KZuoy{|~X zJZAT9MfJRSpZUq^gOLEY-Ue8mG}xhP`CII7$}QEyZzc709II+a#helV^&g^?ajsD* zBV3bC>$YRu2DOFQCLR`pN(yDOJ5Qj-M1fulDhRarBLLSJl09l(lVj~aio!T4uub+T zf|~Ys+T!A{u}j@&*Ht5%_cvgqYADsyCU4zdDcf#oLGd#Bch_Kom*9xu<-%#R3O_$#&H97519-hLlImeqi5D_gx=Ug7N;m=8yeVnK3LOTvKR_Pr+e~lWitrKzJV;o@#t5DbmK@H2#L5HT#A7@ufbNW};%1>{E@Q zwTbCcd9?+97E8A0P8|5Ly+&Sk(rrGpv1e<$J>S*^o~u5h>PO|94OwT+~FB-L6=vQn?Y<5q4! zJI8ThMORYYOa8yX;IYpd&T*_4RDTqDh6HO%@T z+ppB8RF6mxms8YOc0CdNmV{5A6j^O>ciP9sAA5eaA7gJAUelNrP|d_wEDXmdX~{yn zNuM$2sUPi~dK?>M33^MU<~v?%UfbXNS2nZ8s2ahX!|Xu<;MQe!sD{5GF=}1c2p!Ng z^O>$hEL(uTAqC1_R;Oys4QWq3M%8Mw53Hf8K6ESUfn^?ViCyZ0^FE7-0x;E!T+OC^ z#%pOD%Pof=tWTwyP`USINK(pe7Y8OPj;vSDa~$K=V74%wbN=uD2G{~SI)cjap3=t* zQ=otDrD9hNb<^maI2OnnoJ!&?_&2gmkcJd6zf;p~NIqp0N%n+}uWR-&+FUY*6dCy~euQk{<^kw`8EPDFN*qm-aFV?#+Bho7MXq zH|=+8;(awo_3Al~jdQxxeO5zCzDa*ed(hUDimMvy@)Or@@>TWs-=6@WBT5uIOoh`F z-_hp^K&?MF`Dvp|ezRhkBtXqUdl*ovglpKQ0zEZ{?E@yqv{$X)kbs6nPQX{60N`A< z|0))aaH%~}xrE~@?Y9j(4JhV`Rw(MQz$R+#zEH-oRa~0|MghQ$I6|U8@JSPQBt!YMZFuv&X}HW!7Y7CYDp46J zQTm=pA}vkBep}k6F>~k!jMly^YGIGzq-cWnO#ps`E^r+ z+jTc%SZ`+_`BYTwRgKMSjs)Du+>6g^{@b^0TKxuMD^)zI5w!eFyxY&X~yX=>C8 zP!m~s(&$~IP=&6VvWf|_O*Pk~YfAzgfNV$r8`CzeZ3Ak@&SE;}TiaS`pUzx^X^2C+ zK9W*J08}g$nNnlZd_|BHSkG&Y1T@DkP7K($``EnZ*p09r_?EC9!NxE<-U8sMlj%*h z`wb70N*Xf9&RK4#b9Jo|+=KIeegyD2|K~>lm|QLR3plo8aPg2SANY zX{~clU3cKG^fC~h_BwxJ4by({+-*h6sFw<;0W? zz&l|zQYz*?a^Ic7FyE0mC(S47xg2#ozdF!^~BY zJJ!A_q%9F($=MXR0{sp6D_hz^Q29LbmgPpXb|NNEqyhi7R@FHkt2$R;=H}pWBP<8z z^^{L+f2O{+AF&*4auQ>yVx;2rlmIBuK)V~zPr0Y$%$s|1+ozPM9CKu#!JTiDBWJB? zgR^fQyIzhyuydsw&r?rU@%i%!K(in^rixP)7z^@T=W{-P@y|8ol%T2rls>%Yz+c5g zt+8c?cLwo0@a7=}$Q3CLRf}owVTJg8sSZKt<(d3q(f_=uhyUHoJYtb2XWoNVW)e(7o)`^*$YyyzzYsS%{2OyHjii~)a2;*B^)iiv~Qrzks8!@seA z(-a*@DRE4F+k_SPGot0<{FL-KNlhcoW9RL>)QH=)-TY0X*__X7j;Jz{r({F8TTpg$ zQ%_RlP5D3H@%X0kCj!yvVtq~3gy6U`kcP!UpXau47cJ$egikBQ{K%F{meG!huuE*wkFKnaQT9o^w?Y zxOTIy12NTX8k?A;eQleRo6G!DK4D`}dp*~1$4Lj9%Y17I00XC{hLUc#*XWBopV!_Z zfJz?&24xqEZxcft`=sx%`5U5@!2gDrqTu(p%&(hIIKb9+eX7-~Fz73g04J$l6bWQ| zx50&WZ<{*%=f456396u3;x&PD4lvzM3+BiTlV)R^X6bCLmGk<=9i|eGml7x|E1$u7p(U&;!$`(9VxwI9Q zSB-D3QQ&#Azf2tXW3DMsSNWXC277{aHYxj+eFeKiV#wA=;`Zo<;JI<~695bXQ&^}!5x9BX zI_fvn3h*5q(3#=#N!2E-hRX|5?zM6UrRN6gZCZO$t~#@^YS4Jfl~4Tv?udiObQ zK}emaT%FgD1SlJsv|x8j0=|leK~;y1UblwIU)3+$*tC89*z(sW0BR8hOicq%pvMfG z2BtX)_)%B0=`02tVR3019MhrVn)ZZ&ztYzvO9ICxX_>uvoaS!<-|1`YWZ>?K_=fnZ z=E}g|t=9n5yyl32vvmu)n0sdo&SAa6fuM zXX_*YJTI5mu1~2wWHGzNgs;mV;k5cxjw&CR-x!;O#klrAp8%-FrkXVa7^RDex`zd) zHUt|010xK2+GIswnCYnBM7|T954s1O8hAphP8F0tuH-jj;UK+Kjr?eU?-6 zI@8}?Z_X77z;P}n&S%!FH?qwmMFh~droM1Kvq$AVt0}b~DbG}|NpWDu=iC7r^P79`9nuJTj8k+;21lnnA^J;9~3M)E1Mn4iEagB?; zJ6+7D&Hk}GX2-5UC~*x%#TDnN8ezII_uZVET$rMA4)T+xM`$I%G8XNex=(Ln%gLKUjdu)VaAh*A_8e&*w6pbt|U#e zotVsPES}CEw-#$$4)8Vwt@k>16NcSb>Nd zuj(0(69_0t<5Mj%!nv_yGZz7&wSUud=Q@gb99{DrkKHv~ugUqZfHSW4mG6n{u>HXo z^8x!gBTJi_I&1v(BLJ3&b5XoD=Q!|JfWQcX3S3IkL@Zc=GkclsY%Mpx5eEMAo_nz| zY2Lig{BR^t>2`jpbF{+&0xL*%JjXP9@MNF1jW4aA2q@}Qjg2)hXA5gew}i#n+|F$Y zb(+uSsGN1p8}B!Ss_988?=dG>|8aS-k(SuFp4O0nX}!+T%h6N==w z^4(!rLjsuJ6N8g|I2Y}j=f9Lw=lv|KfTj`ExY#wv#?t0AcDQ0?$BjSEzf?qc%z#$K zUyXO;lz;A8WB&&HTm23Av*LRKMob#Nk|aW)$mW?l2?&xA1yrdCvU++_TW#FgzAK`| z#wnc&IGTEP?@xV208ivsfqzRXTX8zsbw`4mo{|9Z))!Yh&bP1fN6b&e>1}!!=E(e3 z#Yv)2hwBq_-9MiIVBxsrOkmPT!^Yben~!=K5V3}_AvMa6=?Jx4-7e_xTc48rl#i_ow}at$dQ1j(vZM3@0>fJZeu&&)inm3YMv7$eB1nK z0k?^P8msDRQx|Gom2+typ8`L-{}p<5OA?Ya#4(Ybgnvq0T>lUNRgjKV&*Ris70{uf zH{jop0v4!^ZBtij+IUTE&>Mj72paP>ou@oV1W7T|MsQvN|BYUjpRTzyq_J&=Y%T)< zw!W$>CNA#mydez@NqHh$@M3msb+v%6VwgZ85x+D>+3MCz@p|*VBQFiHm?QwdHdxJJ z>$0At*4oIo`B5ALUCcpzC6a-9*!bz|`EsQhN2VwFNC`j!{8ll$h8vZNq5U(BmINp} z5j;}@I>BU6jgqa&fcysh-F~?OO}Vhi1IJ8VJQd(K4VtZ^atzm==W73zI9+LuYLK|@ z4InDp9nor~=^I-~I&Kb z*R8E=uG?=mcME*(yxC`$e@xd#&f4=yJQ0D`|Fn-Y-?8Sgei58;ou@?f+teNFOSd`6 z>fDh~95;b~`a1tYAd#bqGk^UOU|V?D?6zdUi3;{{dJFmpK=T>{f5jqinz0fF_ze78 zRyTc7=jqxTPjap}8enFPZrgC%wYIuYPt%B>fcJJj^EEw9ZEa?`;*PgDJaykl>O~D0 zH`f}no9t|A7xPV#hgRI!`3V4iB>z_5Lpf_OJi#74?@fQwgXLSce_ix|M&D))1poQ2 zdblIjANlhM00s{m=vx4>Apz$Y&+(g;82GycR*kHQ0ZYtAwuQ=)*)>rYTbBeWL5}V@ zMzM69gYi!Q*X$j$Nl9wlXiM@f<&)F>HW5&H>2$Sg;u;&!$Kxc^{`sEfY~%*O+XlU{ z$K~G?>_KUro1WZ0ATT2Ba!TO_^Wo^3uhtg^{K}{QUm^it4}V7rU-OfI69cygc>L#k z02~W-35o`)`Hbl)$1qUrK8{g#Bw^ss>|{RNJk_Wqy#akDX~Ufjt^PB(2dQGc%p8VyUy2nUHRci^E`LQ=%eZ}Y#&liI9)2P>e`l+Zpo;d zuW|*~c3_ukdG1Y1yl(RNiMU><@@&5UZ`sYH$-6|T=KBGD{PbqVr^W4m{vEJmdkR>b zgQ~Hrrf}Zd7aQ*a>VlDcyh$VlzzK|7(sgXZaf%2eGQxDW;`V2*>2yBVAPzWbe;EUKk7Q*&wdYpk%t-2e+WSH)UgdnYKYqcYea20 zOD>gs7_8d{t?u8Y`g*{H$UlfbK5;n1V`X~0tv-)%2(d&3&IBV25*^TYMrO^&D>XmSDL_%v4=TirttBcFR(-RV-)Z!0+2w4)dsGEPi@Io6>mjK6iH&z^yat8z*(2&4F~f%jJU`%%AOFu!0?sj8|IfbzZh$<2k(EGCp?lGIxy%zl4JvO%? z0chX$VFHg`f5APO^`VILjX2O#jhX3Sb%DBZ|K>aM+4;vn@Wyv$r`wZ6{FHBO{LC>$ zHdQ`O9Sk4P_S>GYB?7L8ZFBUD$B+N#HI`Fn?y-UXJ7AS~Z7`>vr{KjBVg5sexwdM8 zEZ8m)lTFTkmpHeD=;EjBQ9e7ucS8c$T-GSjwx-duYbvRoAlXd5IvY~qZitiflljm{ zYI;)_)&YyFd-B=>e?ugh-EJ=w8Axk78yx6uYeRv*(#v|L@;$Au9+I{_jMMs-#@}^NxAlv0H{wr&?Xm`#>RC07Vw*=c;|asL-iYV{sqTre_Y>EHgD#) zeWAr2<;%JDhX5Rf?Pj>5ZGb5OAZt1k{2dXPy62|`4t$hv?i#yy^iKwsV#3C?v1*6} z`n&~y)TJ84)RBrJ#ke5>=i2l!*{#-fO$}|@#thfmh}-vc+|Ez=;|PEn*EF#6enW;_ zouu4iK3Pp4XZ8Oa1A9GTb#D#&+;{(=%raV)xB!!r`NcAnzp7M(e}M0Ecewl*Z115U2nnv)9(=E9SOM2A+_h} z0k0j?;(_aZn};)211*WeHC=z3=YRbO;3!U;@{|Oip-kA=`LvIOqG_J4QMiq6LExO9 zB4GdOQu((Lj42~m&ejIN9Qe5x#sSx1x|?G+U+t@FaESmkV8Z6HI3|D6y)@@7K>0}X zAkgIQZG&}<)B##s(``<>>y7W|3#*SL3IOn)^1soa{AIRdZY$t#Nx;;_d}MKGazJ6x ztkJI`N80|8dWrH;?alO4#fIroc{h*Gga2;=aFjJq3<^BbT-87vgF^?I4jik1*0E$4 zL=+Wh6a;?+h7_}{B=BeU-t47o7_BZ>bZowBa4k@`|38B1R1IDE;zaq0V^p5Fz1vF4 zKS$5l%5}+O3=e>Qo@mpWpzQA-l3^>xp zCG|kH<{9Vq21WIPsdHYRcFlhrH2+HA_m2RMg308R@wk98Zt7%4ZX~h4w?I`TR01rw zVGd*(n;96ie&SqJ{AsT8TitVd(59r90l+z)_tm~_3YP7^YMLp2PEw(}I6aXjXH>&% z^`Nb(*(Ts*x#Iw!sa?F^0TFafY z4M*GNOZkHHQBre3UF<$KQ^n+xR>ds&sOB=jx=B>jrPfvR9KqV?Ynk+nyZaHqMpO*9I@Gyp25h`r`w8f? zF&iSVA?<7o%ADu)U|iDPY??f?r#V(V1MPc4^pu}X9y8L z)p)eq=|xd7V!tSP+Y?mHI7vN|{TLcQ1^9~qoY(vXwzD7cReSKQ8|UzCXP?}=SKj?Xa@f5e+(;wog<8av7(v!H%|;r;kY81 zzT%k1j=AZWK<9nN`L@`VPs%1|3p;kcx$6!nS&^YVPG2fmXIpyuSGI3-yBJMdnGPlD zN$0)j4)gvY6`|0824Rwa5U(f!y?u34;GTPA{jI*+17* zabj3A-&MT73S#M=_}hMnI+Y{>t#W+&ho1nj-}n3T3BZwgo;C~{Gjq-*j@npup4c|i z&w_U5sh}_)Swm)l#|>cX-8ObrFo^`N1m?=G}*{U_$hR&K?0_~&+b+D#$ z6rFb<)c+sH@6JvLg`81l$STermF#nhsLYUggY13eTal5Gbykt>?7dDIDMwjn6CryX zXPxuA-`}40o+B$MGU!q`XJSfqkHNNIab4lpGg4jxI$x=c7*wAKseRNMr zb%iK^1t)^R6b2fBAc0D&1ODpBzYwL>5dWObslP9&JudZ4ChTvez*ndG4?rm)4p9kesnce1oZ28&5imFpsc_Sxi7Y@Fbv*r=A6hyA zo=kczar-y;{+%7Q8;F=|^BCQN%;i*bh2SlR1$ZcE!i2!Wh@jFO^NKp^mS;5?N<)pl zUcHco-gh*uy*gu!YL?CZty*!#9m|Z`Qz&ju21-0_-mN@D5O^wR+WXo)?@)$Nr-g(7 zGC>x2ha>MP2K4R3h+$Ls?OL99Q=<8eiGU{&^11xV%Ap7v?a;VzR_!bsZu ztUZz5z4PL1i{!X;6sLju(V&MsxYgzm#n%(j@q_*0u34wy+3z;b!DlE6lU7;WT~ehW zkk4hw*$9L)0Is37!e1{8B0lumUQL(!J>KAiH}3>2)(SVc@r%ob%vS&h_1lQ4E@5avTo^2le9JjPkD1& zr5!In72Ai@NbM|n260L^cp%nrYK=#{yS~9-IK-8Xnb8U|o2%mW8Rp<0jjr!@|FPWi zLs~TYSTV3`93I@Tj=3~5(!_FHbgaDVuQ>C;@}aI6yS^X(q|}8%e1db?T^);rsD7g` zvC=4y-JX=EcV2%1G;P4kBfi-KQaO|p#p|R*Z($R0^T0S^;D{JsLf^k{x+yuSvwQnv}#C z%FGk5UZ=9?ZoFLo!>hq!ycE0N&NwVc6WuW(PFdm4^*#oEo9^ zhLeLG(qlub3j0840`GW!mOp9nd0fH|20m~7t7?8a3ry*yLf-<4y(HbnLb%0UP@7c6%t|DfL*#IO(cE!7c}qMX3qsYk5;p2@lg8G@zC z#mCrESnF+tGH=5MeV22Ef$la9h3lUx1hSClq<1neOT}CJBP|Ym-GX zRpjCQ461s=BFg@>_9n2nZ_T1+^1&0LQvGZapg|_M8V2x$iVwb>r(L-0K)0PVD$nYN zQn>{bl*$FIE-5oz~iO_pXxI4-WpegN>%iyUr%63q;{O)*K3QQ@9^iS$a2tnK?!NSR8ISmv{8|LD{ z0ofU%9?iUn+{Zcep#p08Hu*2(#dj?SRcGTAZlqnJSuJotF0QXAS3T;-4`xW?pI>x} zA?}*?>FKqOk3;I14(sBo5eLMHHKcn{#h$bJl$TXmsWa+3>o&}h0FJ^{EC~R3ADzEj z)?N3wU3ls8*n{lMA`uP!TNk{KFAxGX>7h#PDTlm-x9OcM>}H2&)Qx-K7I&VXcc#4p zt^K!Oa*0!g-qWsw!B#g$QVZ9XCX{;QA0J_RVeN*Q?c}w>Onsb67|6Quvpz<(QG#-5zY4N%vAuWYuIBQ4;+Hr(P=v~xLo5{b$WK{A`nubeX>(CnnuNweUbd^z*^N&C zB^$@!;gu=>lO+}r=vG*3&gX6USdZ~X@)y)%Ptpr-xcj~|XV86ssVr8$-`G&KZ+)~S z{r2`o@+@b!nI{bYIu2&PJ!f^lD)jyf`5+-_Pc^^;JJffUx#{k&{H^sfd;|RjBR#_G z#f214N?ZayT}({+SZbl~Z1bEefX_6K4W=(89IeEr(*Kr4rRC-vPjFgVi3~587ZgbV zxAl;_(py7Sf}YJ%sJ#UTdC8NE+S2Hvul`+bPcf3+(#N&wO}&4gdV%b8lZNHjA3LVQ z++^h}piBNDk51Oeo|J}Rt{kb3LVX+c>b)u+g|MP5(;J5tIXyVR3b zXsosMwcVj=S4$cSF@U*H$&uq;WZO06H2G~BULA?0XJX%1t7i#-`E#t(q3vLR{#FpZ8ml*@(1}s+&gxp3qHB2^mAm3 z?<08AhF>jwLIAbFK$TxuDi#8+5ch>Ae(o`EQKJE0$C2GzZV%e{u;G7?!~s}#$gju;%|H;>xD*%@VAc>-IGaqHmS?|Al z<8}-d6BCdd?;tAn#;Qd~zinQ_5my^N{_E65!18qf{jzptOH;%&^e!9N^dSxvJVnhO za$e6l84B92V_L<_2b}Xs?dC?xn+E@wODI--RR8+}s9rBXU3BbUE(|dG_e3@PPI`w> z6Lc}xzS*rq_{wI;R4H#V=z?VgilMp!yw#<&A$_8MVeIINaE$Nc#j@bi7jS}})zuGq za7_6WilNZ3j}w%~m?C@$j!(rk98O<}j*|nJRU?C;G!&+VESn!(2X0`gj~+6sSD!lU zIOQdr4ta#RJP_h$3g~<-tEAzI)u?R5X?=;_h^^f47d%;hI;QX{Ou9-4RI;XktK?Z! znA2dX+AF`O@*>RM{@O&Nu>fJ}s-TpRvBk#;!+A~+x{pVEjBzrDdP-ng7~$b!Q$z~i zPAjaV?OM|cnhJFxHhLM$o7AtqI@W#6FE%R?#yUF2nQwKm^6B28-Z;&Sb-5Wt=48m- zW=wsgi-bABk$fXB$W0U`pmI`(mpvApi#J+IQNHY>_{N(`NiLwnCAAm&F0Oru;Z6>< z_0J=5E?&Bx9Dj|__Wy1txZ^#W5vcdG5}F{g-!@M5+B}{8ZuM{m($GDcJfrR_0(cVW zSG!6jAwxWy-`5xO`Ywr=O*)4OW3jyF@qh*1rkq-!F9O$tf7u|)ztzEi#?LySoSi!o z?!*yseMCj+u(?v(eA1fX-@5wbNca0?S}SfsH~Dmf6Tl}I5e_;R0j|kHqbF z^e+cPFCOn^AW*NL3^z&$`3+`XrD?`Eb^FdHZ@0~@{21*7CRNzdk*P_-6F(XmEk(*e zb115>#rNYmTyU4?%gxmQvN=bzi?969M?72>oM-8=bdsEz@fFZm{okx*=GnzIX#fw! zHTxDNZOG5%qOzFQvxFdANQvqH4j~x7Fl=}>ZRHBSq@8@o;&TmT-Abt_&kdg zQ}6*571;*|*$z!~4k0(py+>3_QF6Z=+dbx7XkUQe%8oFw{x($^I;97Xl&|)-&c!Uc z|FLX!yIagko<{;;Nu!9^DRsr81~&zgl9RsT%R?Wzz88B6Verk;X`hAWPC$gp7Wqj zcHiJ#X@d7H*)kAzXCR#AMSR*HvrbLV8MgP z{730)&{-hQOWK7jShyKWc!qd5;q90gZ_(fguXLAT@Gij;SX87>rUh`n_%Ac5?7GrU zQDHi7nuoJHTuLb}EZ++FWa?kx-lA&%N<&`e;qZ}Ro_e&`zQOV?3(zxnP=Up23j>ll zy!s~SBPyP@_~{1b+<)%2dYg&SW){+ER1GGN9lybD$E`>(Wn_)b~m!87p64FCb@$yhTB%cgY?; z)2arFS`jaVjoD6j$UgF~ma3c@n9uMuqh_;;|Ef(L)59m!1vF zKw?tdA?}E_BF!y*rcz$z*4|!KZTJ`IrnnAzbd`kSWt*PwYcF?t^pm?2f5w@{jYuWNNT~hQNbLJV#qub*poo!=*}syhX^5X;Sa>T%0X&}KEc9Ru1vn3y)D^y zim*2_T+;0Kt%fygH_EXP5%gq4pR&c+L|sJ1(uzEoN*Nk?nFsM|J9^uo9^3z0JQjTw zWb4?6#DYQvqd0bCe&G`}+ym(;YTqV_N5?GH?MN*=r6FxT zKTFv;$gfZiJAKzDBPIosqbW|J<-cG&iyL5~Y;Opg{rVM1?~Ht4^MTV;l}eDiA=OfOMPv9?l*g$kHOb9l#w))er{``l={a;T7X>) zZB?@bD{}s0Li_~fAzQY%#0({9g z5BPWer^Da@DswgY4FT|*zI>u>)YY8dSi=g9WYn&)!H05^ZDOZF<~DC)N0K5e(xK1` zoQG0}9N1KxC=Rp~#y@4qan1c{R_G$k8lHJ>64(N~kR=v`pS1&7SS31#d1BKx4i72; zSAUr-6*;UZQ>GVA94-v5D5)YqV(+AC3hf*6hPGAOp)`S)PQ9&-Kvnuzcf|oA56s~x zUd9n>e>GplF3y7yO)a$A%z78xP5jM;GZQFzQ*+b)GMNMT;B3ZA$}0H!6=e;q?CdB9 zb`pIrpBeE4r&Xya^6@a!N$S1S&n+>eyD{_1MG?UB?UQL)*2jdWvZw;l&CbV(wUOE2 zAGmNDvIIZ|&9X6b^}Rb1)(upvePZMBPR}7^yF_nNalY~4_bA1LhK!+hpN2YgH*Wr< z1a5XClwu@CoK8gh%qD{9l*^fFt8c$y*=_aBxOKx=q1X$Auh*JHla#HjvM&Ly6<_26XYhT;R5jB^V|Aw2lfYGnE)w0)1zAb^i}|$@nu~+q zo#3C4|E|v`F_7kHB_oo87aun?;B`*M0bd+vptW96Ze}5ataQj9V*YJ6?+td)AFL`` z3*MWLxJ%>X-x+sp&_Ar1M}8OP^%(DA#PM-QOB9NoL0_bZ_1V-q6Kt{zeW@{;U*^GG z?vZ$eKG&)GR@d|U)5!&4WKlrd$$uTvKz$N)emMQ=>y+Tm=c2}muYR3p*~L8x+Uq%x zm|js65bT`r<)(@4c@t+c{~WxkTFWy!#qz!EzW>9|yqz^(>kbd!?cGxnxX zoNVRBsV6Eh=MN0pKj<2##<(#;>Z;Zp)#tBh=@O>XtFlbd-+ja}DV@;QK{b`d-pqqk z0{nP9M}q0;Rk;`C4S7dbM>%imJc`=xOwtLM4Q0q2e9L7$k!d2nLMto}N11oj6=+Xp zZ`Yr!q3sd@BDC(YH0RO=9&+d{rjcX^BDIO!OK0}YFM9o{fCr@UziAPkR%}j8x}ZUh z*r%G{DdpT-A8pnoOe#ju6*NA4-{yuw)h(KP9Fln*S}om?`L9pZ?T#JMDsR?2DP@X$ zUHU;`mw3P&ZyZ;fg^dhnUhBKZ$Jw9^j2uP}>0M{0kONTiMCf=YC;P;~%eV5Zm=*cp z3nALP^F|!?CCX3yJ)t^+Gz4pQ1(wQFb>DK78{+^eryQXdtQ?^`8Ovo z1s0b*0x3k%RF&s(W|;dS7kf6H`jC~g8mSx7%C)?&w43pI@GI-bOusFB$CJdD6NG33 zDsR?~2ozqt(W&9}tUsVnbiL=i{ocyogEMJSJWNy$9+W=y;4Q!OZg}_MngJ~@hukWa zNwVjfetQ7;u>Ua(w7{GCyuiz8&V}hWQZ7d=`v*M(?QP&ReVhS$i8H+DIPXxMOHP@({oec)9=NlyB_`LK zlDb)Z5^ddnv-IsP?M%`Oa_%;ZFc8_Xk-W~o^nE;g$%_?RarzK+k)TY4!~p9mU-$F7 zjJentPH&lXlKBCI^7O}jO_N#lh*ws!7x>K zyps!W0oH(4O>U0iMQWTfVuP}b2^U!|3IQ)#tqMBeoW8VHozHq1a7 zS8&P>FnibK60hPF{Zj|{p$2xX+pG$9IaZX>NOC3I>_a2L=2c69Z!B5bfYf^KmWguT zmt=TW>mOupE4S;9Pw%lij|Z;1c-hAVW0&43s<|wZU-ZR)DCWTj$|zNhqrk;2e&a7U zngsp^b%Dd3A)zjPBS_IWt5fW={L|K-?euJrh1`__+o^-&z{=U==`8pG`OpPT z1=OCpLy#1{got~ulPM{;SsrzPGIws@d*bLz7rP;n;a69!t2QBTJKen)U7*_)GZ^Q_ z3p0|*7%~uFi4X#9YD|{k<|9O>%gkxY#W;?|1+Pv{aqOs2N#4SpIKkV6rG%0e*RFpO zyRB2%npL)+PTzv&0>g-va~ZeD%6^IqmG5v|WUa5gb?1s}E*&b10|i^Hm(?auc2J<1 z8kD&LKMeDArNmLJ(YW@Ru%a%!ExxYFVSYmEu+_ z--Sf&Y<3!m%$mi*&^vm=(w{zaQ|}v!Y(F!;-H*r?20*%-M17P~d-{Pqwdbj{#aj0q zs|_?;=gPke(S(xtWx)4l^sCL6_?|rqCG>0>9o)D087J_O(`wg$_&~B@Fc$@K+G#?! ztR$5+uke~L>CkXd`K}LMwAww!qItw`(USD5Z)^VJzuP)$tAMWE6W2f}c+?Tiu+(e` zK)%PAU&;o*VZm1nbz@p>oBxnqICrhf(RYlLtNR@E8!*AO3*NO16$K^}mGDL|N@MfH z=2BY@VCb+JGhzIwU`ZHRQRuIbbXsr4;IU6F*_7NY0r}u-ypke#nBmi&5Zn?3ww7D{ zwMd@sJBHRLtSQR9d$Mb>f^^`vFFBpg@7AV+E2?ECc2t;J=IQVBkI3tkO- ze%1W(3$1>6zOqZPif_37n!gU;SgS5cE~3q&^L5$bYb?1a@TW;vW=mKN7OEH=YViNU z=S3SmnWYb)2`3XfFCNdGUR@}p$Tv(RD`}GxN8j@NagC~oJ}FsGK14MltyWV2RS$+risNS_Gx--eD51`XOh zG8hBdu;Zt#oSkGi)tZXyRB<_sT6$J_`~Z+sRbNri`(@nd#_$I~+EpuiA8P%gK>Hgz zL!XHajpxJEn9oo{YF_fIcMdnxj6M9g4^MT>h z&8L?=S!>PW%t%t_CLFtW1IqrLi$n)0@N<5ET+RgI`jt7zi-^w zbDd{zMBUXIGn)MR*ROMKYAJ)T9}a91nZI2Z2wh5-Ubp4+yj@j7i1^;zn|bxD@~xZq zHRY;qXLRAE(&z~AG&&P>mmvpJ6fqvw)*#VjzJQFaeVZuakKI&rM7itsv{u+W6iSaX z>8rSqER11X$ZGbB$*7s;uT_aJC?Kv~1nT9XF0{_g9x3LX{>myMzxcX|T7NoL_`axh zCup4I=F~yE2vpXDFwJ`m1t4Q*YZ6!HzbqctO~%c753GPONrOz}SVjfy<~@D_Y6~VG z{Qk+E?AYpB-|Vd)9f(0Oo9WN9AcLB>lSMd+IO#IsCCNV7y!+|0@6uiDeKeoa3q#|E2Dq2)HmEF2ws`3Ln>>{}akAIgUSfo>KP>z; z4r6t~!rOlzOen*jUOxW#Li$(Zf<|&|BB1Dj{7}A;#(7y`K)lTZe_RF`H6eTI%X}-k z3C>~D->cb>V-!aCo{m%$?5opWT3HM^_*C6z(?`w?vJto&I>1$@1#Gh7TvfHvdI)%) z=bQIk+b>d|CPms+Rg1@zTGJv%^$4G!DFwd2xYdzCEyEY}$2?kVFRD#H?@we8c|Zq^ zKnqz2DuV~5qa6Dd@?3rh;wx@mH2rT`M6tA4m)Wl*T8sT)`lKjEQZ#m0ex9Gn{wwct z8+Jtm;ICP*GPW5f^&_xNlxBqFtOcoG#G?M&s`!DT|Az^koQTq{#I}nv={yxEXt58y z9Ly!qSl^4*Oa!RIqL5S5NnqzPwNAQ=@r#6Fjey4wWSp(q0zeeOlQCXWf&En`(>H(U zOF-#MlM^SJeKj74nv%u2?!lf{$*$OiXY#i^-GQCg^gyFwLtao}Ov?;BSmJ5=bDW;I!^Ic;SMFAr#T3Vp=9SI3caR0;! z@xqMj+)ku>-#a+o61El zimRxAMU4$}W=mRZ2GsU3<$1PSRDxG=Ie6a_wIS5^2U4+4Qjs4*EdMh576-fJfP(4X z#n;Ktk4{Yp=l&&dpYUHkB=8{4Zw-$wR%}R@R@R;;h12D6SsdBysl7Jk3lQ)9^8LP~ zvvXzbPJ@@k0!lwYSoSj6=6MhqoSEa&f#BA}-&IWTzo2AQmr%nszxMZsO7!azMdnyJjMCom z_vA_^lSRfcc0wml$D+#5TDOf#=%+TO%ij$lpE+bj|5~RhvT<*U-SpZ@U6?ucIw*_u z0!)d-5dRc)N9cGMxtEqdy6d&@%4`$*NNoW{aPd7%&dYr#77T+~|L_~X^+cn2LgiIb z2=p_+VC$ig!ekp}W>!DOV7h29eI=rhJylPp!AfYCZXq{^vR8R0r7|{U{>lP^&42&w zJ=*#nWZ96tjm&BaCRm2616kE=o?lSg_|6g>uHRhFZ?4PJUfH86{~vGp!UJh zaZIJwXzSrd5ocO$+|IP`px4x0m$&X{6bDZO3?AE}wB-`ii2invLnuVuYPAkU?U#m{ zW^`!L1zY;dpgcQ*t4vk8v<0ZT9<;CZfpyx@<64Vvkp>p8r{f!?S))6qZZ z3*pkrEP2+~NFDt7hCcuj_axdeiP#ge@R_HnPjWaU)}GmS+Eq^cnCD&$rgN*?o7aSqr5cz7NKEd zmubOYy*_rpo8*dp zO>pbGv!HiTZNbM_DyFg{{kTg!q7bd=39zVWi!*mnedQE1sZv2bz!Q946r$^NC8Wy?R3AO2&b}a)n|l`@ye$zmIs48bYd~x{ z&3(mfj&EMBKTZ(pXsm;axPu+*9hjsN-9awH02OdTAG~cij#M?#L_Y?(5GjdJ5sD%{ z$w6lN3QLlZw{4A1RJUIC!3x$8u2P_IBM?SaUvc z8aJeJtNgA3sk;W;Mr?#&2L-Cw;~X0!5XnviEhOBinVfY8|0!R5hu3d zKxbU8*KseF<{+LgT{1V|kCS48AN)(tl_YR#--*O!re|)1Jg#w@k+Eou`f{D{cX+ht zRiNX@I7Ho~fEh>y8wT{28%teQ&3?R2X?0KFadU2vvuf0^9lKO}Ru-`6@6=umJDfOh zUz*-jBCh5N0-Fl-wZC5WQnksq_a2-W+6WPn>+9&5nar%H`uiC6-sEjiivui%x*^y^ zPc^3M7+YOsa&_`(%uFEUTcvePsb?8AqKX+_vwpd!i&&-F@ ze-{(gji4{uAU3(V8_Rl#SfK+?oj$EBggAXPPQUPObI7CiN1irD<&C{xh3Nx0Nj@3- zD~S8Yy23hwb23Q3>}B@aKKz-)qxMJ}@54eL(Q~t;Fh$mRc6I9k{(rgEGAPnN?=;lMB z)A~5P-_o&jL(R;3_SU%}Ar3Do6JWz0_J2g|#&y@l?Zpiew!)~;vf}spBnOXfQ#z6k z=RTKqGNPsXW7mHqJ#J9&zr;oznYBmFMCMLhm&7Z3T-IY_9Z{E8w!p3CG;J|a)v1UE zcn;v%eHFV=UbDW2HN`&T+?abU%ek%yg3n2BOd($0)Y^d6rj6)(z=jmVG6J>gOn1j z#E1f^lL!%$xNtLwN;RH_0KU4LyHvUVJ~W>7(0(x}v8+N&rox^gRTB|*E(VYWH2&4T z{s%2lvDIrIQ6u8QGM)(dXeybpt19+b2xyk}{=fIDEH_)-WCFur1Rwu9(HHVfq9Uwn zki{UmGAZ?o=N#EAi7)&_jNycD@=ULX6uHvu{y#-&zUiHJ9gwD12bly^YN88Y)mvI6 zmDtUik-n^a-h72gSo#<*BBOFGP+#7R(n0rOdrcvx@1j%cIW{(LXy=X`6H2?_3gI6bC?YobT>FxW?{k0PyJOzbCq8z(;T!90~Ha zInZOO?MGl^s)tKBN~`Gzv-(1e7@m@5L{C{o>~%OWbO*kTGpWSHKslzr$PZX z0-ZylB_@X_0%)F=7eOEIzNtVBc<7Ick>b_i!O}m?6w|0ce|63jgDxnYmEi}XJW1xw zH$xW@0QAb$yr#b||HCi>yyr9yrFK2NQ&_glT3Y5nY~UeC3Ib^;xhg(V3HS1l3YUYF z-nzCc{>wn;ljzmb=mK9TooFZ( z9dCHb_$CKbUeQo;s~O1{7@!JOkE=YD!xoKGHi7}Wvl@?3T#p7N-cF>u4UuyJ#QfCj zA(qyLDRFT*g>J~`9={QrtmLq)odcz4B~Gp7WJ!zOA1H^n1l~78-~Zr8Y(|`r)YDWA zzCL$!c62xyHq4jTtXmr5mD~GApk4$Jt!=aoX;vk)mz;~S5(P$z1}{9kXiehIIs*P_ z5H-EUNLdVz(~MYFW*yK%AUN61j`lC0fXVn3l1A*3`HG&@u~so`*UQ9_DE)dyz2wVR zHwVQ|$m0lqN2BLb*{$bO5K)G(Ql>e`^_NApar{cB*@KNZN{Ml>R{h<3rHBG5KQj-jZ0v?oG_dLnq78@ml;k2Izti5 z1E2oyLN?1c2Y&0V79ob44kg&^ngO2FG37x7HJTGh24Z6o@03d!wn zKj&`p8|vj}?Gfw(9Go?GabK=)j5C3{d;HvA?X6^qo`Vb$2lpG?{wmXZx725Z^8O0D zv@qU+KNr(;X$!k!N0mavYpjjbIZH$QFa2aM!W>GIi+s!3S~SlKF7Ad(^j?tF779Ml z(euAjO?iFnjS%ZgWFl=lLPUTiHy!UNE_CN4rO%ph0C7dsKR+U(w%*I8@r_YaN$bg| zHhSZQ`Vv`%V!>0ty};ZsKj8yNvo5R4!*wy>}NX)JE>AxX`m zw2kMV0!L@d)OqJ> zJs^U$;bBaqVC}*l*#>wx`l|QtbULegZ@S#M@#u=g2TRNjX>BEa@;5elaS7LO)5UPA zH_b;O_bD7Vzx>KbGOPVZPGi?9)D2gVu@ltO&{(GeAqp?^tt6dY)ee>XpegNnm=a<@ zcrxUyh+S`fR$DE5(!Z~fD92s9!ys594*IqJquac9XeDUH+b2KLo@Nw8zI z+Hrz(#Rc~t2~v|yDK`jYOmBJt)feS;}K`9y%VZI1>8}|ew9}%7- zPKv#wgrvCLJXJKnWxZ|TTafhh6+BYH&Dfgt8L-8>#APYon$e1dxx2b|&e+~LH$Y;7 zN7OOm&8dKT*neQT=0r@Bm>qb(kuH3Ez5y`1$!y4M3f8a+h8$r8$Zr-hOSDupCN{FO zw} z`glWjpyK@6OL=qomo4U-=1a&N_dOopfzTI=IgztuM8vH}BovrO_+cE%!0t=|(pNm8 z;YNmX>a?Or#cS-6vJf46W=d0G%rdQf$gDmmgKvA>m$J3EP~*^uX8`xrz&SdGS zb?Jj^cmHE%vco7Ak=^;snZ>Sn_i7|UUln<`jf=2yyjJ1%tvSSS3j@40v7c~Scr!13 z$I+3Hk;_%T9ls*=LA{z0c1D+;=gkhBxVagmCPCX)v+nB7dJy!^Fx5W_q2J6&`6j+L zz|&rCf0nL->J4Y(2Y{zx)ou5z%qSwe{u_6G5E zy>!(xHN*Z^YSH)%O1xzHUYH-l|EUi8o^C`Gns0Xg@!r1tWQOf}^nB=sU~A!~ijF`d9J~eCI#nm?D?IH32x{7s9EQ>MiZ5EI7y2Kw?RB zM|V|3qkDVnH&A`m9iLa^eob-I90 zyB!jR18-Pdzw9xEfTulkXwK<(b5Gko=8n(n3n|Ljv5TZ^AEa!P7!U?=*YA1QTEfcy z`3GyP8H7GO!7Wx-Xw(JMWR+IlI4%=7=U*(|bkOjXijETDz^y2ps4Z7s(EWewe`)i` zQEqI;IP~wEM({wE5KD-yuV*qc1HA_TGz@OS#+j;QFk#lX4~>oqO$ImF1}(xM3-W4~ z-ew7&_R8l58_jKAVIrZd%wor_FklDGFI9CXiZM8ejb*$>MbhQ^v&T)xL$W~Xl((jX zYyVBOC2_*DS5#>()%!6U@>H#fpS7chP7H2`d^VNWnRWkW#ewq~^yYf|hOsH&MLGYC zB~6J%w3owDN+`?j-7~9Qdq>p0w38sLW9-BXg7~uK8FcQ0P+JG^K|`TCe4x*7$K1Nw zHdza0D8yebSmHG>IBGQ;JObvcl2fzZx|?U#GG%+GufBcD>WbjC*9B%<{S65s8q$H? zPtOhWvTD6z~ZX((^ zj?293sK#+c7v|HFO63baQ}Jx%P?kM^)g0-e!2X2F`V((t)N0=3#=@kmyf1}~K zr#Z);$Nj5as-3(n^4^|}KnPoAZ}y?tj_p;aU2W6l82v<0_($SkrB8dJbMDn!@Wqt% z`B~KZHw{x2u=(vmJ1l7;vs|o$+4zhJw4hJ6fYkTPZBmio^rgmyd9GOIx|aEWrW@@& z-)Aq-&ft;W#2%jqw4Tzj3BzTiFf*Un8&w-9{j-#SO?~3()1$g!^=F!ggo_*=^RK1tReZyVT<)%Ezhg{zj#_rVJ;x)Lk?+ts>dmr>x1mQ?tY&mm7P zqTY#Kh+!q!%|0nbuPvVIzIMQ;Rn)pAMVr=y<{nX3^7(`CtXp5@63m44FsPMQjf|Gx zMUA}7MA2-m`qvd1``VMEA5`_ykqDFEO;VWAyKCPiv`w5|V5bl08@b-o{hU`4km)ee znhemp3fUij{oC(625_s&5|~+oNZ;f|R?!6cYW0&!-inG+%!FP}ClhoznCw?k-=W0& zZmYG0TiU2ri)15~o8GWQ6EXAEFA|YYF`%(S>@SSdXq^$M35ZZ4vu(sj2Up8kY?< z@ZCxT@%?1&?;amzvXkbyJLP1c_tBF!{75D<$a_2vA*%7GbVF$&dcZ7f;(_ zcmakt0FOu|Ws~BC7)5rtSX{9#Ddol|ME{#q+Pttry^|yJD+hb0>U~Wu-r|yBuzjvz z!Kuo{3F}}rh_{BW)G(*}EtEoI|E`dDaLAL1Rnt5`$g9XeX8aj~Mn(ar~~F zbB+s@a-Y~6oWF9@hI8F5f7U2tdjXLp>tyxBL<`?n8vfg`e)2=QNqZNu!`hLwc|J#eoK%~ePQ)=MzZB>fpu^(_^=1Y=Y~V?+*e+orIOw3` zGJ-}&KG!fD*2&N5gF<{S-k6s(AJZ1S5?M9?-BY)`QFT+<9rmOH>E3vP-!%wr3=U%kXh)$t9F;yL+#dZ;vL{;s79i;Q)U!;5WVbndmH!i!3 z*8{k(7IGgO0G3m;)iivK$;o^uW8`YysK$Qb?vC;3i-5wAf{fj+PY7Xvy&FEs-x z1sf6>rt+pYYUu>WXu$^f)HoO(y{5T{(~5{4-r4jejPV@wjs1e!7CWzPBCzrwj789E zrV9tDkdP;#sLCWhM+FGGo!RxP^#4kAxiv#r7D4J+tknw6_^Y#j7 z0)1WX=IR^Ts-TzElw*I}Tw<9l=>BrKHq{D*Fyw4f0=*-_d!?q$_O*cSmbcw^AVCQg z0q0!DkE%W?b-ySJPlj2Ymv`ZVjs1my$lTGaRJr1nvI2fB$C-&BHON$BuwQJIYf5WsX-)umAHNOi6i-y!fcz zTq#Fx3xh&_C84b*Y4xsYKDa{~5Ri)23=ru*6zd)^6 z4zF6WUz>YD~vmTJmjDj+OR9@T7dNBZXK_<+c?`QCE$vo+a3y z&29&C`V)>;T5GI;f+R))5hETxy0O^3X_1$rGZ``8{*~l8=-;B1MD|rRADsOwwBC`e zbo8o&5(28C)~O}>o+s0kJ{s_+)m(A%##ihlrJW0|(UU(y2XlKZ>$Q}7*9<<#1ka(m z{MG{{%Ri}dByGB0;xw3fw<>09RK$AvPJV;H5*Ms2A%rN4=nZ=K&loFl+;8_d{d zrG~N`Wx}h4MG?HwdG;UeQ(E-NpT&aB7bK}9CBzxy1|EFpPS4~L8Tpqwgx5wgH?#fx z|MJj3Rt(k;{OG2xhSf4PM$sj&WG(d>#|;@b)(im~rkO1V5an4~BVp zgSj={jn34km6^GW?}V?yq1IZd^VPdN9Q+} z0UPoqt7_J`LwP^&Zl7b}UHnVf1p?-I?kAw6oPNsi?Mhl7tU6&beDRQK1jG*oPlze9 zSc*oCEb|K3&O;Ck6qf4~>lS{6QQwvV=Q>jGg9%ERo3KGM+C8RYYU5lJ@jbF8S#fvt zau%Dr49G|br@Uiv1wb7?06eI7i$`4bzP+8YsCr{^S!SSS{T9=$!tXr@jyKFQ4>v6}QFdcqet*nbiHR`2;6?LT2lR%-UD@oGh85t?e2lsmg-8v}c&CYq3j{Rg-uuuNhMKEQrw$?h77KF@lICS>oRqI#Z3yqY6NBZ+&)Roa{BnD0Q|-+ zxWH6C3Yqq&H6$#g z*QTyByK;|w1NhTiVF9{^yx3T3dP5fF%k<9Z7@h9069NoCEV)@MS`IV#@f+OWO-;J= z$ewRu9e?<|jmv$N=_0AdbM>jh#oZ`ZI1_H=3*2OQ6S@zLXa?O>BnhuZvm@Y)+74qQ zZw|tQ!3$1=ELlmVXX@7{8armM(8B$Q$`M+%s}0<8n1%%{gtprBxmSz%+)yM*7#liWZX# zIH7ADwXfMZ9-3S8j26WS$4z)Y>s^lcXJk}Bx$iDmhUuYy-J@n&z*6NMv zg~W^VNZwp&H?@({Q^}l-m-G~^pzMgXLjX^dT5pCyQGCzoQ80GVKJR8>=sRM#a|M9F z7_GCKT1sC{(>BxGN1otJRycQarM(6RpX)bO+H#5(B^efWnobg@rU@22n5ir1C>W{p zgWKRyP)@3F@jY_c&Uee^bryDtd8z&B2mkDVr>L4`!Exld@+xxpv8tJ~#6l5xbgcU^ z6Tuw1^?%wT#=kHe(1cwu9@97$LYV+%*C-dr0gt{r&pB~dg9-tIBD@1%%&xEK>vZ*w zrs@s2Z+5`G)QI;AUZ?kNj{mjs!uFjK-)O_ttDx%5d;{^*(S7ti9vDf5mk9H4O>?sA z4Px?>;>egRij^>M(^I{@>?gFyYoG}zkykxK=ThwvUPbQ|aQs2yXAV=oFvXKcze$MZ zR<+@#g`+vpZu4ln75qMZz(P%e2E)soaj>}q&fgU14t|FDEDh!Rpd~mWGe(0Z?z(=! z1UhTqzzqBnW^pdA*#rfmdoW>Tf4`)1ZbaPb_;T2Gnc zeI7D_**fLpF#Bel1!+%T_i2VS7D$j({>CUWksff7dvSB&&TZ^P((T3{-l)xtMd(+B z=A}oGcrv=p$Y~4>l3_kp%RYsdvjjR0tk&aKKYDEVQdyfr7xy6T@E{u8BGBA%ON0w$ z&nf!bZ-OT`S7%DGS0sooNcS=G340ot_P81aTG8p5urzFa*P9fRvo!5@U+FZu_3Hs` z#k@!hPtLW-lO#a(SEaj;pTG>GI`6&tR8M8v60|J7r$EC1u$PWgbZN3_jI)cbvnhT5 zf7eG9nNRu8B49^gbr!RyFa4$KiHv0y^3oUqzwptUnqA_U^3_+ zmsdgACH)C0%@V`(N1MK&vkAP6+3$0U{N(pT$E$P(cKUI*_n7VHBQnmb+qRk+iG}pE ztb6YoEpNzYOs|zhkwC{ZGTg{K98m(ZMjOvZ(*x*!y8<&o;961qExiYRmTJ z34lSx-y&a{=!hbg!27{360UQz7ugyQOrm#UTWqfdPtyjV+0Jao@YJ6mhCT2(o7)>O z#4n*op*`f~?#&GCYh&+yKeiWajA3(!;foX6n0AS0Qf7s#1*BZ>9A@W^kn{rO#oZi} zrpNp>yjs&neK0#ze4=9CkiW);!TAXT8DVhqn44BNJwS4Y$@hVRheX9xmzRkmHoKa` za*xGeIzK?dIk$jL4XJ&+&-!PFVQc47u8uqMs{d*&Jnhh&m7sCjLArgNyS9_YM)cPb zd>QBv)1?D`h4$q-jLn)Fd=JHV3_$qRaU}a`u6P^L;;|O^a?3!ICZIHI@Mn&YCXQr< z15D6e@?c1xet%2p=R$d?)4uG%UUI4M)cuEPn^`wa^g4+pYZf@@~+%>wf+H|Lt#a*(wm_|Ws^x*u}ag5$XHN)!)7^}q#m)c{O zZbPE6|I)!SehSN44s?Ke>IXS!sDlRS4 zEcLB}IdqF8sbBmozO`;w8Jd36v|>(HCL+ z*$f05Yh%Pdg~?3-YRXMz^eU&X>N#cL;X9~favRw#(wqJq=3aJ7XypVEy&w}c&c^b( z{q3xtOXgI*ISak@^*dTN`h5+>C*OoF6%=prcF8cS>z&_k{>;DrA&tva$o&4dHPdZ( z27sqr=FED)7)K)l?-CK8U`YX9mA$fsnB%a;dayjkbj`iVC;=28MJ|tcVyeS7|Iso1N+ldyq3SpY5VUN+2*ru|1PZHlfY``_F@FMI8uym=>h7HuYcI?z~YC3RHT z=)C*>oL$}KS;y9bfKv9W>TnFVUOlRL+(D!#P?$6>{cbnJ`bC?{t>s= zcmj=QQ*HM=1(0XD9(=?MwkIWY&27b_f)g)cb{Z)DZ$@ghjT#xIZYH(8Q|aP=V1T{3 z2dEJL0a-B7E0I3Z2QJ9oPuWCqvuYy)ojglljV{?S4C5a)vzl4jwz#5IZnzFyfqaZ! z&C4`_F}bXR|FA^k5`e~koS;*Fw{89UwkxB`{*M0Ub9{jNe9!6LXZ*N7h^#d_66S}Q zr~&d?I0%7UeG;7i!wvFQC-6!uw;v+TtH61w2D@C^c_;3G-vN3D(0nN2KqXkH+Hxx3 z6HGY{Rn>&`@)I6Az=NY*kZXgb+LBD;vwvXSV1y z7wj`$M^q$!o9Wv#&%bd+J@k=uTqKWbhAU@3u=8_i8Ehp@Nnmld&k`iNR(Xs&)iJnvb`k}*RmrHk8cw<$D$OXJY`Kr zTY5py2aD!UZ2j789Uv7yhobuxBT#-QZ32SPwu#nbYzn^DChtGni)nmUrYB5ZV;lGu zPJ}koOQUtkjc%#@P{sq3E30ohhQLh5`iS?LSP7`58AyKwg~#;Ko$Pa;F(m z63P*F<~e&tM$aLwQ}??;(vBZna~%iqfm(~SrTl?Q>gLx^B~n|nC*1;r-{JLvHrh8U z&^=-5E~|Y*8Fi4e*94j1D=j93{L@~XC=MBJa*@(O5Ty={h*eGPi8A@P#4Zj{f!JM^ zSM*tV6@kRmMHULAKMuF~zBD_~c4To&np1nCxk4=QLieez;Lik_OX`|jl>tO=PvWhp zs-9u%<0_qgi00^IOrUad(O0nF)tkb9tc6h`jZQoN;jU~u>!^1+!dav9D4}rFg}xWv z#)i+gQx17c1D~W|#@-&Vfg1d!M*!5vyf+<%TlF}Ljc~>GqDQ#X$V+gFD~TVNa`+Wl z94unqtl9UYHXkCsHNAH#d?e)YW&G0g@;#sngthO*LlLj<8}bQQ%@Ln3M)+nAM0;tX z6%tVGJmS&gg=g|{`>!6y^&Gzp_tsOB_rCriOkeyoT@A7z^&r@o+nqeE{WNcoZ%8%; z`YgEM)^&g8laArs&{m+1MzWEbOGIamsn8LOhI&R1=lDjSy+Aiw?g}BGcI_*vV~b+H zO3ME3t3L%8d8G7TyC?g0K-ZwznTf^crT>i^Z_kWQj56hftqTv?S`=JR*Y_uAs^;&% z8O$-3rmf_IoAZ{2za4R@IVB5aXZrH<7l@TVhh_)P0uwHx1L5i8NB1pvC*s?gcqRhv zD^_usSLC+)fsw+u44kxGR$A{4E5wWvFZl2H_MWo(eMTbICRK)>21nl1q|3;%{tbVI z;ebc?e@8X_jZ)uyg-1pt)$>$#jE(*1<@PtWr=@9@oazNis2sT&?eUb3kl@EQ5ZKEP)IW`f z*S35e4CbBLHy+vW%O5yfsPdZBVo;aL0$U*dBnKjJlq=$Sp(B9v#qUzgmk`!qojp;q zvTuSyXSGKOniC6QC@lMg&qf7TVCj?eZdKbU%J+J0q{T9*H!kp0sZ}iMy*3s`Xk40N z3e27*Y+N~#J^dkXu0660?mjjum!q z4QS=jr;|pKZ59>EpmOg;9(E;+8d}g%jrQ(-9lXQxG))RvCqgHw(^nf=JwUqBX@O}~ zxr!7jb~Uzj-!&3KVzMQRFbega1j7CCD|uq%=@F%A912gTv!{W^d1ZM{-DFtb*R(O4 zu#~Emft*>>pBFTx)=E?xl@Pt>D+)Y6g7jeLk~-Z3slf?IbDNbDz?m#jsD>qi@frP# zp9ZA#@0hvtU8Co1@SJi;;(D~5Y@}0@YCvm7?HQ?)>25YHgt_~LNl&=oc{ij@97@o_Oe4YL1FLT4Wn#SJc|o9vox+YgX#!cpN}}VOb!!ExqI@jjH@Di9xN`r25%T zNqYO*69(eGn8T;?(U7z8D$@H*IzZCq5q4#0UxGxR>I@T!M-3BXks24&c0?td7LdDA zt*!e~U%mvN$!6qJSZb+=g%d~ueX`0?{>;8C#EZGaSQJfu`=B3x15*&%1Ji>ETA;u6kHk@)~`Sz?p7r^vr zWqh)9x6B%UiS@REW&Y!_z^~-Kb7QZa_T#~m^W+H*6ykF2Ez9a3?lVjjXo7&kpu(`f z8Pv`0EKDKIHo9uXw0oN2{4TrCx_xu=!~Y<(sWKv5rhegowEUJx6{1ou5M@dbbn+}R zz$`b{F$mC-6CljIW(WY6Y(J96~3JOB;m&a4bO5~-JgZ;66zrmyot z(Ke{&1wj4AIVHB0c&yqEndQ0loQ1q$%l9>}raa4uWVkARpSU&&Xcl4^&!O=I zX|+BV)N`Pj@?|PLDC!Gc1Jj!Mw83-y9exQy!L0(nv_l-KvD27Dy5j3gckYCSypw6E zDQmRd|5Te#Oz(k(l5=S+j5zFE^2*^8v-BEYNtpRp#j`sh%}>;s1(r}Y_w(*4$}diF z0U*hNEFAPl3=}wDTgY)1#T&FiY%O~_q@LRp)+34=48qwyZZNv5#+eih0gTX%C#RLA z{0n}q-0sjFomAd+Jd%Ji;SC;8+O6w93@vAY@??MeS}DDQAEJ}tIV;T&ooRA4-J z!3vcjwdsO~qXy{1BAHhW?pl4VQOnE{xf1!==A3qBND1+g3W4%Q^_a@Q<~c4h(A5h8 zxGC`rZQgX{22(@YH&i|U} z)KuTGp03_yK4SOgGB6CPqd{ZC4C9x*V~|Jh5%c7 zI_SABHQIoI82@otH0h?(0H5_+O8XJS!|c5bQ^0^(n&6Y^3FtL%=JPxu^!P~c6wQkj z9*c;Srujjmjz4?ubze_dnTw^jnEzD1{TQ4z=UAcPFJ}E@W+fF*%h=zdwIs2>0)-@} z7L|f{ht7F2)~g~EstLL0Dy>8ME7l%s4b8Wx`C5Iuco(tYAcj(dg;W%)9dAi5|F|t# zO(#T%HRe)=6wjW$(3nDL?&m=}2O+~*z6=@MACraH+t$cPdMaCp-679|7H>u}i}dJ` z<0sH6JS|hr-MIGmuYHc2w~{R zl+qTCM z19M5LQb7G#UOLn{m?h#6bW=-^Um6*LX?=!|or!!?T=<(Pr8p87CHl$2x>trBaMeuG z5@VaIH7^7H^Vy-ZPm1NW;@cD%5A`~{xj z%k-WH^TuzhJ1rNe76YRj(i)tX(hfxqwCBq^afIR|L8%DnO}4jjeg0hR z=JCA?#I)RD`^`o9;Ef^hychioSsR&i79ECL>$gC=p&d$~>g}mgcgAEX*K+@eImH&f zx6Zk@0PJ~~GvkcHclZ6LxERk@COE^6r?@_WtTQ4GEi7dW2uNFe_+TX?6!_ZfPIF3n zL_>yU?{wv%xFlBMp!j?!uv%cny3F{*Vl9XpMY9ovupZq|cE`94{N@Ad3lq zh2fxXTy1;tH~9a_DsW1{Eg29mm#YvaY&=?7$#6rIf4sI}YH*e5XryWCLRZTiiEXQ( zW)cOd*uAsVvVY^6MD%k%3w+(HA-d+(%MQIXf2hyJVZG<~Kcw~g_6|3T09O0~SPs-^ z=@w|5eM>Nb7&*s811|uhte=Xfu%T6-1*Kl7dAz6|gq>|;w7#!+j=O*y!9%`#@`S-wQ#&}vWam)KmV7(sQ*zY-D$p2Q>Jn>BO zFy$E`xswzIuYMTJkdgMhr;(8(N%-zx{;xh!fYR;mgHcy#$?>~bGBjjbzQ49(Wz!*- z{cs*cTU2;R6C}=QXVY5e=SW(e0vCdKl~Rg z6ncMKJ)-ap*sHi>&CwlpenA~5U%voIQ}iyh9or8D=x%ujD}}qYuvur65OMMf4Bimr zm}^kC`#WKPSEdb?*UCxTTscJ*sV#ZaYmHa}41Pp6h_#WK20&vFsh=uhk8yqsyBe*F zQgmcMn*3wNyGvU1lwFH`G?T>;Z&6s9<#>ujrt5cEe8}O{O&~I)d7w2wd-XPZUa)`P zutAmDbT0q6pB624m%*pj&MIy(;2lwa3&?8MZv_;mBen^i)f{gGaB&Um@|($(91Ah- zS66Fa;B_QI*MS?^FNG|<(Jtn5tad{R%-t($^&7;Y(VwZ~gk!K?de2v|(iW`8z8G!G z_f7w>UumhLJ}1C5y&Tj!ggx$~(iJPXh6ozv!P?}#>9TQ?0@{CCP-9^jH+`9rU zSy#(5t3Dl$6k?AFubi=2Z^O+FD!9Uu1U!3a;7Q68n5+wymWk?}~CYPWD`~EI~nDRH$(PFJdF+1IG$<3)(5Pk2R-6Vbz}Qee_Eh}{N0kQNfS#hxsl}%9?nW<;v+{qTewxT z4=eyTG}y`<OA!q4pkrhg`32YO_+A;Ut?!v)Fe>Z< zYBzp;QZI-+RWQr;ws#k#-*QG=g)k8{hSyjjgt)z4TT);s*H z_NXpLYN@&F%>FXu{kCbH;VbFe|AviZ9K+EAI6u|mZ^MR)-V4%O2)}f$|2JIegCA>; z2!KyJ-Crk~QYrUPv|J>cVJ2(-u>zJ~S)Yc`W3nk4`6HMdXZBw-LygRuA?o;ISVzhC z!!SUe$AYb2Sz}wHoqyr5VJodX{;PC+`x%!41Q7KRA2WUBQBc1R)1?9lp|9@k66{($ zW_SE=_((t#+-E`2 zxq9g-W)VM}{nrG9FwfCsW2mc4mW*C5VYW{B+6(RH-G>WR@nH;b7Qo7zcD*J9?~{uY zJq;ewvwy%QsC-Tl4DXgh&*fJ6EAkLkYdLVm{z8z#j3-U-TX^<*+c*OfXy{xyqyGKs zmy5e6Z23RsrvSnBC9C}1ge!uTiH%^@`NE@Zy?TeEFTl0Hm4}x`$joDHYiCQPRQ8dO z5_;aW*@Z&nj6XM}k8)5=bx4p4{<%Dp%PZi*%6jbueYrx8BS*(M1SRN6U~}j-fIaqs zg#?>RKl`%dFqXOIL`qXyRAu_rbM6wZ?dSAFEyI##Qf-D1p{jos$8<~u zUAht{%i+bKj?Zpd5*z&zG`iKTifKH){|PV0=-!t7&;YbH{KS9r;Ne+K&d?e64g=*X zN1fQL?rHocqdj4r|F~G69&KPXKY0 zw%+`~5Cwcrvn}X*+dTk|=%%e#E9vng!<|lc&sEe)7Fhm@xQ#go!(p29R=aZ%?MM}4 zmtVj(zFBPPVt^P++)L|i$DQn<$)nJoHTx0*?9gdLYl4E0U6z=dbaoyZCCy!kI<&t` zuv72R*^IbArp3jE2AYKm#K&K1`+g+lSgrWezg%nak>GNLpJu=Rrv5Qx;o4_R^JF_w zPMC;B!0lQzw;Y0>&wy>)-Z3=~#e*^Y6>br&%4d3%1O*O#uw2hY+klJbW~FSmtQnI~ zO)qAfldC~wn2z%&zev;L&+a43PrYh#$@$g$c>J;A0vlJ_OnVzaKBS$M!p-T514u)g zx?nosey(Uo<#Ta$AoScveHp%u4x)wRg6T{cxt3?Cwf3`d9`9%ciUk(6(^j-7%ZLok zGm8`bMt4AP-98C`LER00L`H`!4SR!;r*a*sN@GQ9Z|cC#1!DcxtszO~$lc}lo%Oy= zw;mOQC8E(ic&i9a_`$spYV@N!E=8-$2&m2B4KwJ~tMo%%+XB6w88M`$UzmE$`p7Hz@WdKh3~?Eh(*sQ{&K6nij&)hfcNbNu!r<-Vs^H4J~> zJy`Ba1cP&7`bL}xAlp9JS@HIk(eoYzHf4*abv{w(4MyH*U0@P{od879%`()W1$7*D zJ}r9_;Ah@$reGI@%dq#MWdk2jVN(kDc8zHJ8AH)%`4piyB?AKE8fuz)=4j;bw1-Ay zz=N|FR`lo93`9k0e9z{^Ji14iD~Y-=W1qC&KS8adDu_IWr!sAxpLK(=31Pwrw|ddd z;@Y-p0Lr`z?@izR#0BbRK;m;H^fVu5&Fi^2i_egW@EB|7e6{+ri|ekW;2G|0M=Sa# zpgSb&yzR;fDOB~p zcywcnVD~|O4P-t=mk3#VXkgcT1G*^v+N<&I@~=}}YVfVzWXy3vY=wW#LNWv8vz+j& zeTTyip;dmia|uvzZR=H@-0MPg8)hNCt7)mZsZZ$N`{3k*j9oV-LK^AVng4?~8~t=c zt61Wk#jVv-0=%4QwKm)BnvM`!%5K7$QQ!&ksdnl@RRnA|bmMmmNZsG=M(Sd27Noe~%K0-CEwr6;PELc4zN4f%-fsQW`5||$-lJEb z;QW{&1IWbuYdsgRQQZOQh$G^93`5|L81`Cr3`HEZ3 zK)mBmy%rbRh>=jCAYLBlesqnmpSK`r>NbE~+U1=K;6k8>()bCo7z%t^9Yp^hE62iF z)`w*k50`@?piJ@yN2`gNk}V-uk(K4ZRj7Zd(aefa>$B05#V-IPi1}^~>bzqBs?;%T zx7*KD*W;k37QtywJ@8!ku2y+{QAOq}`K=0#z zzr4*3`H_k+w1u&#+nQeNA6Xjof7!R}xjKzHprNq#38y7HJwqw|AKK0Jjc&HZBwdE= zi1>w=Ys^0!Xdh7#>G_!+Wu6cm#PX+XuNW!lj3F7Dt(-ZnXMKODT64K$v|fN~?r zwCsd@n$8ocLGHwdv?}%qIDs^)?FlZeNxlm8N7MI6sXXksqip-8`gc&d_>D}&L z5F`5bTV1{NRQM+T&@v!Wp`>bmhk zFbqYMFJC*F^s~BXG5R`7D4;hdM=dE(ym&LXn7VHY@c2Q?&a`qXC_4%^REw}odk*vvIC!^hTl^yG~!Xh$Tg{5mNR|Zar+-T!A;0;uibY)7Ak8K-ib-O zguH0|9xv6=PI%9;_TcH)$`~qKmW;`uS(?f6HB0^36|FMOt~$B z34~*n^r11Wr%NO$Thr|caN@PS$>Yz1?@~o8MBtK4Yw76V+vus|ZRLZSxdFp1TiS;G zS#8SNGq=})HuOi)x(YKMLV6_z&RJupTsU3insK1PerN47A=uW&5tW1k-alw%1;$P@ zP?JYhZgDK1gAFjD{u)=p72LL`)ozt3?()VM93tU?ptT8s=`~!J5VO+C zym|+v&8D!A6COWv@<<|N0zT!XmF$-NEPIw^&Tjl(0GJKa*ev#_kOZw8KG`g(=(SwG5D39h9`C z1<-ZD_K&ETe$Wmd;jw6+=Qan$j#lNL;-~P#2DXMcr+uZH!4LB$>Gd_}US>x4$E`Hp znrfavc}z8>$(Z|Q(kF)TBlrK6(JL{Xuqo7qDfA_kpanN1p30|d@>u}kb{eexBYLxM zi|?4HZcF5>zg#{iFmZE^a+MCg9+LM??=ZB+y^SQ5kO{xfVfhwluBGWn}FR2^=qdfKVHZpobc#myMtkju! z36gNt=wjqX{$COI@w6+yWkG2%U?!glx>3We(UFY}RYr^uTbaAZKz$u)j8Kc^%@@K; z-ST zA?6eGR24Aty1!6U2#& z0CB0-7Z*|zJ=PGHcwRW1fKQ7S(tMvCT1*dJso(fb)Jt;-A#vImJI7p-2j>eEt&OQg zmo?V-syk+jc!9W39tqFvV}&kT{Y&ii=stn0ZZ3W#2FK)XXl9z>9A!vD-_AtU_e1}p zn(Gh3q&fWW_`0PhF6Z~Jk^F)LfZ75kbCpxJDacWt}Fke;w_rF zjae5T*dt%riXIPNNsQ4B`Xm@qhs`*F(ot0$oWIjp=nG1htVzA6{~w@Y(qAb+Zjj2a zdiTksdI{JX!LeiHceHr}>)=qCU%L#XzuUMh_~ozSuZ<$?r5w7v1Vi ziw>`0W_bUhgGBCbz}L_?IF;+4%ifgQUMZGX44FB(D75bM%K3B{E9auY_i`uAZ=>hK zv+>m%TVi~!S-WYnX>aQE^*1*Kfzl7uz0uj$KgKilXCgl9xf}BI|d8Y`8N5tU_PkY6MVX8eIsA#C$ACPKXj_0W=2x7E_go^RVxy_+t6`ORAN}K7@Cxvl}o)-S|v8L z3=+P%e}#krI|oFFEnXQXELJyfK}c}H-+fjES&hbJ3Ldnv?XQ3H1)na%j#qylC8pf> z=X2COP&U}#3591fslDNDmOEg2E0d#lSHg0okH>(Q!*~^PnF*p<_qU_C%5EvAkMF@a zMqoXLrh^0q@9#ku3xc5YBs(sTTZEc=s?n8-2H%;KT?38@@Xh(X@>SL%{y%+CkQ<36 z4~ie;8T9+F#g#hMi`l!97oKi=Qv9Ov>a5uLE#JwwT`&HITy1sPAfbY9qRNv^)fwNh zD~oPU-MuAkVT2O$m`}3^5I^yLKUr>iA#Py#WYC)kJOF(53!(ELzQ}ze#Ve0u=et51 zJy8Sb{>WW|Y)IrOk>4&19G){-_2yUh?Y;ejq$_60tV9 zGR1|$m^D1mObL(*%I$8f)xPR@1e9u^%cO5ZF}8jBM;_1!Fn5VnjD*yhNZYwj6r`+ks zoeYPC&%89eJVn(2^xwNu+V)ot$(X_uUX2oL7p3uKvTO`>T2QahqwlHfT2#qn&{puB zW9|>FyO`_6vo9i+gJA!njVcci<|W?=`(;)%Kih#!x}J;6u(lsf=D-#;axFxNo|;`6 z>OK3pM%3f!1rEwU`#GDVwVDXOc_Yga-$;e?c-uHY>e?=ot6`#gc}4(kcPk}b7GpM% z(xX@L&lgTMa-+iKU*aI|J+?$qUjEr?{_HczYr`nU1D}0dMKMswzvW74uR<=~|hxXrb=o6w2%N-Lw!}p@QCYwoZF3 zrkBbio>eeRTD=I9E`iv$P#?CjAV*GeH}3Il6vAVO{u%;f@_)XR=#!)jtTTt*Nk)i# z><_=P3J2QEjTL(*rW#f67zT%TO^t}w*A_oeHqbyNLgGbEngqmb}H}nlu;Eqk=UhPP=j3;XIcYGKBLR3HIUCz{j zNwWv>mnQII1seZ-C!|$(-l5-nyf9%3Ed@G$^}Z=_5SgmvcJ={{D05#3r!Zt#%N0jJ z!YJ3vbxM7USq)_rR2+VFOnQU@+AEtRa6I2!&-tu-_AG+w_eRa+ad??&OFrzvw>xul z&?tvH>^@29yXpLQp#9t*>B#1e+AlmT<;Q&SJ%u^VjgtePsq9C_K@w+cfyL|EM3a&g z+5C3#Gne8-+T13a6AZ==afRri>yJ}Thr|F823cYFjkD|h(lGEV{=%I1&Q-*zk-3HG z`x9PwMO~dPR5RQ}n|f@YRNgw4pCzY`UrQTO>Ud`HsnxIdsW3TW5`E7pI*MAhyqkY5 zvQxGyr|XkcyteP?Tz*uSlzzU4cM;s}vcd?=0xza+doper@uG;9X`to zAkNml8sLdNqltCjK2ICs-fi0utu~Vqe(is8VyMcfPvQJ%chp;$4n%I-PZV5stXntE zZ%1TxNWF0==Woxrxl}KZPi)S=7!W+$^5zb!S$e=lsO4n?lA#*pSLeE|vUf3bdQk%^ z0?^x*BW|=6h<5O``L9S&8gki;4zGF`J!qF7=)!+kRn&p$u)Ka=;n~qTY~!!m2fd$o zcm9%clVe+cI5u2}qr(L}J-<8*eS;J#*hsX(tJ1KCZjN6Zw&mv3{~mH56~GgT*p84#^m_AH;2 zZWtQ>Q2)H_Vg=JY&gQ))bg#!kKI|%yHc+^SwK;$s4jq?GPCBPXOU`B4juXwWrTe|U zPs6vdT#T7;L9@vMo_tT~Tr(Gm)6W78xRtSa>lFiJNK*lb?u-)@$HHHN9)(c#kYgilF+-{!8O@csZyA$t zTrX%Hv1TYxKdQjKFICZyW_ndLK|JdMgOa-zWLiWEdHy{fG!z&sY0J1<{jc}(@NcfI zGUwuuf-{$vW&{QC({2%Gc7Lo`Z9UmDxA;r;& zQRfqj{E3b%--P5L1mT%)L~5=MU~Jbij_%EL+whI3YkTHJj@rbA16oP)$qkHL3r=LZh)Md7(LtGfl2uGYp8wmYiP0%U-Rm)0=9tESC_tVJ1l5cX zy(Il2&9EWe#cOh2cx-+{te@sO`8~yS`=&VD`XUPuw0Qg+nPyD{hx zG|f_nWJs6sPI!x2@_PVM_BTEW{miRa8};+S$J_#ealfPN=+{vdjOO%ae=FSBg>f8! zOK)ug3T_hAGk=9N*R{cGs~pW=$8d<+w5Jw1O-O7GQU;5VhiJ}bbaC>U9GmTmTh9*R z-}iL%x;fV;uj*6M()5Omxrc^Jj0&*8=u=JeCGo#ud!-?(%7Qe?l?H(4w@mQ#HPbuO zGlmtKzXz3yN}A(AFz216Gn$F4{c*4Vi401XDHg}j%Zc)}x_aT2N56|t{a4nd$&2e} z67OdY7}~|ROc^p9OWERb!Jy{8?7+RYp_d>YrGF6`T#}82Yaj+;VNV87x7KW}{jV-eI!2NB;{r@so z<$$J-PBdTIjQ!4O&-qk~R}+CP(!Q`%2S3H{sWTcxTlJJFbJ1m1->zubfOe31atZ)V~;qWNkp4 zM;N*6A}+f7OpR<(CgMLvx%&|Dj1OZU3glMv*1I5x|~FGg1+RAE5UrfwSnha zM*sPd&F7BWu;WK6j8skcKUlk;V6mJjD@d#R_$A&gQ!4uxnLLIWR#(f1$dS)G2Jvy+ zxmi{~R#A?`wgb`Q2@A-p`5$4@nU|$*jW)S~{zByfLf@JMHHhY%EOpxtON6zdvG6Z;C_V__t z^^YEH%w%mn+W&gwfGVgl+9qUT%|wr6Z=*a$6~|`Vl!w>h)7Pvn*0EAX6>a-KH@!Vx z{&|xjy;#5ctmI|kP)l1`&OfX+xF4nAjI5hJY5u!OR83c&WUn+j&#!xu zl7oj~#q0i!%Q2>4d10CdNfzgNuHqfq1q`|kD0q-5#s0^#o1i{8_@?~pXcgmcTwclG zhg4<>zS!NZy7ERK0T-va#NT1il;1?&2?Ykqo z^!SS%YxX~0_S!mt-C?)U6^HRBdqz0~Nj_#g)H>`x){=5Ei3z3zlt zKm}ZS^)G%PZ8qS9X41CQ)z482@b+-AYh(A!I!_ozc4VejUtPnn(MW4+Blz|;tjwEJ zpH*{>gCY9y5!y(e5a&g3BL_pwHJZ*|P6bpE6EwRwQlwP7qXU}2r(~a$? zi0eQ`dN90VY{vFrnv0W^F;Q2YfMO0$kq{fyE1dc_iSS(0J7H2j@Y(nfuQZ69yY|AHD;v+bnX) z9fv2d+82j_OSh5le^t79q))<>a@M<%%%w=Wuc^WG!k36wmu4GrfWS96`Vpam8%mZ= zjV4zV>+zG2lm7}I1=PMu4M05TUruQsvEr!wTT+=!BTV4u1QQu380?^ms|cU<$!Aq3 zyvI7py!&&AQoVO#=^{_MZbVg?;^bJl)+2J|0Q8H%Y#_|BZU5x z*<|t=v+-tFew@Nqoj{z92M&h(NZqUrnnK$CIfz>ri}!*W2>O@XIV*7yX9m>l2x_93~4Fb!! zdzo)bY-gm1^n`8;{3?P2u3nb(xxUzXx~*fKzrI%hoD!%DOH>{Y3!)iNkG2EqOJD~g zqd(e8bE&&A!T`@e(8H`1*uN+tU`IQ_d-g~wMAN@u8t9r3c?-BMYCZZmpG{vcVzdvg z!M`F=4-8r?6r%IMR?sFjzoN%Khb@su7y_uEAE-h`^+$K1nL8PQ<);!{u^|b@LM|m_*khuopU#pe$zJ^LnOupQi9i>F5=}n z8i6Cp3|aS7pKGFy(-U%~EQ~+vB6%^XBQS0TIN)xx@!i^4bK=`Itj87oNdVOsY4wDb z4w~gTstHqD>|RC8dO2qW8u=OB&FFz!J`zvVlbG(U_b(GvR)S6*qf48*ZTRT=uqiyAgmc+Uebwni6>4@tO)Dm-U*ew-#@{Iz|KUdB4K@$N!%+34!sXjckOlvD4u8DBE3qHXGngSJwJ1b3_R zWtb@Jlxiho7eYCZ>Z>=zO_O#SuRmImflstGi}mke=e(7IoW>6yZBoO9_?EwEaB|sO z)wS(On*YH4ewxy((zDR47wg9x%G@(vctG;;4DV@=~fIa7uZ_l zM(u_EN^@;KW6zcm2eh)DD$cC*ua62;{!Dv;dkMp|bcMz8>k)zRWCy<^1=V>+xgUmIXx)&vH z&T^~P_5OIz`4^LZS&il$B&Ca~oPv~CqM=H2IZbq9mdmTY-nF`rBqN(}xwuC5mA;CM%;Z`jd++TkJ1hH|H_FYX zYme)9e*eLJocnlu&iTCGuh;WAmAz%G;xL%>+Pw>a?(&p0j4zm8K~RjZF7CVu+Frt0 z&WIF-`jH$sAzpN@k}K~YLOvu09wfu`vJ+Wmh=A4Tu$byW*}i(%wK2Z8WMMEqUI`2a z)`c6`Y3}w8Euui-v+eTG%i-tcQdS$qQp{ht=*nKR+YE0aIDA7VJjWUDKz6A|0jRIw zxfmUrtUhP2_si@`qPra;qm{VF9#`8Co$V*FS1GhFc+4_Q^c(J*SL zX{t}DLpWd9%LqFu5lc&4971Z?-8j*CPTpYAYDqNmTI@DIU6L4vWR}Y#`z?6z)TJpf zH46(SaF|in&UVsa15YSs6rveWyk&~-OyEBsT)TAr3<%`;9c=6!&g=Rr^v-{N!hCaR z5WKC69%aaRKo%vv5(>SR6Z~8+I9B1PTWVKq-jmaUbAi;j@-Jv}b#k38k{e7ox!@my zk)kPp8o+T{_6R5QJ4{v;uL*LRc))9Abd<3w9*@h_s4Gq3zl069J}}?TNZk^(NXmk& zgeha$Aea!tc57UoxA|%NdYJldC9%-jHPQZ$u$g?yv{zf)?s`--vqBd4-8OaubZQXd_o79Q8o_x&AhB@gi%b-!g%nxI0){Gn<|veQGnj1A31l%NHv~w?6lk_~|ovw#XSOmARUiqqY{hQA>UH zlfxG}UbZ2s<*}@uL2RT&_cU633`fhv6qsycm1F{BF&CQF}tKI%Rd3fMDZPN{&$*Mj>S4H>8`NRJ% zJpV8LNGDT}GN%8F)hmBx8`iPHPEN zFQA@~!^8WYERyi$iuW2h6jJE)@0~xKi#nG_w{q54 zsV-1D{|3T4$#()WW1ka&v^b`D@G78O7(oz%Y`!c}V*Jv2C{CXj+W|YaLYS`n7{7)2yu3Tnu|=%ya^H0oPnDVq!@R{TxzaPi0M#?9L0`PX+iN{$lH7Owo(2)Vk9e zs;rex0w@fBmW)1n{PVnVUys@%J^puj0NB zU!JvlP&*Y$uWuwrT$0mO0WC}39D_cQoDqW(l*uGuqv7(h&MkXv7FU}4c@mlm@wnMM z<%ITA*Yx#*eORpT#Osj^Ont*Qwuhyi;sw0mGAE|4XYyvdI*M_6f#YV(5I+%cTZ>;( z5b`$K&&sjSiNi0LJ1VB>9UTyE&H%s?o^7S1W|R59@8Z#rx3=xP46E=*hz*Op45V+EJnZ_?y)KK6^N!O?$L5zLa9 zV!<}Pt$rZu5ppfCq8Z=p$}_-1Wm)p#;TU2CfGra_KI@%VYEZviwlq3U5us_ykP@B! z9Xgi{v6XI~B5WC+k9dzH5LJwmtggT9q-b{EG(OFsE*$K0fe@*-5gSr8yr8tdv=(kV zVz+gOY7}`#{A;$2VX?apA4x7srG66Nt9Tc@bEyn6$2$*+WQteKe?+5(rajUl7%9hT zPsBu*JI2|>Dbvh~-(x6H*j*VO0{&p_x3GIGUK0K}G+kOoxx!j{M?5@&tkP2!Fz*uTu?zw>n zJ!q-U5bWHyEBc`FUvLwh)YyioMti2auMXMgE%WR!snHoY99mq~PiF11t|OX;T!Db( zragVuIoagb{uv)bUFFYTW;UBPEIl_AhiR57xOUJBIBv)t5EqdY3lcr1>1(CaKw;Ng zkUoXwg5ayVWd20jw+(E!bB`N`vBWe;wBUk$Lo1{i#@3>V`=uvu`xDgtg$IOBMo$5V zt6ymEg(_&$PH(n=amMnee%lVhJv?l~^#0D4kIiyhdRkHM0?+&L*5<6GpN|_!6rrkQ z$?)RahUBxqDdzS}Nwpas=+f#Wa4T843d49H5_DOYiSyQ`F1$BwG`A+>?!_~BT3t@h zKYyQB`h<0DC%F6?{oH{rXUZ-PGNYqI#!ZR_`}E+JJDgd5_>hGVPv{3m#lMQHDf zVk{>d?31%3-$G6NJAl{|En244q|H!gLb+GYkH@9e8E6yFz(e_-KzD}^mu69GKb*>< zYq{ti>K(5N$@#X%z$2!FpaA3f0wm*kx!IpMq{-VlwB=hkHxqm9gcz$B`7dobD){)g>~pNhw0uuM>Re z&`2g{V!tz|X34?tMNu}je%4&l!>^Z()F!VfLGt}eT6%Glj|LdW>MXj-520r4*?x0(|xfwV~Jowsn#>6Fr$3&yCxAPuwo)a34uxca6GI~ z$nN*H=I}fHYJpP*LIXQ(T2L7q{^Lg;)wchPg%@#>R0Cz!kWg6KQr0#^hA;OxZ0Oj$ zgSDxGkSYmczE%n7`TEXbWoGhopN@TT-0xrqd4AhJyI0rtPW!}+o-$%#{kIk%HpVO0 zbKN~H6n{=roW#ZpubK|TH`HtP1iJ+L*YLhLiR-&Qw0L%%+VXqwoP<8C#n6Mt%0q{Y z#8&}~c-xpzj#L#Bz5h}vq&UarojPm%m8&uqV=rSgnhqOPFu2RfV~V=hVRI)=m!a3l z-ZxaABg-)PUAi!*xr=i~v@+R8N2xy*w(j9Tj4=cH)5$NiG>qJg`UF#N^aX}sAQVUo z?jTtvCfDENVl2+6Ok#^DeaD~ae=hrb@_%_-npFa#nh+RlOs%D$(JrgHIoOaF{ zeIgUHJTPVTRG8Xi;}v>?sh^}2Rb+TD%ko9*;W1AR>!lwO;`>c0gK^Eql&GAFwXPNRqKSifiY zL8i0Q@62t#bd5bF@o2Ky1m)2*9Ay%JLuxg_Qmuvk@SugWKN{DFRPh$gU=7C~VGk^B zqrAoa7_-Gm>k|SBDhuyR|CH-q>qac_9V1uru-5e8y08dt$T=%u6X}e;0&aY(j~{D3 zk9g|(=x99P63{ApWU(-R9EJO94k#^yf!BZVqam1$%nZ6ykYVAi1wQN_ldpImvt%#% zA#BFn;J>BWI+y!e)?Bv{aHk|9lKi4%FYQt@i_Db!5vjjT$k?0x~ZpP%DsBhw4(DS{NCcpuq(aWq4pNILwv7qeK{Ywf_gp3RH|x8rJE3jI45&Nk9CeW|?DVxVg(3V)=KR?!jaW9!8sGS-ns949;oRa(-%!zpCkOmv(whCY;-O0^ z!M!WXYtxN*g`pku@#GMb6C!=0Berw&a&!*KE4>O<|aeta9=HOIGKC6QEycGU2 zi?%^s(JFaR*AF%tCqc45TkXuA<{;eNx;15Y+-;$y_Z}z4#)yuh3P$NQ_(}>4kv-LM zH(}k56N(MhEzFTd%fGwIUJxTKYnwc(qNkWk#b!9ioX$Hz8M(CmIgHdKLVF0q=eMA% zxBCq)zyYk99Sql8L~~*C;<6>wrhL}qoo4Ek+tbPsq1=!C=AX4SA9p^eSTp`v+B=H= z5o^>Z_&hO0olSl)Y_h9Y5qu-HcEd6OUOe%l0g>T{rzD z?mhW{$jYb9=gv!QGanUAQ7&=+zRQd1Exs=M{rvjtJO|}lWAtnKcDcm4U_<}ThwnpI zpsBZsfvqn}u(MEA#O&+TGzcDbRM3l?o%$H=ApRkY>o#!bTb6>+Or)&LXUR5HZlTaq z=rjyr%VF1JxOZosGF^pUJ2mXqqwKPt_jfVxW^N}N@9Nfn!gf}7S_t*JQJY>5hCCD9mF26z>N6i% z^lWUoYAUTJ$@EIUF7wID9A1c*(DU@LVmZg~Q~AaCO7vBwJ4k==UM(B{Z?^6&hx;4p0riHH!MMz9 zO;Ru&IJJ3I@m9h7tBbb>S#7Ri@MN!oGe!ckoRtk#ovF>@+GXo{?+YD`eV>dXR z|EpEqvk-c{Ey|2%%&?u#wnABPX_8U5TykvCPhwun|y21D=;=9`^ z4O&ZQT7^~Iiw9&)WQ>r$HDkI7{5Vd7Fb1g*!(28Ha5Q3ie~J!HQin2ZeFpLlcQCTP zh_!|W_~mPk`1rcvx9bKE}fe{o{d_&Vt&4p}D z=B)!>4IMQ!@$tfiyYNh6*TbHIfLB1T`Y>bd!3Y2uw|wb=T9^G8pdM|>&Qv9-bfs2) zb*POdrZG3$i{6}Mjxbm?>*8nlrT~`W{148BXZ;=QtaFP-!`1XJN~l5~f}dlG6H>G6 zgT6iOuFN7C<(+Hp^|=k+VvbPjtWAQx=^n%!`z%~1p94760$>jr(@kh~&z#ybu|BbH zv$yX}%FeVr01Z+({kS;EzSK=-q*RN++dN7f))JBRS15o^`l@E6#B$owrA4Vcd?s}` z9H4{bB4W~gf(DLuKixPgFwnKh7WsqlW{v2_auV40oR?|G_)-LqDEnvYB{h^Lm>8?- zRO5(~@`&pMXxA0Hqzsdbel9f~xwE74jo?S5o2+9E6eq9xEbz-`xk?i2o=1|Kk`7R) z(^^EUqVJQBy}*Z*P;Ryokp}##7Y+=OC*xHc^Yw70XOTYy?))$=)%BqEB>#&VVn62T z{Jiuw6H$ITvcqffJ01kPWyAQ4Kbbq~Yq5%*3_I!@9LID&F>+K4-<4&i8Yqr%`xj_>Jq|5Kqy zg@)D$?2{=Cw>y~cp;S4if3tBr*n(<{!$B3f3?%$k$-SQ=m)EmfU@M}6+t3eDGNEty z5u9O*eDCapQ3x{3p$?R7_dKlOl| z;+y5LCs*ue{0?&!Sk1%LwFlRH;h9?}G<#tb?%tui20;a9^xwa8um5sRB9_EoeZw}x z?Q?eDgT&GFH$L4Q7-$e!hPgF9ja^))aaN{;-Au$i>67v%qZ%9QWiCc0v8Gg}31wcQKX@`}!fve+1Y5i4<;8*einy;(FJU zH3sRAcixDSCrrO$NQp(}KX^PqrQR?*VIKI;g;kn7xm+TjpMHR4Hd%qvuxZ3-ZyAouicgT=AuP zWJC`LDS1E_k;q6lEek!}RKU zOA_oUQb})p_RWpn2wWMffBH<>J6NNiIM?`skzY}WGi|Gj-*$^fhm2EfVTknN4jVB7 z{9@H;N1}%J zb}@gm@9ep>DYP~P`OhAhCo zTkJq&QxRniz4L1C%c+Ira+jCi(TuMUcHgjJr4fI6!vrd%f1Nfwb$@z7>k`#o^WP!4 zqxcWahL<-;1oI8aXo)Iw9qIq1=Ry6JMmQBM|8N7T0N~mdS-CxRvu1MyL5St6jrs;#t7BE4NDCgT2L9*OK$ie3)ydyyR z1>Lb-zmZ-7UdUFYKnvIs1o1HgtxD1iIoDk}W@knO-+UySqjuvdSC~Q+SnG0XGfd@V zGl4C#dywP}L;k5kTO5SCebZP)=o5-!fs*ywePK_&%&U~93j^P*11Vgh!nKpFxkx2D z4@vCqg>9d!@EZ5hbM)xhoU;Py;)+_qq{RHYo(<&w^ZJqBv6fc@fu{`FLv;bajy8KK zGYajtk5vEg@q%`Y(0Ru491rmzAEn!)Zr5XFW_)It4dz)! zDK8kwP4?ZMiYAaz;kOx8Cx+NH^=A^Tw-;bXu@4RX;^z5zN=Y(*i56l#UakpYu+LVQQe;7nnEg*B%2Lzp_aw zHS}J~_?Ky4xa^|6f@C777nhwLBSdkBlb81WaDBu5HH30|$x~@rb1u=o5&Id9s=LJ( zTAPYdcYCqm9|e?FjELktie0nC9?!RTZMutS1}@0X zEp;E$j$H{V%DdWOz0;=z?n!p!?_UKod(P_^%=NBEdv6%5-aeU_2;(AqgfI45Ci}1E zt7c-uFW_6TIbH_#R$$S2pcK8lE<-rsE;+ClTH|-)dW8`jbhno!!j6rz6kbR|m1C~` z;ASs$m}}Xu{8{Q#-J1>ayT^}qY~WN}J|X}w+RAGRoA|;~^*f}Yu!@OEm(9(e&Gf1; z%d2m`W^l#rRdwUgeEGSGAMd+jhs)jp>?o%Bv>W20nm1$@1*BoBT>wfWZ%2Hs8@pxN zaP14i8?D&K_r&u+R;@&XUQ2MuY02_@s3+UKfd?z!T-BQx>`YqN;y)*SfXF|D%ri7K z;&<=eGE{C$a1xmfQU>YAn_I^kgrMfd8BS_LWtMT)&xh|2nWJLAhx^bT*32$+UzGXNc|DA49S|V_g4M*z(-gR}Fl5@*lqjSr+WkWY@~~m6eL-wvGo3dYb)w z&My@5oqU&YHx8>P2M!jOv_a{CDz!*~9rtVvbnbM<*(gv00h{TW?rK_&`ePMUry#_i z!$?Bt2W$P%XVhsNT7b~`-lTqGpyxAdum878KZg4xH3`o`RnSpxTrRg{O;>T%|Lo&J z$hQo3hvle_9wjscz1L%_@Y3;$%|6BUN6qV9g#E=x*>x?fJtQ(#ia%?<=Cu$pydb`e ztMtZj$cCA;!+zhK>ExBUzQqVm>l2`2aG8~};T`kuXl1iU0Tc&yKsonFS8%j}POxl5 zVRxhNo=#RGxQ;PszmLT)#y&H2>id+!h*($%V~!Seqm&{yLG$rM@$1o=FTd;>l zX6uM+Wk7$L8&WB|ycK+fk+E@;3^cP`zVAn!jtDU=#k^krU70@5@EF)^1)udR(4Vr< zu>s4x|G?KT`1fj;%GZSO>K2wFq#lwHP>SSjut3NxMU%aOSkYWy3Vds`xX1)6oNo%< zFZ>kiMMS<$c;>hpN%7E1po?HeMfyJp_CwuOBTtT%I*^bDv{d9vT6Dc0c^|+?vKPlb zZ~U6t4aAhHoR6wcAI`VwU%u_xj}6xLd+05?jA1*mM0QlL>`FF$>&DeQuz$E7Hrf_W zb8{Ymdolp|xO-8@=B)|}fDt(@rZ{2wy%tky&UK2EEWgVF{PN@82Cs)#Cg4%G82Y8T zxfp3LMCkd;^}Y=6DES$atL-hz?-wRVdS0v9WKX!|A$D(IFt-+Im-xGXbuZxxNc-tE z;Z1Fi5K7YeQ_!~I`8ybb`px_uwWj3IDaQ1RZ|@Re71`&Ea?BrV>LCm{b{l{N^at+0 zZ~q*Zv{#O ztkrKWef(_vs_$ZZg%UAwh4G|7uS*@LEWvMmT`NSGI-JH@o>l%cC?BQ{)K)Rhgfc!Zo`PYksUGWf(M;_;Z3BuSXiF1kQD{d0; zS2vA1SBh0M9*VEL7YYO9*3yL8JcWOB;Ivy@cReBB*6?jC{T^T)=tV#UY?mM7zDeit zQma5soh)BPDe-d4Iu5oe=4lXBuEBkNMQ{yqWOTcK1^IV0R~+8t`yx+Y&rzq(BIm=A z)V%q)ubah<6Jak+Q281@@U_TP?dFG_QNz#Ww6G#C7f#DXNrxh=F^0#5w6=v4|+25 zyt>b8BY5K#!b9WzV5hOON4ym`vb$%G^5I1G-5TF59XDvje#U0Y;1sfF zC_;|8L2E)ss%oG~_J%7!bSW9GmlJk_6bn?Z;l8UcAm z#%Ler=lh}3!|WGlC($t2iFiEtHsr^&gLDvMU)_@5GTTQ~jms?NBwyZ!R75Q3_^cOm z@TI-Nbb%z{yGC6B**w|-fF$iDcM4aXwzoS3PM>7|&3|2YIvfb$29@jzd~13D(wYID z)XCeQoc}>I@yoZVOne?G5x!AHG3-Pe9kGuA(%zl?j`_i>3!wv2j&4bD|KtlYU)4@joSV70pi`#HM6mMzQ?Qt@TW zfc8Uq@rAEmP=$x5Zs0SceB~Zbq4^NiPQ!?&i`yMfF1Hzv)@WL;m9qq6q&)1!d`CA& z){?CsSr_Us;Qxhb_c!o#o&MTTz>Q4~K7S2Ty}W)v5IbMtJlG0` zU1zfet>4UUVaB&Q`M@Z{+P;t;L~>C)Hn0Rk9k3iiqEB1*zWNk3zt*D}c^;F_%nrMn zWHoPvb%KhvT(WPnd36W{HRLG%w;l0AyzOYxnh#zsF)mjuBZ@Vv3h_gyb%R$JIbJU( zj7n2h)&^O8%j=bTHa%B)&~ymP7uYTyKeSR{<=gY`yM9tF0!*B}Ae_Ee*;kZJ%sS`- zDMLK{1;GtSMSQgg`Sl$fYuq;6`_L=K1UIxJ&4sp~@@?z0n__rdx4Y%CExf0g+d+>1 zic~8iDgD;fn13C?&)`pJ9I`(+TX&G*!8hUcH;YLuLMmDveaw&?(;+ zekJFBTDoP^H=j0qXe{FT?{dg1BtEu}wd#C1*T2Nl@UR9P#sjt>!y;Vzs-^VFasX(3r$DGFNB}o07wjK3!}FQ^G}7$rD$jN& zxzmw^-t~#d%AB z0WAB5>Ww0wrPkj_+tz%oT&yB*l9oo{cN{)zW4`bTv*zj397eT;Rg%9fY*$eYr3qoSI!L1mkxH;zL;ksFVFsxfEJL|H|Py0JRABG(5TfHglsMS z-&B7uz|jrzm!&@~|BYkIXDbVaDuD`0rl}_PGRvn`VN@{z|0oci!@?)_Xzs`lR3iifT1@w4%J zY$L5!g$V_KZDDb4)pb{ma}Y_Eqk&Fb8_01{n~+wOGRG8*oW>nb7J@e1+?nq;-2bcs z4I+FZ_c*dUYQXNYJ*tIlBZ=6|Qa4Tm3Xl2xWtMAWMrKAPO1&#h!zp}i4H`o7 z?P%T(=RBwE{eYW|o`{f{zpy*+^{KL-!I*FeVju>G^B-S2YTLhy7L(Q8gw3#>-pcmL zEq4hiOX}^QUE{T_C%yxT-uHm*rP9kBeX!Z31`3r8d?aR&tR6gHpp14;s7eQmgZZ22 zY?&u@?+7q4KHB!56Y;(JO-5q1(Pn9c>zo!*_b2d~mN!NIPX7y>n2Lln$_t$3A4V-@ zGBe8;Hz#Etsb^2tGdhVgQl4$KRg3)zXHZu&SNtqLaYbt;7?cFpJKlP~c{RexURr^C zdf_KO#Crx{o4;GpE4nvqXuih*g+$GFn8+|#YG}eEcL{Cpud554E^@bKG0*(5%RDLM zXOXNtUOk7u?|}F{5gN~wzs23EyEe*Uws<>S%{T&%9$pi*Tlh%BFR)9HKSZwtCUV`b z7=ht8rs1&apux`Hs11L@C8fhygCFSy%HsTSVHzAD%Hjr}S04j|g44wVQ&{H%BhAWx z+cSll!~BAjEybedA3Z1-+XPPdetI(QEf2t~1%4b9@y(wN_9BA~R1>57IT{;~8rXxG z)3+pmI`(fG*g%v?G}K+oj`rc6efZ)YzKMlhQ~^j?!bYM+hwYEBHH$`#bd3RC2)DB$ zfLUt~!7KJvI7YYkzVjDh}=3{0of7JvP?@0og)gUrI zbsnPx3H(96m>y6Gc&61*mD_uvxN4}ZH$KvSjlAGI5+wJ)OpiKq^Dd^|pwr^2cZY&h zEFz>oHyQYkP$uhJ9Y(ixZ#M0cnzWVu26|?pn8x#OCJ|KVCUhM#*YU&W@&Rav5#*@y zB=PQQuRufX1Ev`2BBu-uP(?ZDb9-%lQqjKVXn}2Ehqo%{OoP~`$;?=f2%G^fRt(afML?h}x;`l%L;wLWs z4>#JFDM`dE@~}pA9p_s03BH4njaPk=XH#vd)DqtUmyW<1xKdDEy-@hg&>w7KDlu2}^3ku^2JjebEp;t^1$1HUDcaJ7ldh7yqe)2c0V}(jn67zn;l57~Ztk@J>s7UpV&odt0pZ%Cjrl1+FWx+bomq`eg;N1-ZpGPiM5gz|d2(bQ<|X z&W(3%T1-gn^5tz$gXa}~^a^0@(ck-V)4D8@{+YPUGf-^`y-r5_$xl#ri7k`5Yxa@U zI~Sk=V=f#H(2vOjcL-OQ|D1CslgMT2A8_jGuC_5`f-7;QKP}v z)+%J_Z|W=z@V(jwE88 zPhovLE7OUp>=;*lTvc=BT0Y<3BRT@~upX>$+IO=EvIxY*UN+r(>Gm&x^pJQG$f&MP z!ETF{j+4Th@z4&cO?@z=)BAnqrul6_p(s?YQXq3DbJ)LHrlIH%5Q4xyRH*`0#Xlfe zB9lf6yE{3Gb{S|Z64Hrv+X6H;i42wSz-u-L_KI=<;x&N^O$V(5`{Y&`@F{O#9rA*iR38SLtJSGhOD@ z(P(%G7ul^7!to8!d6M=iR|r+5c-P!>>Fs}2U3s)%sa1f-&eK;Iu|1*A*v;ZI3#F8E z`3~zY#8Ehgxh`#FT*j7tXynqoo^QofP395)Ac2KSW-H6JqP#As{=to*my!)G_Z~}n z#$YA};SrAL@Y9|(#F6`&HCaRN^yEys!Y@$f`r>W03Y&2pG{htUG(%^0Ddd@bKDp2r z;%NOyf;YT~DBr^!_5EG)yJ&PZyX|p`uq*pcS4<}lsJs^Ov-$qxLD(>Jr+uy<*et@B zBc7olK^9^Q7a5XslvsC1myMz7 zC$arR=y3|S6p8Wz!`dV^Tk8dQ%E?|Oi$yHHiK~|!skT|EFNEtbp9cV~uc4j0pHSPh zTpw3FZ|Kfa8^5W2<3y0;LDQ#A>P$3Ehy7VS_yE;{h9z+mE(Y8-^g|ckyTuVnl#>|` z$)aVDAzraKEA7+&P%mozZEA*p6~xyD6t7L~Q{pG8Yv^{1WwgX_-K(WlNw(kBj2~BR z8X511TQHDFL>e@T4}9O^wkCSwovt9rvj)ueCyzL~=&y;j!?MB7Nt=;3d9sbUr1b4j zW(G~?Qo1-oh4gKg!+LudtYr49TG0w1)9|{OtbDKZubJL-X8mOebKMPM*#pComyXIM z&d`_gD%Gn6omKnS?~A^yn6{GBe zzsCSOcLgNK)BTSSDS(@srvixxocuH^TNmH+ZP)|`$3Azk*=a0sDNvkRw9W?M?-$I5 zhmnhpq&0>w^UgPXX(j7EFI&&tjK%}xYixFUbM0l>>Q?%(nWZ893Y7U%dHy$TJto(z zTKA+@08TxTph$J5)yV}eYZl;eZ6i-t`RGBkZKW&w+#-geO__<2$Y*1vLZoWc873pVmS}W(&U!W12(aJXJ-d?T zS6~!gkP{iGIqsF`7T34RP4ids{J-Dv315cK!a~TZZ2v@kUZ8YW24#5krZ&p3(_nL2 z^L;cgDJ(7Rb$*w*yhK=w{q)>5BsUU+6BC;viKcnM+R0Xeqa#6q(u19vdjr~!IuloRh((4yQ*cs-+>lKtr2SR_Us zu@dDc-f38Aw4#x3ZvPUqBDyzI;8ODG2_=X3aP+lWcNv%O77SQe6|ZXCH5s)JN6^h; zJ9pgg0;Ne-7EIy&ifN-9src%#v@lF?3%zAFbhunT22^Ctq?%dr^ztp@vP&8+zNEng zes#xfZeFJQd?TeJJOwv=*3HKdcb8C}fA^0L!qFs;dhyO6VquOeQ7Z!cRx;7LxJ#RL=c zFRzXoXu66Csg%snPpV+<@9bX$&CVpWvibs*6L**lOz!PdB{%*%8nyQgvv>}{ni9?D z%{B2+_N-!VVDF9D?gX&n`JF8tPHq}t#%AMJPgq%9B9s<47dGd-cUPtyKiSQ#V0>x2 z>>2b|Y5CVOCFZjt@GtHeC8kfv`GEqz2akve)u}m8u$yyXuhY4kLPob?IgCiA8}z3gd6Pv=f1ERDut^1eGqCb7y^ z?uyTBRPfJzOMT?Pmwq1Oc}tRs-d%snCi*UHLk7yPnEOL(cYfyeL%0iE;VGyRz2Xc< zR9F}pUgg>!HFkT$)xAK$Wa&tE^o^#LcpA}o2t!^4y!A852Obq|G~w`~j7jP~=u3NS ztZQ2hcaQ!2*yk%mwuRkK*moB6NbC6t=OLg)u6Yq220=}E&bN5MezCc3tG#p0oz-K| zs5<1JNwe-=3kG%)b@j1NYkpB)cx6`~k8UD@8K9qVQ`2kyAY;eE&5-ClF?~PSS5nEe z)3HJe5BSyHiX_wbdy&OWo2w8Cbx{hHD^AbX<`iDA)W zIkY1RCQNDSkg-rBJ}GxWOiIG{n?#I;nJde$rofx6GMY*!Nx5X|k6Kaw$ zdN6-u&X-Z>!Tdw4A%)0L<20>O4q-oZ!7frVDU#n zpzCF%iDCjV^N)LybBE1hQ#{FpJ-x-dI}z&A;4w#KN-ij3)sfXSa>Xzs@v@E=heyXn z4RT*Q+vs38=n0>#3p)Gy9&Ismctc&nvW!hmfBcAU>kBG$e0e8!gO`vM{Z#wROc5&u zZQ42rdzlQ917G2aM#M-cjEjNgoDVz)E`0;Uun8QsW?4TePkjH z>BR8u=Rfa%e2T}v{H3r$TG{`(&oje%qBp+=HCZiwC!rK?pTH`X_%yz&Cp*~2ORvSb{es91`ihRVzxE#hLUDjcd_$$miT^#ZlVQdSV&;B3zzVC2 z??b%29EVE;qJ3y*)LNONu+6h$ljbhV3o@E%98It|R5pcZy9>~;!=CPh2ua>2r52ri zH)}eL!6WT*`>)OY4qcJitD_mv&sH1{t3?2vuEUll`n%TnWflkmuYVs4$fk zrSi6i&>1c4=)v&p)hUHdp2}TG0P1qN{1MIfnO&)FW}ctZkuX`B#SS#pp9;xBf;_S; zjScxeI(>_7zczBZG`BEY^*L-!kZdFR^|T7n;_9#L1d8!a!Bym2ME+ttnTEztSFVZ#MXL0!an! z0Yx?HjLtHJDg)U4kJM;xE*)nfgnZfdse-EfA1L}srL1=WVqvFvK3n3>jBQ^GdyPFb zGP5@H&q_Trg=q;Vp>V=Xtd!XH76P3?ZzX1q#h3%J)w1xk^Mu*@@z zDn-}3pTHwLWsJp|d^3g-@JEQ4D6VJoy~_)LVu!q`gxXlnQ^$3M*d%;A*crc@zBz5J zDmZCA)EQv4eMJlQRO;LEjCDx*Ksy#n0dY4vrhVbQvbxr?AWNWqUo406jgT&N)i`|r zd)bmI{>39$V9IXv&8mR%_Bf^nV5tlSc32*qi~foa&*$zHp3zNw83CB!w1%|W(6jC# z!rTDNH@1B0HrPpzd~lF5rKs4`3m!u!>%U_5*uE|9OG`TyT0TpG#lK*J^&*JO0e zbgNIQgY7OU`Yw{|n)W%oKOPR%#c|2qykNd-=;hdf7n@GqxIwr8R9s4@8Az;7l=c=+ zv^zYAxzrSfnD)aiqOa;u6MK!6KZF&xIZP@e{Pk9+9SPiyzyFa#>{V8qV*JA zoI&&0bW!>zYMMD>{?q0F1kzABbJdv9akIc}umED^kVo0H6@Wk@6q+hejRSr4){Xrn#w9kl(_=X*(2cQIkrE~C z&!LSEKus(Y;l~B{_iEW}VWhWpv3DT?wI8M`H;j;p=<=eAS6$1eL;VG{${ejIN9VwH z0osIDcIT?pgVI?u6Z;uq+MvCY3?R|=ZR7IqDRCKOW$%u;^>`)Uz2mX1A-7T7Y5=(w z(ULiWJlOe;ED~M8O9t7~qrmflGn?d_To__IXuE8FONA$o&r1}GPbZ+a7uMf#*WEg? zil5zKbgQ9ZwOq;_aj|Gg4OH5UX!*2tN@M%heaT+>=NmokA}2bZcE_Uup5ZyoNpov1 z{+}M>beiP$zGRSgkMcQcrI) zC-37K3j+*0hbF<}C-Z9l=3Ko(X{`BED@ys-=g zrvCOGUD>z~hb;r@2fz5@7nQqc1wIEFBYWH)D^9jer@D7~QTWb%2=#DF%?uMpjUOiE zmGXkb>6qwk_HActqq;<#u*Sgd(Usm`1RAHpG|O%}DWxk`F0RPCBKN%K!|u&T?AX#b zCVc`+#jIj+Wl}$n_|yjPH7E`%l%6+Ychg6coW2BWsNiV;$&*dRIUjwfvYAJgJ-cMz z!<8P_2is5c_zxfPVqPQr)3`LEJjF4h$OW&>JDVZIO^mZ#_5s7%vb29-$G1WT!8;Ss zgBf~IU2E|v5HkA6r?)BsUU{RF{!Jf~hwWMS^?MR>6aQI$zPG5KVJ>d-Ntki1-zKjC zeo2BUbAWUzBU~`s<1m0Ksla+r;JQtqd|FwPzVC~)VD?HnurP%ABICT^YqjL07y&$$yuu-k|G~~_%kMw+UfkwV8NflS^NXrH0lfpAQwuX?8ae+>9vg$xEb7~2!CW2_iiE;{^qQcv88pXEh%(t^i2tyabLg*^3cO^o5;m92 zof&krT=F|mUG3kECs)R1$4`!@JD&1SC4067&j>QnZyCdxK# zHkF$O=@LwVo!SrAtemebhIp-!L^_nre3#opMYa!s8(`?m75v2J6S^t?6R z<@vdW`^oB0fu!qK>4pCq+T|7c+k%D4EjP~jdn>L#Dt{EoY2wt_qw-;6lZqXRn&*hd z88iJoPyA;+78ktFq#^r%eh0vToC5;Y3|NzV1Gt;@Q)8cFSfZ&yR%Aed6&6~vdDE6Q zhAH7NKFn5(MM^q0f7=k;JY^$`$(tmA5yPjzxoN`l9-HgtZRV(anfDq$-WJzjlPLQW zNo|0T^@#J1eFEprkGauS=Su?~%>N{cFl=Kmh4s(I4n+dkn(g{&Ob>onxWFgNJDD_i!VD@^V0e_4&&Am0M)!$9F%MTa8uXH&ZgGp zTF!p7=hGKHAc267GRM4;v&omO9wjVqwnW%riUPrTyd}GDf&Z83qs)$Jr>m)SzxDSP zcbWV{0GN}m0d7FX1uqp}JGLz{1h{EqLjZ0PQZ;*KD@zCkE-KO3`t3DF0+dgUeg{OY zE=G86h3QavKkwtdp#APX)6K>=^WTs+z`tqEswOwRL*+F3!fa#vINwoETiXo@K-(MO zcTf6lyj2cuaw^qq^8l54CIdlMldM<&e|x(Y9mjDTcmd}B|9H`d9zX+5ZM`VFmp~B5 z)`O&|s){?E{O<+(kh`Q#04Pjo=h)!1T5GjOv98m4eLqtHFbBa&+r-bw=7dfazO|_6 z2lJm586*Dmd+b{=kP1c#0bDqxEay3lPrX*>TEAHiRIVBTY6M;a)SsxOGzSxH2wVuL z3e=MDYK{3c{b&RLo>!ol_Ar~Hi7%0u8&CO^_Jr%#=ImU%5deqy6@Ia?SpBFux8e)O zXX7Ofapz~ASd2_8-w01v-=Fa2YWm^kbdScDITwou@g%K_5*bzi;2=%nRROlDTNN5p zH%aB-IIaJT{HF*|u_!=M$#=zxi>o#TBYm~tDL<^8Gc`sF0&2_~B60C-Nz@6aWSCn= z;x5*0zvfuQsesAVn*y*Tey~pqgsg2->(i%nz1pJ~lYpr6#zKk%~ zT*;b!fO~%W-*C(g4x};IvGV<^^smV=&h0R|sXG_jHea|de+d8<*w#7L$0b(*T03_G zo(h=QSd;Wo0L-yee-=B&#LNNfY@_-q0ifbdLg|d{)@=4^H}{Ogtx<|yZ8j$Y66iev z9Gt5$MYQc0j_jM)`0PM)^P0`W=4A7`T6VtT{!#VGux=hRfLD9xJk*~7zT2G-lc~7p z9BSV(Vaevgu^sljQR~bHNBjzBm@yHlus)5$j;9|VB4jb|%0C=?Q==_TsCls_6sFI( z(Ej`zpd*Mk)ggPWz}b zn9(mM8r}G}VnD5RYO8!zZRWU17@cQTXNvT(h7uX7`5AC35Zz#st8qvEQv|3OnwW9T z=BM$^BC)lU3WI)$N3$`Gf_0hYwU~6i2^K{YL`SJ^Tm<2 zn%8ll5;eaC0K2`70E~k=p`aYu068uy1C$$;hzXef&ui)7{8e+g0>Re~TsP#u&Basl zS7SQhM{7yH5eU;i1r*Md5NU~0ty?pv8qclAx3Tq6{WpG{c8bil`C)Aop|ko{gxXpn zwx+h4y{E2YEI964)`(q)UoHz zuKO(ja3U3i+XxAiFxaQY)S&Yj&ux`$+a`&Pme|>O);|dV)|dIJ1fPpFwNZjaT~A}S zb@N7jwcyeS0QStrWwoQmO?}@mMmUOiHz28At3K+1J~+F56M~>Q&ncg#5NODMiiY#& z1f18^jq+O&={EN1ha#2fPkIxC{>^JjIHnvhG4Y0dnY)Ku+)lOEyuo7QtM+N#{t^JJ zW1}kA!ZIgs!TgvbLw0>@N|-oeoPFo>4WTrqNFUEJ-*A0qZ0hxCzg47ZW88w!e`!o$ zGD1|>*?D$dk-i#(wfPB4ZwOp0Nf2z;-;Lu4kiDZi*V?(=Sd+9^J+SMH{O4x~*j)g8 zw8X=5sDy!vogF9gS2(2VLJ6egJIetZllh7QiJf2}FsZUg`ucLRV}nDd(QO>M6Ksvb6TxpC7b-wJ>? ztQm{dPqQB#>9;Z9`nz{6ZvM@jPfX>-0rKoO2@lCU?bF$Q68||j?*=3xkjUTFxf4na zAx%r-PxYC(XfO=t!|~=b_EjR~Q|uX+ch+`3J3H&Hh`I1;AFFG<}pp*(OM} z*{Ui$*Yv*?0%=UW(@J67#6hK(qD zlXxBBvifUsg@ogXN%hC$CnBP0r}{KNI(<4{4SbHB0AS=l3Ezo;>0cw%koY!YpdtM; z?`EB5oJju7Z!EZRTOYm+*C^HN++Pj(->Cji7a7#Jx5(0~#1$K{9RRK0fd zvs^j>kX|=J#9iBfnFA_y{AA7>bAUMa7CWC}Jmphb5sA!&$Op! zz9!kv=M(1~4{HhU3^byzv9#CQ{f|D+a}FC(2(X`3Zsun5yW0O0@BU{$CPthX#kE2l zoZ~mYL8IbO>y+yDR{}V65RaWXlDrWUioDXhf!RL+7{-=-RB<@aZF9%gMxDpw#@PC( z93%ixb(A(369Foo1lB4?Y?C_jSM$4=8^17)M1~4%)VQu)OZuBSy{Tz-%=~0+Q3SBQ zEf^*YbHvALq{T2c{&{vnU>={nblR|-Y>ma}^5rl9u{ast5n*=#aPOk8j`W>RpKwPN zV{P7CG=_aP0zl2x*7}U$SK}xn157|O4m!z#Xve^F-=;u-jgkCjK5hU+joFe~Lq<-p z&GRY;^R*(NO^itdxLBCExiR*eccX4>w=QZ91%QrxSuLxYX2&j#I` z)7anE=k0SQ7+MjK=6;G-Nz~vod*|!+)OL@inn}4yc;(*oxOv&1Mberv-;XcK$EMx1 zhTIiaE3UzMU;==N036@Ffo*dp1%M)nM6jxV8lzndH#b`>=100?z>>Em{RZ$iW3)Ni zTDyJ>6qG1oQI$3;ksP!*Yh#o3_qx20G3*Sg<{> z{sf@jfhFQ^2wmaLrXJZkvUO#j319-?yz^(GdY)_7&b?`B@XqbwZLaXoA?|@fN$msl z4fF9?egM$r!x6vgJ9Eu`fDUWb?PrG3&c6Fn0pP%jO@!AlnXI5TgyISm`zGJrqBzob z=b6v*S=m+$w413RI#rA;CRSAG9X;Dr8xnD3)XaZF^qW3z-5L2ZOZ!TZe(U2IqXD*g zPLZlI2W&UzRessuQl|*Ov04$3Vq{_=tp(OTNx%&ML8#|de)W=$J{S8r%sohAW*{jfjg;`e_6xWZrr>5_uM zp)<-+rO;f$m>(<#4Bj2j@g4cIIX3SKT4O-&ia?F;&as%>dFCGjWp*CNcSO|?-kc|a z7VEne3U004dbpZyh#!4Wc~O2cLT?4C0xs7lU324Y_J_+k?%VAzdv1L}pBw==k%Qzm zNmA{vbBtqe#GS)DW=dqRdUv&kVn^KpIM?v*$OeD0&8PU*K#bp!dctcQbH;ZBK>7gS z^jVFq`r=yH?*UK)F+x|9G~~Y#0_@p=URspKH}t{f%-Ob2^R6!u($;5HIOx+k-aJ@L z3XE_rHupR~ue+Gly8$=n9H$}SId>}xPy3s?LSNC(^Ev@#OH!(@d#$diV>kEl^E?>g zFo4|>95a`@4KXULn#YqEPFr^Se}3&SK5xj8eiA14A%ADo+wsTNrIw#~!r zo37z8j_C-Kjk%4-ZFUu>D#S(rV7`*zQ+b$Usa&Y~alptnu*ya9VGDrFj}$WlL2T>x z2WzXqyaB`;;#aSezp4KlcL0uIP8i5DUtA2S_ED_hzW`#qOl;42++K3{?0jWDw5Dn* zeG!ln0BU|GSUwkb0Gy>eM8L+^6n7&8RDV`@=-WI##pp;MfoN;0UjsG@jGvNr3m|TN8=sqT zw?d-@$z&Tsch^!|hfi*NtY!9SOcAC6_cTt50p~%oVApUTo^f1lB#dU5=f+UN(apcz zhiq(xFUkLp5z>mIoUgdH?u`T^|9}4Zhm!vs_l$|3huPHOy#D9sfR(ciF*3p6f}Y2$ zAKM&maTCb53xYnfm>beZLtj(Tw{4VP?OHfL)x7ide0+nC1E3`6()>SRibNBkVtfq& zyV~3MyCwb`l52@}GiM_{H{(0sZUjIhcAW@Ndlt_-kah&BL;!2=#JIDBfjCAYovkoc zeHGRno1Ym#-&Cz6T)L@KhTm#0pa3}J@BaRe>zls+p}~CmbHE!@N!|dv+l(raZZ7q% z%lYEQR~r+7GAU5yqa*%i11k~M2!Y0wPv=n!Z^3cr|>u=IQJqWh*mf;|Cp~V z7dVF!O^nRb*f<`Gtsy=Z--r0oVmK2is^58LxBk_q|CzbF0Bk-h0FoupQL3jUe=NSV zDcn4+k6N_G=bwC+v;hhnCy6gsQ2364jl+l^pB?e1Vr1*i;#0-z)|ti7h$}|;E>>*Q z)ItjuxAvqS-9B)l*NFmWN&K?sCI(e6ZQZe$)qKfkwinnq?f%Ap2b|=8p4oToGe29h zWNUM~-l<+1oW*^bh#$vdF&$ImIKNds)i@{s&TEC&IF7<0Cjyv%Q2?Y(sU~X7B^m_< zTX5EoT~G7B0l>|Ma0QIdj9}P!YQ82m1?Z~n^bP%FZIwS*+{#xM*M|6KT#3XnR@LXc z)0%eyPTRCD%(qm7YCo{?e$qA~U_%a0gt_a^5}0nuuNi~wlg7_UVh+G(dnE#o{E4d0H)0e&TCeviTIK^ zfNX+lH#A!yRp`weEFUa4^O>Dv3#xo_q9~aoawLC8^2tB+t?^IQOadZBJk948Qch++ z)?WejE%-8fB8=WPwiZkTY{$4If0b|MV>(uGHT@LvGqIxx1!GAf0KXsy$#C8YXz@6a zKikV&5wOLo(+BgX*4@SW6|lOEWzLcKBZ3?5S=U`MWmBe?x;y%a)jeK;iTSBoO8`L z>P4bF7`a#`2?b z%s$P};zsgUJ~+}&@v8dVh-8;9*9X^1`Q6lk5)KaCly0&&f6fJ zdIx~LZpld%aaydj5t^jT^K5?Rp98q0!e$JPkZ|1jIxSYpg%cu;57X4GO6Q9LL$<+G zkvpQ8<6$4>mjVa1Nj859*zAL3%s&U5j+h;>V1C$ZHZG2t;z*>n5irTre%lDGIaYJb zNFCeF_VboI1M3u@^BdQQ{e&+2O#%EsU|M4*^wLAcN}b!#20p@(uvnjQ~(ZoWLIYx^^rns=rfhjWvbqKo{Ge=k%EES|1bOGv6_P ziU8+aX`?&vYHZG}@~dK1z;=`Kv|cK<2B^}tId;>|t$nj+-F{TpRGfIM##QD-jWS1C zvyKw+Z%B-Zrn5hiFk;tQJN??9&}DlziN}pEY>t$-o4S~OH+|K3U*b)`wX6{5<4#d;(eBF>rOZ?8*qM!9i1m*lV zg?Nscxs9ymYDJ6^05iT6Pa^^_{4W=P5j*;m+RX7BxU=}$`bqP(0szg4#LYfed1}N{6FVcu&DuQ0)vxlM{f47lj+*tH zxHLc4HUIR3nDDD5Ee3#SN=T^9$mUXsQs)}wq^_};n_^bhkUc*C6!E)xSv%Ymiu_Iv zW^rNe6!Efa%IDKo`NW=a!yss>8om_(DvwVr8GGv?Pgsq~kfymduoYGj1gU z7`|-c+6VwVR(ovrV?Hat)OnnzA%6Cg!l{-M4q+Y=8RNCIK1{SU*PON3`YHZ=srcG#Z2n zV(QG(6mdglI0lT)GQU+0RlHdEGgiz8n+xl}iH=0Nb8bgAX{=jg@ofB8 zqK*L-p5I#PGxq?Jf1Ag)MjLEVWUcVT^uP+7B&-0RKP1!FBRJ2gY^03`BPg@S)~IOk>oZcPrkM z0Biv}k-rjC%+kCFjL9!`&H>g0P!$J)Q<~R-X_GTWu1x)^IoNsUGaJjrtt@Tg?Rd(! zdE5wq>0k2U*0+Xe6;Uz~;0W`nZ^@L&fU~I z!w6OzP6#-En7EqfKgE7P-beywj^{+D5=QMq`Py=d{1X5chx&IZbb1CniJ$#sBXgXc zb3vy?V0~P2RI!|EZE_v4I5JaWqb2vv;-hcq<2>)$F%a2+g91q$`$hnqbMqPF_(?+$ zXn_L>?Z(d}e%$M~#E0{ZJ>QTa+Z&Fs-Vh2+%{IbS;lyTM%prdGYwk(4r_R{;rC>NO z!{Hl|gg&<-&h4MHU)b26Al97ktu^(wCH|Se#4|3IUk&i8uRlfSS>a9M$1kS9gkqu` zC^Y9bh8uw;R)MlplI@qzB$e215CsrVMZp;duOs=hA0J8P1X{`)_GtwGi*WoK#kQJ68|~I+Gg|P z-bKt#^D^PRsbRKeO>UeBQ0=$-f10}H##GmD$iDU8i2-c?ZGU|afC5_-0&_szJnVUj z10*^ukVJ;cHf>cBF+mRKSbLR6msd7Evj$)th%w)v*!1O$$^I8Pt&8$0gtT& zh)O0Zwzd(h&9!ZPCop4V+v!6MdIFd+k#&VhLmNeOVw*GE`Qq_!l#$z^K ziqZM+z!9${-`MNc6p1}GwHFe}H*Ii^h9J~K4Cdz?Y1@C@WICjfXQ#|-E52t_jG!wb}0t*zkU32#GlS#l4hdV ztt1QzKbeXm;Phccc>~C<0Gij#zb47*cHkVJ#oU5^D;%~PsM&N3tk}lb#52cL#f|YW z5pZHq&nmVJ__tu(0>usSC$Wre&TG~_`Q!E_wnOmS0y2AUO!=PtZ01Ncqi{2QhDDvn zm{hFu^_=(H@Gby;<@c#g#Bx^~4f!{w@OFz;PjKejSpKIW`Kw=7ROi}!-Q;kZtE-g& zn1zdX0WeW2F-}?H2UJi5oc;>HSmNB`n-1JeT%h%C(Bo0W*%va@vXx z6_aYG{A@7*`^@ngd`LBne-4=E-7SArxA>e0ZN}JQ(^CO3=bOiebKHC%^2Qc%O?~Y$ zu8*rZg-LIjtEWl;Fd}n*0{DK$|2bvHiTtOd3}~Jh68C0ICIs-h0|7^3(?2#JizCH} zzRmNDAQHGLKiPP1SOb=94b|GrIB>(dTr*SoN?`01zXFZ~BHML)W35zltej0k{Sezwyrjj_?tuaXuwtnb<_Gz}#V*PT%MTwPV|a;#kdQN1}EB zz|Jut@J1YGu5`_{oqe2tGhTI`6?~iPlrQH?A`A6yMaAiIs?Mu-N9uun->@41ZRck7 ztH{3T%zNW{qc&xPJBe4Dfe z*xv?WwTa!F$zL~5vkA83fqgNSjriCICkLc#Tt)gDVp5jKUtOE$=Qu1U&eFbV$7sZ! zB2H(FfZM*ze-#gkfO!s&(T7GPG?wZW+hB~FIawXe_z~X~@vA$x&3xyWmv_ST%71cvhe@Af^7A z5G0j(op`TT4rnKL;&m0#2@Cb z6-_e_ESG6r5oj41Zb?yztOS-UhMRfu*qOqwv=>_+72c^hoBCqyFb9o)Nd9=|0R5PA zDN)Dr>Gt$?trbqIdSmU5ZR-pBY&nAMv5hg}Kj+W?hLG*&ure7*c)@$fj+-Ew~Ber<4j=%8s5%7bD#RdySM5N z0M5l~7uOW$W9Qj1`pW!fmU6Kn-LDsM12MAYD}@hi9p-v|_r=dD%)0&Sn*foB8!%~u zJ(H^smfVs*7Vhn0DdpG@-;81Us00yPOtp}1p6QEvZY`}b=7sry*HzIbztcGnQ|&H3X+r%bbk0 zXB&-u01!W!>cDXZHdW4<>pYZC^L10$8$;r60LN|O1h9@M=XupP5eE9{0NDB50O6MW z6}deD_9tt9`m_CT)9;3;a8J3sx?|>_YwN^$+J`C*<@a`d)83nTRswg%$gnN-YwMvA zFinlQYj}HK&EM`Tw$~MIxtiYAIa?3Bw(0*dZ_*ssZG3j7zE}xOVJqWgCj?IWU#fuT zqBCG(j^X0ZvCcL1;gWNW5h4rhB+ZdNGX*GZ0kZbE2e} z{1qTLV%n0wvP~`*O zfo5&+{H%jk1SR5WYl^KC`)XVEpIr0JS-Z#5n-(^fT7O*2bp1wbeTwyH<5P1f!N}%8 z%y6$A7vtTXbI#rYVC2;}&4Ol0z#oe_Acc-8A%N!{c{vbq#N)t0jXV9B&(q%x@vEXy zek(F{Z4;?(3;ovIS}6b7>$gO}#_uP<%#0DohWwo%#A7xF&g%&7JS#xL+Gk@q;@<3Q zHpVSEBt!Cd;-wXTYTvPZZsNfH2Wyib-{w9DnC8McagJ6{B%!snP5pnH%5@`h=CKn# zX)h(hXTBi^A-|s2zZx}xL8W9UKtAneQQ@tLBmnSlQ;Osn05IR2NMM#gl`XacxY=J7 zyy`RKO#ZpJ7*V{j(dPIpmW|)+e0qKBXVY#20Ne4;*bFdQ5Nrs)8AB1=^yfS~A%MOx zP*gN+3c#z1f>dFsN~6 zOzj(#rp@$=?GuMb%GZf!H`WLP=KqXizGi;{nCJ0sZStMv@JX#H?0=HK=KPl+?RL*^ zO_4KOL*!+*x90fAH|+nfI{-?lw#G;qfffll-37pa6X7>PVe_t{AxcIjPQBv%3`AP; z#JF5;+JeDj2I`Htao1T)=*OGF1m|%CHGOEtV(oG7IP!2~o<2!XwdU&b)>0gbM3oR| z_NmI_4G23^`%lGiGiLin!HuWRF_ERd)83Iy6Vscf$RFovh#CEJHU9~BzZkbXA%1-A z1VFL|%j!D-O#;}&ikNV0RG{b}yPinn%;`uIkI_FCubPjwy8+auPa-TOx|n}XNa1`8 z2$-N@ie2?_0PhG8UpMMpop&Uda_zv#`J8-s8${nAAl1Jm300SgL|siJ;$`)!u064~ z29>G2tJo6Zu((}Y_Be4g`<~7Fws22^x7pwGHEYw1wG|#{%{r*pZVZL_4lj?{gUzi- zza{jvFPte+gMPAex90Hsi{zGxcqfXsZx)y!I3mD}hsQ~1&*M4f?;itF<^a1PLPajv zmwnje+Z_3)F_kZA9QSU>Z2$#oTkv=S_RZpN=5g=LoGO1K2AqdIJAWEK()E`7n;2Sq z3UIf!d1sY~V6IaVs+;-KyHV$B1L97IV0|ZFa12JYvrkLxN=)J$ipU$WJKIm}O`>-b zM;liXhbm?zuAJ|mlI+>sF0Le8lK*Nv+($}WyITI&R{>)uNB9Z!UD2I$_~jGdrrzx{ zVq>pyUIjc%0kQ>CnyHl`3lGyXw2Z5vL;2Mxn7jxX>FyWBw)Zb7YjP z0ii}HseGWhMOXPbcTrfrwncZI6Y;s6yEVTpXt!=@v!uFBb)mq)&2vi_sfLt)PeI~V zK+JzDn|&OfG~+kx@fI+)KdVxW|0y2Kui9UYfZo_GE;K|u zeg86;b~7Kob~#Og&iSF%rLnmW|J(tX7qRi|gy-1c%vt9gE=EI)%omJh&g;a>Y|rMJ zekrBsa^dDv*HYgklIC&pf6mhYPl^EnLam9)35$CpUeM5=2E1@=C4|^q*oK{Jd}hzi ze&QX1@?{diBp>W^j?+GD<5RxjIYx|4jEWqbKN}(7+NrPIWkl~D>Nizz{g8YZ3AsIU z6aS6f*2cWX1k5pqZOWVo`)MJ%snIhQJFe~kr~x)2U;|32nB2RH^I*lgC4W`m+hT8o zk2>eX%#6#Czw+6=MnGU7(TF-F4zNEXOEq4K0m;eDtNe17YV#+6P~}k7hkF;&>_xTT zvBoe~{9+l^|5gCZSf>vTEYtW+eX_anoD;c?;AP`ElEG{03)I;7>`Q=}*i>9BS88v5 zKh)2`Gi%m%wo|6?=4**-s!uB-(%Lw#VL5hjyLGGY0I>57`M2ZFV9v8jOhf)k1gQD& zn8l(LqyuJ^aFz>2?5r(oryeR!x8Ye^B>-9xpkm&TQbY3WI=hB`H2!Z)fngdC{dQ}w zjK#kZTZ#Pfx*{E%=M4dX^}<9BTl*yXZV1i!m~a5w;vSuOZ3Tc60W&}Br`OqkTOo!_ zTxGdZv9NsNJSyjE+*>i=&V3b_$c*6+Yvb0B`QOZ|!~*UQ$LYWC0H}?bh-*V)tZ$oa z3=SVh%`%W;J|-V-2#NJ|o1YcC0!o$x*GKicCI5Mz^*=dKKlAKFKx>ZBZ-m5b&(>uF z1c@vdSgN_6vkqzP-TqF#Ibrg@;PXwbs~FC)!d5n?$_>lQZ0qnA>+%0J Ws-nQCDFE0200001 z0dCwd3`;Iv*Zu#oYnN+1Y&Z=ZOG?V!wdgPdMzSn*Op1~%xn1X+%m3U?%YWX+2YqKX z_pYHV=f6t83fJiToZA%FDV~370@h}CZ8rO-BVfJsleu1EHbAO@xl}MN$MZhY6Z*Gm2Ji^@!Q49%c+wpG9r!`OGw>}EMgu$ty+L5P<8vlKxj{4~e+2)l|7a*F z#%gV&M}q(aj@C!eU+MZ#kkBEZ0$=SWdECPUtWE2GHJwRtZom^&*8d?0>{vc_jgwZJ zHUTH&`wae(u1o@}0DU+bE`Mx&ryfUu${FSn0#;%9O$0tB0Xw<{{+#k(6#(~y0Jnwa zt@AKMvSBmuG?JFBJ?!fN}x13OE&fGb%tpOaKPJUHCn=Y}Xj`Bao;G|H_VLi5dwNrFSm@CM0f{X!c(Va`Ea#&U?Km{lQu&TpWXRr!*Ci&6@DmPG_)iDw`;X(<=0Oj*+ zW!@O6K<5HqYZc&Yr3gociysIA6@cKKA6KBW`7*BGt&!`f$?-9kI^e()9sJG$+hBgTGrl9psVPl|X9K94l*`P{ zW{0Qt0a5#pD|S8_%gD%xkqVeii?fAzQmU5EIejd3iU6zyVrL@wyGk_`rX^EAW@QH! zt;gVbhnM97Q3{LBL}a97>PQt($-g?Q!->24KUzA=24kFOxIQO@I3h|n7%+jZ8LNCXm)xk0Ib1tt(2mB7pSAkdZ=OF0Z zK;;|i{*d*%C9#;4`#-gP=1r42vFWTHii9KSa%%mfH9;mJOPu0pIyja^lv@L4P1-HQOWKDxjrDn8wFx~v+-WGIUt27e0y z3w)55v9NID5z6xFbVtP$M+82<3V5{jj( Date: Sun, 29 Mar 2026 02:37:45 +0000 Subject: [PATCH 018/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f56657827b..84a758166f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4035,3 +4028,10 @@ 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 From b131c58501bd8d052e5214d83834191dca38ac82 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2026 00:09:08 -0400 Subject: [PATCH 019/247] Update Credits (#43376) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index fd60cbee29..221a6124d5 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, 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, 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, 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-A-Chair, 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 From e9e497103a22d7cd45111aa9c90b7264e77973ff Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sun, 29 Mar 2026 00:13:38 -0400 Subject: [PATCH 020/247] Update RT to 275.2.0 (#43378) --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index a12555988a..3136118b53 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit a12555988ae75a6c77b916df5c448b9a5d19f519 +Subproject commit 3136118b5338ef2d9580178caf5c723e65eb76e7 From 29d9c1786d62307ee55f40a4c035815c8435b222 Mon Sep 17 00:00:00 2001 From: Jessica M Date: Sun, 29 Mar 2026 08:02:51 -0700 Subject: [PATCH 021/247] Fix changeling transform ability (#42107) * fix changeling transform * Add store UI on MapInit instead * Apply suggestion from @slarticodefast * Apply suggestion from @slarticodefast --------- Co-authored-by: Jessica M Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Content.Server/Store/Systems/StoreSystem.cs | 6 ++++++ Resources/Prototypes/Entities/Mobs/Player/changeling.yml | 5 ----- Resources/Prototypes/GameRules/roundstart.yml | 5 ----- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index 7c5c99b5b4..3806842507 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -5,6 +5,7 @@ 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; @@ -23,6 +24,7 @@ public sealed partial class StoreSystem : EntitySystem [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; public override void Initialize() { @@ -47,6 +49,10 @@ public sealed partial class StoreSystem : EntitySystem { RefreshAllListings(component); component.StartingMap = Transform(uid).MapUid; + + // Add the bui key if it does not exist already (the check is needed to make sure that we don't overwrite existing InterfaceData). + if (!_uiSystem.HasUi(uid, StoreUiKey.Key)) + _uiSystem.SetUi(uid, StoreUiKey.Key, new InterfaceData("StoreBoundUserInterface")); } private void OnStartup(EntityUid uid, StoreComponent component, ComponentStartup args) diff --git a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml index 6ee9de8d01..424c942c66 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml @@ -13,8 +13,3 @@ - type: Store balance: ChangelingDNA: 50 - - type: UserInterface - interfaces: - enum.StoreUiKey.Key: - type: StoreBoundUserInterface - requireInputValidation: false diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index b03b5b03cc..76c2ade9d4 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 From ef2ea25cdf6472a2ded88948feb011c4de6b6107 Mon Sep 17 00:00:00 2001 From: NotActuallyMarty <27968892+NotActuallyMarty@users.noreply.github.com> Date: Sun, 29 Mar 2026 20:14:08 +0300 Subject: [PATCH 022/247] Kill unused terminator files (#43381) terminate the terminator --- .../Locale/en-US/administration/smites.ftl | 1 - .../Species/Terminator/parts.rsi/full.png | Bin 2187 -> 0 bytes .../Species/Terminator/parts.rsi/head_f.png | Bin 997 -> 0 bytes .../Species/Terminator/parts.rsi/head_m.png | Bin 997 -> 0 bytes .../Species/Terminator/parts.rsi/l_arm.png | Bin 807 -> 0 bytes .../Species/Terminator/parts.rsi/l_foot.png | Bin 746 -> 0 bytes .../Species/Terminator/parts.rsi/l_hand.png | Bin 748 -> 0 bytes .../Species/Terminator/parts.rsi/l_leg.png | Bin 737 -> 0 bytes .../Species/Terminator/parts.rsi/meta.json | 66 ------------------ .../Species/Terminator/parts.rsi/r_arm.png | Bin 839 -> 0 bytes .../Species/Terminator/parts.rsi/r_foot.png | Bin 782 -> 0 bytes .../Species/Terminator/parts.rsi/r_hand.png | Bin 759 -> 0 bytes .../Species/Terminator/parts.rsi/r_leg.png | Bin 848 -> 0 bytes .../Terminator/parts.rsi/skull_icon.png | Bin 1128 -> 0 bytes .../Species/Terminator/parts.rsi/torso_f.png | Bin 1803 -> 0 bytes .../Species/Terminator/parts.rsi/torso_m.png | Bin 1803 -> 0 bytes 16 files changed, 67 deletions(-) delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/full.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/head_f.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/head_m.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_arm.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_foot.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_hand.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/l_leg.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/meta.json delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_arm.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_foot.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_hand.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_leg.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/skull_icon.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/torso_f.png delete mode 100644 Resources/Textures/Mobs/Species/Terminator/parts.rsi/torso_m.png diff --git a/Resources/Locale/en-US/administration/smites.ftl b/Resources/Locale/en-US/administration/smites.ftl index 0702afb33c..2de4e79081 100644 --- a/Resources/Locale/en-US/administration/smites.ftl +++ b/Resources/Locale/en-US/administration/smites.ftl @@ -107,7 +107,6 @@ admin-smite-disarm-prone-description = Makes them get disarmed 100% of the time admin-smite-garbage-can-description = Turn them into a garbage bin to emphasize what they remind you of. admin-smite-super-bonk-description = Slams them on every single table on the Station and beyond. admin-smite-super-bonk-lite-description= Slams them on every single table on the Station and beyond. Stops when the target is dead. -admin-smite-terminate-description = Creates a Terminator ghost role with the sole objective of killing them. admin-smite-super-slip-description = Slips them really, really hard. admin-smite-omni-accent-description = Makes the target speak with almost every accent available. admin-smite-crawler-description = Makes the target fall down and be unable to stand up. Remove their hands too for added effect! diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/full.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/full.png deleted file mode 100644 index 44e3df3e91b378de6c18fc0617a87996c248a5d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2187 zcmV;62z2*}P)Px#1ZP1_K>z@;j|==^1pojDHAzH4RCr$PntyDS)fvaXloq)C0d1|U-Mt{CO2uj| z?LeTS5dR=z<}~{QaW2HnB_>PE{?P=cn_F}yTe2)>f4IekI2{EOlMNFP6D**$2&3(; zcI@^C1$sMbTcIue0kqiN@6-3Wd0T$H=iGZ=;>IVrIp;j@dEf7Ip7)&Ry!X8qH@V6G zH#3~lm^BYQG~rb7@xFc5*Rds8`c@}WeEpL@1p+}}vFJ5||)Pr|^2N%c25h|@jG}3@s8R_Wy_dn#=@nfH*BnUe7zaIY@ zV;4tpVeA63v*%)HXb`hAW+P)(0BPxI#!JCdM~au-zX`kFdd;x_OYbhnoSY(@KX)3( zz6d*3V8iAuX#S)osY`Ro(bE;hw?elekd=?(sz+o7uwD1gz^gVq|Co1 z2cx~sQfJ9F(c`Hk(om0+$Btm|%LqQ)_Og_0({96c1HkF8@a_gOXQB7YP8^SfWva7e zn|2;|x=uMS5!}<6cegN9hzp}<(AnC6JMJo#l5N`EiO=gYxNZQlzrFIZ>8u0QX94%X zb=mN|WU*}RQ*nLf7#*nUv#mLb{n+0to|B*kVP;x-+!;Oq5x1TFl6CjVK<_E}EU8>C z*PoZ4e?@(@v!9;h1y4(WcXRKbpO+`SHIM%U%hvr+j_Dm>n|2;&KlV>%5Wec|L`%b7 zsWZ9pLsZ{wV?Xxy$~6PPsl^?DgJPYJ!OD`dN-VyoN=k*t*^m8so=#O;c>eL`gBa-P zxa?Y2(kJZ4{yfi%zLA46#bScPgX7XZTDKcj)f=RqH8&kC&8huTG+n=|GmPMZa;e+u zd&&WBidSwz^8*pk&1pGH`x4Sgq4;gK+=|r@DUOVd-8PexY|d(HUnI z046&()7#%9j04bds3!J>=zjTn%r;B5Y4?Nn!#iZ%>sH8;ZQ5-x4nWrYGPH#2oYo|4 z)9wdc>sQkEB?stK9~mB!Qqi_wOW|tzHh2zzPWhg;f8y4nBC#oK(@wNsve79|uB5NH z`+31r2IWPoAO1c@&SxTX&WMzawQoxX<}p4As-0yr9)jG6M(p{%u(&+_B}cy^>oKCo z^_Up1gXaJ^)hVb0PIC&$4uDggf?xVKxyk=9*6U>Y+VgwOB}?DxB#LWR783l8-W2M) zeZ~Ax_PDf8z>smWowQBW5X;}pZ z2hPSEI4QQH^`EA6@QNlo-hiBW`3b#?G+qK6_)C8}AcN1EzZ6AxtVZQJf8GZwBO{}@ z^S;M0FIa)J?1flRTnhR`-<&RGc;m$<;vYKV7u0fV(--IMB^o-xO{7KwpT>GBnycm|)g!{CPYFa3_c~H#tWqN%dAZ zJ<+NUni%)*{FTv`Xfy+O;oaM|NS*7B!ei%0r9E+BT%0h?!Y1(A)SwojHhs|KN8e1J z2dt8yLT8Bku8;KfJGJEFzP4(r54KnbK$k(roIre99{u2LWESMg@l?QTQ-e^G+PVeW zV!U46Ymie<1EKht19aW%vLvSZ#xsBdnVA7pZ}>3+nb|15Yc<%WJ=KFJszXg`(+B#Z zJ~-VxlTZ#Id$zsc)cl9qUjGetHLL=_OdEeEaD(A{N!xb;B;gtGO~)z#+>iOMES-L~ z(N6~Sfxg&2e^}1|3ddiI-$2|ynA%_+fOGv_PVL5%h%X7}0DT{y%17YoP>Z^p`mK$7 z(AHGz95Kf_06DoqnR<-A{&UoiAn4$!OMUv02v*7KS_4gWLQJkU@L$|xn!Ybfs}BGG N002ovPDHLkV1n=*R1*LI 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 dada5727bf6261b51b3df9e29910a92972386fe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 997 zcmVAge`>x4_Hc93`YRXlW1{qLyw$ z|3R%mYe5hcK}1`7w)i)DR`LgyY-%9izWVp)|_XnTgMT-~3SoMyg6FV{8C5xBPD;<~QI z_Th2|hMx4O5v@vXvhB$vyq`mF1p4m4u~y6K@0_zo2ye+Rx`l&77?b<1^UE1-g^h(fA_t2_l9=>(SI$V{bNsm|BX9eUs(zG?s%#4E41OyhhGCV z%|Px%nr?g+LGlNN#(?TDaFw}Nen1m$!tD8EJcT#IQP=L}Nhlh;V~X%;VOo_RRNZ+g zIR@?Bw?{+T3m?m}4gS@;;=v1ZE$y0o)fA9B|JEEA-;V(CSxjQWO$Bc*vR`q1l|xl^ z4eCSf{uV(u1wKsA;Ze95-!dl{9ee3@3Osxq#gB_K>};-~v%41$>KpMrol@low4HeJ zG>X=aUL5YO;mdvsz8AscgQMH)mbS=Wc@q&45fKp)5fKp)5fS}UO0qvd`2I3es(khS zfM3!!QebVUfIBbb`~XbX`{CcG6Od9a@!xS9Vu%QMJkj{Lp_ z#N}lU6_r)kSW1`<9NV_7&l_cb3%KITri@THg4((UTL2;=A|fIpA|fIp`U5`!d=2XX T9$_Z%00000NkvXXu0mjf<5$Vb 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 dada5727bf6261b51b3df9e29910a92972386fe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 997 zcmVAge`>x4_Hc93`YRXlW1{qLyw$ z|3R%mYe5hcK}1`7w)i)DR`LgyY-%9izWVp)|_XnTgMT-~3SoMyg6FV{8C5xBPD;<~QI z_Th2|hMx4O5v@vXvhB$vyq`mF1p4m4u~y6K@0_zo2ye+Rx`l&77?b<1^UE1-g^h(fA_t2_l9=>(SI$V{bNsm|BX9eUs(zG?s%#4E41OyhhGCV z%|Px%nr?g+LGlNN#(?TDaFw}Nen1m$!tD8EJcT#IQP=L}Nhlh;V~X%;VOo_RRNZ+g zIR@?Bw?{+T3m?m}4gS@;;=v1ZE$y0o)fA9B|JEEA-;V(CSxjQWO$Bc*vR`q1l|xl^ z4eCSf{uV(u1wKsA;Ze95-!dl{9ee3@3Osxq#gB_K>};-~v%41$>KpMrol@low4HeJ zG>X=aUL5YO;mdvsz8AscgQMH)mbS=Wc@q&45fKp)5fKp)5fS}UO0qvd`2I3es(khS zfM3!!QebVUfIBbb`~XbX`{CcG6Od9a@!xS9Vu%QMJkj{Lp_ z#N}lU6_r)kSW1`<9NV_7&l_cb3%KITri@THg4((UTL2;=A|fIpA|fIp`U5`!d=2XX T9$_Z%00000NkvXXu0mjf<5$Vb 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 bb7425405cf99b761b0b095748bf740cb7fbd973..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 807 zcmV+?1K9kDP)Age`>x4_Hc93`YRXlW1{qLyw$ z|3R%mYe5hcK}1`7w)i)DR`LgyY-%9izWVp)|_XnTgMT-~3SoMyg6FV{8C5xBPD;<~QI z_Th2|hMx4O5v@vXvhB$vyq`mF1p4m4u~y6K@0_zo2ye+Rx`l&77?b<1^ncNU$6an6#hp7^hQ?_yWi&SZmw%b<2G~r_Y;sk)lYEVpWy@t l!+P^dUyJ|%0002|%p2p7YPS_z1BL(q002ovPDHLkV1l*XY*_#R 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 8e0b3f150767e0cb3c9920fab3982345dc405fac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 746 zcmVAge`>x4_Hc93`YRXlW1{qLyw$ z|3R%mYe5hcK}1`7w)i)DR`LgyY-%9izWVp)|_XnTgMT-~3SoMyg6FV{8C5xBPD;<~QI z_Th2|hMx4O5v@vXvhB$vyq`mF1p4m4u~y6K@0_zo2ye+Rx`l&77?b<1^_z3^#5%xa|X^m zXYSpdkpKVy000000002s9|$&0U>eP&P%No(RM+Fft&hdiyCCaA&%!WqYx}aRVed-X zlWs<%z{aId8}D>=9W~a@o7=RoLb)W zo(z5t!9Kfq8o4YAjK}>yzpB3B$uI8!0000000000007IS8ezL}b@jBRnrZv7w42Um zISyZC|HZicDIV&1Huk;P8rII0dOTh%SA6sDZ!iA;04v$XM_4rP_SO8mj-A|qW%<&a c!Y=pz0?f}?PP0Wa$^ZZW07*qoM6N<$g2F>t{{R30 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 cf93432a5e797cb3f22ffeb6ada16a28f8ddc6af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 748 zcmVAge`>x4_Hc93`YRXlW1{qLyw$ z|3R%mYe5hcK}1`7w)i)DR`LgyY-%9izWVp)|_XnTgMT-~3SoMyg6FV{8C5xBPD;<~QI z_Th2|hMx4O5v@vXvhB$vyq`mF1p4m4u~y6K@0_zo2ye+Rx`l&77?b<1^00HOdTu~d;>{}pdhHtMX4cB{FancZqn1ONa40002V;+PJ&>3Lyo??|t^?S@?SdQOyBrL7&kzPmh)6zeA` z(*afTZ^K(-@m_<09&2t*6+~iR0M+%SLfL2$#=(<3^tAC!?1? z>D(+)J-gNINO97pH@3K(x10E^`^x%8MbE11ZZ$>)uzCFToy~r4S#^J9wdkB5AB489 eGynhq{;Mf0j8IAge`>x4_Hc93`YRXlW1{qLyw$ z|3R%mYe5hcK}1`7w)i)DR`LgyY-%9izWVp)|_XnTgMT-~3SoMyg6FV{8C5xBPD;<~QI z_Th2|hMx4O5v@vXvhB$vyq`mF1p4m4u~y6K@0_zo2ye+Rx`l&77?b<1^vOA zLE#7niwFU$yn%rfpgCwR*8c^9Ljr62`7i>99E7<5o7KXbHufnO;K+Odht^d( Tds-Bv00000NkvXXu0mjf=juxp diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/meta.json b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/meta.json deleted file mode 100644 index 688877a32d..0000000000 --- a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/meta.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Created by ps3moira#9488 (discord) for SS14.", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "full" - }, - { - "name": "head_f", - "directions": 4 - }, - { - "name": "head_m", - "directions": 4 - }, - { - "name": "l_arm", - "directions": 4 - }, - { - "name": "l_foot", - "directions": 4 - }, - { - "name": "l_hand", - "directions": 4 - }, - { - "name": "l_leg", - "directions": 4 - }, - { - "name": "r_arm", - "directions": 4 - }, - { - "name": "r_foot", - "directions": 4 - }, - { - "name": "r_hand", - "directions": 4 - }, - { - "name": "r_leg", - "directions": 4 - }, - { - "name": "skull_icon", - "directions": 1 - }, - { - "name": "torso_f", - "directions": 4 - }, - { - "name": "torso_m", - "directions": 4 - } - ] -} diff --git a/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_arm.png b/Resources/Textures/Mobs/Species/Terminator/parts.rsi/r_arm.png deleted file mode 100644 index 51f05a477380a3bebd9e5d2834e82a5f49117441..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 839 zcmV-N1GxN&P)Age`>x4_Hc93`YRXlW1{qLyw$ z|3R%mYe5hcK}1`7w)i)DR`LgyY-%9izWVp)|_XnTgMT-~3SoMyg6FV{8C5xBPD;<~QI z_Th2|hMx4O5v@vXvhB$vyq`mF1p4m4u~y6K@0_zo2ye+Rx`l&77?b<1^0zaK7mu0V(cakc9JSoTa-2usgNNkA`%=5f+FG& zlR`5}QXF#I{~HoQa>KoU&Pj$40RR910Ki`+#c6YGW5@UNT!_a_FW2*KZ#uiWMYpAb z?*kAi_jq=BQ@2 z(hP?qOax%hSISjQH&wuUxp8PH)NwC&-c9+psEX2kZ2$lO00000000000013=^EyDB zn>Q1wJu;qD5+dWyc>B0kl&M<2W-5Symy7Mct&7QBXR*(^PyilSnoI|8u!zPXRiBq@D( z-YorK-ynUyJun$STP-lPv`%N)v>6+l;I#T3^$+l*Yxnr1000000000ZegW3*bYW$< Ri825H002ovPDHLkV1m>CbISk# 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 19ac240da3ad9bb75bd86dc85b5cad0ed453a20b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 782 zcmV+p1M&QcP)Age`>x4_Hc93`YRXlW1{qLyw$ z|3R%mYe5hcK}1`7w)i)DR`LgyY-%9izWVp)|_XnTgMT-~3SoMyg6FV{8C5xBPD;<~QI z_Th2|hMx4O5v@vXvhB$vyq`mF1p4m4u~y6K@0_zo2ye+Rx`l&77?b<1^2%jSz8axxU{^f{zFeMgJ=2OODl{ko6D;>U)Ig_ zxrt4*;KOED-cenemd;u&ZB?s*-X!;+A@8^ztqJ3?&t=D1WRh&SI~u-bl$jZ7wzML@ zeG<)O3wb$}-BG-|H_;1tyzj}W*5r4Zqn8sto4C8*h{jM5_Y&;5i7&jzwlLi0)000000001hv8*JUOuM|aTG688M(ya2?yP0{^=UBqo$Z?D7RtKyJ>6Zk z6X%~wT12i;j1E6|>PP{Qz9M`8$%U|Yx2Q}Sn@oadB2mk;8 M07*qoM6N<$f_3_BDgXcg 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 6cd2eb37ccbe12bef6a484660d4be6459db0a47d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 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 Date: Mon, 30 Mar 2026 00:01:49 +0100 Subject: [PATCH 023/247] Remove PDA equip sprites (#40498) * we have never had pda equip sprites. * I didn't know the rsi validator would complain about that one tbh --------- Co-authored-by: ruddygreat --- .../Objects/Devices/pda.rsi/equipped-BELT.png | Bin 1156 -> 0 bytes .../Objects/Devices/pda.rsi/equipped-IDCARD.png | Bin 1156 -> 0 bytes .../Textures/Objects/Devices/pda.rsi/meta.json | 8 -------- 3 files changed, 8 deletions(-) delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/equipped-BELT.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/equipped-IDCARD.png 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 6901e6c33b16b6a58f4a635b276edaa70a7f9845..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1156 zcmZ`(PiWIn7*BObEp*JAY@#8?n0g=RwSp*ETFoV)9BOcvyEE zJBWDj;MLQ>!SE_fL=biu=-H`w5p~1ME`m(oOS<$A?SuE;_rCmo-}k-WdoQbnxtVxu zG)7TWJU5#y;(3Df=m|Wo>kmHTDdHAqGStS-nICvD3`@CkK2Kf1b`*EgKHhqY$CLK# zf=f}cU!+G?*5Y?5>NwKpOQ@8;DXLbDQ#5N4a$e2$S&}DWvj&kudo{y!MK8_x2oc-F z4H(*oprtfZ$`|M<%Yk%~<2jy@V>C@mj#d_n+3PJjUZt5TLbeD%qtW0RmpIF*fJ7>l z0=xhOfyD^ceP|-ZV@-EF2yzrB3tiRGZKPW!P2wtxRvo1oh9nAGP^YGbdF;Al_*pa+ zKm*pGiCmE25}+Rs>E(8XrrVMO3j@d(Oym2~ayC*gW2L4&Tu*C%xKP#4>mm(w0D!>r zS6N6pf6mwA78;q19f`|o=9NmL|M^L%u1T^SPhnyx`7y3H>--N zv5sD;A_)Y@?g)XmJFo=$)F6?aiwi~QT1K5{FJDPWz8qc7_8|m>Qa?hcvzJh)TFAn8 z%F)%FiJ)UU_?n0g=RwSp*ETFoV)9BOcvyEE zJBWDj;MLQ>!SE_fL=biu=-H`w5p~1ME`m(oOS<$A?SuE;_rCmo-}k-WdoQbnxtVxu zG)7TWJU5#y;(3Df=m|Wo>kmHTDdHAqGStS-nICvD3`@CkK2Kf1b`*EgKHhqY$CLK# zf=f}cU!+G?*5Y?5>NwKpOQ@8;DXLbDQ#5N4a$e2$S&}DWvj&kudo{y!MK8_x2oc-F z4H(*oprtfZ$`|M<%Yk%~<2jy@V>C@mj#d_n+3PJjUZt5TLbeD%qtW0RmpIF*fJ7>l z0=xhOfyD^ceP|-ZV@-EF2yzrB3tiRGZKPW!P2wtxRvo1oh9nAGP^YGbdF;Al_*pa+ zKm*pGiCmE25}+Rs>E(8XrrVMO3j@d(Oym2~ayC*gW2L4&Tu*C%xKP#4>mm(w0D!>r zS6N6pf6mwA78;q19f`|o=9NmL|M^L%u1T^SPhnyx`7y3H>--N zv5sD;A_)Y@?g)XmJFo=$)F6?aiwi~QT1K5{FJDPWz8qc7_8|m>Qa?hcvzJh)TFAn8 z%F)%FiJ)UU_ Date: Sun, 29 Mar 2026 16:13:55 -0700 Subject: [PATCH 024/247] Lathe Menu Title Enhancement (#43392) * DefaultWindow -> FancyWindow, use SetInfoFromEntity * code smell fix --- Content.Client/Lathe/UI/LatheMenu.xaml | 5 +++-- Content.Client/Lathe/UI/LatheMenu.xaml.cs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml b/Content.Client/Lathe/UI/LatheMenu.xaml index a5c8f6a85c..84f1d7835a 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml +++ b/Content.Client/Lathe/UI/LatheMenu.xaml @@ -1,8 +1,9 @@ - @@ -156,4 +157,4 @@ - + diff --git a/Content.Client/Lathe/UI/LatheMenu.xaml.cs b/Content.Client/Lathe/UI/LatheMenu.xaml.cs index f6688a63af..adb115d352 100644 --- a/Content.Client/Lathe/UI/LatheMenu.xaml.cs +++ b/Content.Client/Lathe/UI/LatheMenu.xaml.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Text; using Content.Client.Materials; +using Content.Client.UserInterface.Controls; using Content.Shared.Lathe; using Content.Shared.Lathe.Prototypes; using Content.Shared.Research.Prototypes; @@ -8,7 +9,6 @@ using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -16,7 +16,7 @@ using Robust.Shared.Utility; namespace Content.Client.Lathe.UI; [GenerateTypedNameReferences] -public sealed partial class LatheMenu : DefaultWindow +public sealed partial class LatheMenu : FancyWindow { [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; @@ -75,6 +75,7 @@ public sealed partial class LatheMenu : DefaultWindow public void SetEntity(EntityUid uid) { Entity = uid; + this.SetInfoFromEntity(_entityManager, Entity); if (_entityManager.TryGetComponent(Entity, out var latheComponent)) { From f812ee23be300dfc2c80cc097eb709e4ac5ff51f Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 29 Mar 2026 23:27:57 +0000 Subject: [PATCH 025/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 84a758166f..dfa00ca08e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4035,3 +4027,10 @@ 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 From f44e9999b41dd2bdc68d99ce0a5a5a7568e7eefb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=81da?= Date: Sun, 29 Mar 2026 18:15:28 -0500 Subject: [PATCH 026/247] Reduce speso value of tech disks (#43395) commit Co-authored-by: iaada --- .../TechnologyDisk/Components/TechnologyDiskComponent.cs | 6 +++--- .../Structures/Machines/Computers/techdiskterminal.yml | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs b/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs index 02ffc77616..0afb845a9f 100644 --- a/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs +++ b/Content.Shared/Research/TechnologyDisk/Components/TechnologyDiskComponent.cs @@ -39,8 +39,8 @@ public sealed partial class TechnologyDiskComponent : Component [DataField] public Dictionary DiskPricePerTier = new() { - [1] = 100, - [2] = 500, - [3] = 1500 + [1] = 50, + [2] = 135, + [3] = 1000, }; } diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/techdiskterminal.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/techdiskterminal.yml index dd58b9709d..5b250969ce 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/techdiskterminal.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/techdiskterminal.yml @@ -31,6 +31,8 @@ energy: 0.5 color: "#b53ca1" +# The value per-disk equate to an average of 1000 research points -> 100 spesos +# Average is calculated from this table, default speso value in TechnologyDiskComponent, and default point cost in DiskConsoleComponent - type: weightedRandom id: TechDiskTierWeights weights: From 90fcb11d1d88a61d81d34128585563c8f19a409f Mon Sep 17 00:00:00 2001 From: Admiral-Obvious-001 <89495925+Admiral-Obvious-001@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:16:24 -0700 Subject: [PATCH 027/247] Enables Auto-Whitelisting (#43349) * Uncomments whitelist conditions. * Webedit ops for 30 day leadup for notes --- Resources/Prototypes/wizardsDenWhitelists.yml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Resources/Prototypes/wizardsDenWhitelists.yml b/Resources/Prototypes/wizardsDenWhitelists.yml index 8b8420622a..51cbd1bc2f 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: Deny - !type:ConditionAlwaysMatch action: Deny From 31a2c1176fad813d2bc0fb61ae6d32db0a16bbe4 Mon Sep 17 00:00:00 2001 From: crazybrain23 <44417085+crazybrain23@users.noreply.github.com> Date: Mon, 30 Mar 2026 00:38:09 +0100 Subject: [PATCH 028/247] Adjust naming rules to allow give any job with a custom name leniancy (#43217) Amend rules, first attempt --- .../Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml index 97880466d4..6816570d93 100644 --- a/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml +++ b/Resources/ServerInfo/Guidebook/ServerRules/CoreRules/RuleC13CharacterNames.xml @@ -6,7 +6,7 @@ - Usernames, objects, random characters, very "low effort" names, "meta" names, or otherwise implausible names cannot be used as names. See examples below. - Admin rulings on IC names are final and disputes should be done through the forums, not by refusing to comply with an admin - Clowns and mimes are exempt from the prohibition on titles/honorifics, and have loosened restrictions on low effort and implausible names. + Jobs that have a custom name option in the loadout (e.g. clowns, mimes, cyborgs, etc) are exempt from the prohibition on titles/honorifics, and have loosened restrictions on low effort and implausible names. ## Clarification on "Meta" Names Meta names are ones which attempt to take advantage of some game mechanic or game design choice. "Urist McHands" is a meta name because it is the default name used for admin spawned humans. "Operator Whiskey" is a meta name because it follows the naming pattern of nuclear operatives. This rule is not intended to prevent things like nuclear operatives using a fake ID with names that appear to be nuclear operative names if they decide that they want to do that. From 79b55a4f65f1cce7d978414059e8a8302dad0214 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 29 Mar 2026 23:42:54 +0000 Subject: [PATCH 029/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index dfa00ca08e..4c53d81f13 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4034,3 +4027,10 @@ 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 From e27f51762b5e0476c7d8f9041bb7db4bfaaa4070 Mon Sep 17 00:00:00 2001 From: InsoPL Date: Mon, 30 Mar 2026 04:04:14 +0200 Subject: [PATCH 030/247] Bugfix of Heat distortion shader (#43397) * revert of the revert * tests * changes * more fun * test * ccvvvar * works but bad * now its better * more fixes * more cleanup * cleaning * last fixes before move to glasses activ * x * glasses only * working * fix toolbox * cleanup * ThermalByte added * small fix * small optimalisations * float bux fix * comments add * more comments * more comments * last fix * revert cvar delete * wrong blue shades * cvar refactor * Update Content.Shared/Atmos/EntitySystems/SharedGasTileOverlaySystem.cs Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> * Update Content.Client/Atmos/Overlays/GasTileDangerousTemperatureOverlay.cs Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> * tweak to TryGetTemperature comment * Factors are now const * renames * Interface for ThermalByte * tile color vaccum and more comments * saving yeeted * integration test * rename and cleanup * fix * cleanup * switch * UT fix (hopefully) * small bug+ rename * vaccum limit + space is now invalid * typo * typo * fix * init, works * move method around * renames and split * renames * heatblur * cleanup * more docs * resource cache * No dynamic perlin noise generation [no fun allowed] and new blur softening method * magic numbers and rename * misc cleanup * removed init values because display compat mode broken * misc cleanup * bugfix * fix --------- Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> --- Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs b/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs index 2f540b6360..d849319875 100644 --- a/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs +++ b/Content.Client/Atmos/Overlays/GasTileHeatBlurOverlay.cs @@ -152,7 +152,9 @@ public sealed class GasTileHeatBlurOverlay : Overlay worldHandle.SetTransform(gridEntToViewportLocal); // We only care about tiles that fit in these bounds - var floatBounds = worldToViewportLocal.TransformBox(worldBounds).Enlarged(grid.Comp.TileSize); + 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), From 76e4d48273e445e8a6c27a1342b9c3c6b8176eca Mon Sep 17 00:00:00 2001 From: MissKay1994 <15877268+MissKay1994@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:18:47 -0400 Subject: [PATCH 031/247] Greatly reduce spawn rate of salt ore (#43012) * Disintegrate salt * Salt is forever * Forgot to exclude this file --- Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml | 2 +- Resources/Prototypes/Procedural/vgroid.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml b/Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml index 661350f6de..8d4632c0f3 100644 --- a/Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml +++ b/Resources/Prototypes/Procedural/Magnet/asteroid_ore_gens.yml @@ -4,7 +4,7 @@ OreIron: 1.0 OreQuartz: 1.0 OreCoal: 0.33 - OreSalt: 0.25 + OreSalt: 0.20 OreGold: 0.25 OreSilver: 0.25 OrePlasma: 0.20 diff --git a/Resources/Prototypes/Procedural/vgroid.yml b/Resources/Prototypes/Procedural/vgroid.yml index 0caa9f0e1f..7cc40dbb8f 100644 --- a/Resources/Prototypes/Procedural/vgroid.yml +++ b/Resources/Prototypes/Procedural/vgroid.yml @@ -50,7 +50,7 @@ - !type:OreDunGen replacement: IronRock entity: IronRockSalt - count: 50 + count: 25 minGroupSize: 8 maxGroupSize: 12 - !type:OreDunGen From 74c8fdde7221c54372836c51475ea545f99fef93 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 31 Mar 2026 00:32:33 +0000 Subject: [PATCH 032/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4c53d81f13..9748cd79d6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- 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 @@ -4034,3 +4023,10 @@ 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 From 03b46abef034c4186953b53e57e6fd3341ffb2f0 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Mon, 30 Mar 2026 23:28:24 -0400 Subject: [PATCH 033/247] Fix ghost time of death (#43411) * Use RealTime when setting ghost TimeOfDeath * Don't pause with RealTime * Oops. Also get rid of AutoGenerateComponentPause --- Content.Server/Ghost/GhostSystem.cs | 2 +- Content.Shared/Ghost/GhostComponent.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 9d3d05c607..964ba99ac3 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -205,7 +205,7 @@ namespace Content.Server.Ghost } _eye.RefreshVisibilityMask(uid); - var time = _gameTiming.CurTime; + var time = _gameTiming.RealTime; component.TimeOfDeath = time; } diff --git a/Content.Shared/Ghost/GhostComponent.cs b/Content.Shared/Ghost/GhostComponent.cs index 3fc9c081cb..cd5aa9be22 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] public TimeSpan TimeOfDeath = TimeSpan.Zero; /// From 4b9e7352d57374f138fb8cee1186d7a2c6023b6d Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 31 Mar 2026 03:42:21 +0000 Subject: [PATCH 034/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9748cd79d6..62b7a50860 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4030,3 +4022,10 @@ 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 From d42adbf05df1902c0ab3fb90995ee64737df383b Mon Sep 17 00:00:00 2001 From: Moony Date: Wed, 1 Apr 2026 09:06:26 -0700 Subject: [PATCH 035/247] Gametest Part 2: Preliminary refactor every test to use GameTest as the framework. (#43207) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Pass 1. * i'm FREE * Prevent hangups. * okay fine here's an attribute for settings, will polish later and prolly remove the overridable thing. * sigh. * fix singular trigger bug so LatheTest doesn't flake. * Remove SystemAttribute usage. * Poke * I used the shotgun. You know why? Cause the shot gun doesn’t miss, and unlike the shitty hybrid taser it stops a criminal in their tracks in two hits. Bang, bang, and they’re fucking done. I use four shots just to make damn sure. Because, once again, I’m not there to coddle a buncha criminal scum sucking f------, I’m there to 1) Survive the fucking round. 2) Guard the armory. So you can absolutely get fucked. If I get unbanned, which I won’t, you can guarantee I will continue to use the shotgun to apprehend criminals. Because it’s quick, clean and effective as fuck. Why in the seven hells would I fuck around with the disabler shots, which take half a clip just to bring someone down, or with the tazer bolts which are slow as balls, impossible to aim and do about next to jack shit, fuck all. The shotgun is the superior law enforcement weapon. Because it stops crime. And it stops crime by reducing the number of criminals roaming the fucking halls. * Change the faulty store test into two tests, one of which is ignored for failing. --- Content.Benchmarks/ComponentQueryBenchmark.cs | 3 +- Content.Benchmarks/DeltaPressureBenchmark.cs | 3 +- Content.Benchmarks/DestructibleBenchmark.cs | 3 +- .../DeviceNetworkingBenchmark.cs | 3 +- Content.Benchmarks/GasReactionBenchmark.cs | 3 +- Content.Benchmarks/HeatCapacityBenchmark.cs | 5 +- Content.Benchmarks/MapLoadBenchmark.cs | 3 +- Content.Benchmarks/PvsBenchmark.cs | 3 +- Content.Benchmarks/RaiseEventBenchmark.cs | 3 +- .../SpawnEquipDeleteBenchmark.cs | 5 +- .../Tests/Access/AccessReaderTest.cs | 159 +++++++-------- .../Tests/Actions/ActionPvsDetachTest.cs | 17 +- .../Tests/Actions/ActionsAddedTest.cs | 10 +- .../Tests/Administration/Logs/AddTests.cs | 188 +++++++++--------- .../Tests/Administration/Logs/FilterTests.cs | 13 +- .../Tests/Administration/Logs/QueryTests.cs | 14 +- .../Tests/Atmos/AlarmThresholdTest.cs | 6 +- .../Tests/Atmos/ConstantsTest.cs | 6 +- .../Tests/Atmos/GasArrayTest.cs | 6 +- .../Tests/Atmos/GasMixtureTest.cs | 13 +- .../Tests/Atmos/GridJoinTest.cs | 7 +- .../Tests/Atmos/SharedGasSpecificHeatsTest.cs | 2 +- .../Tests/Body/GibbingTest.cs | 7 +- .../Tests/Body/HandOrganTest.cs | 7 +- .../Tests/Buckle/BuckleTest.Interact.cs | 8 +- .../Tests/Buckle/BuckleTest.cs | 14 +- Content.IntegrationTests/Tests/CargoTest.cs | 27 +-- .../Tests/Chemistry/ReagentDataTest.cs | 7 +- .../Tests/Chemistry/SolutionRoundingTest.cs | 7 +- .../Tests/Chemistry/SolutionSystemTests.cs | 23 +-- .../Tests/Chemistry/TryAllReactionsTest.cs | 7 +- .../Tests/Cleanup/EuiManagerTest.cs | 42 ++-- .../Tests/ClickableTest.cs | 9 +- .../Cloning/CloningSettingsPrototypeTest.cs | 7 +- .../Tests/Commands/ForceMapTest.cs | 7 +- .../Tests/Commands/ObjectiveCommandsTest.cs | 12 +- .../Tests/Commands/PardonCommand.cs | 7 +- .../Tests/Commands/RejuvenateTest.cs | 8 +- .../Tests/Commands/RestartRoundTest.cs | 20 +- .../Tests/Commands/SuicideCommandTests.cs | 55 ++--- .../Tests/ConfigPresetTests.cs | 7 +- .../Construction/ConstructionActionValid.cs | 9 +- .../Construction/ConstructionPrototypeTest.cs | 10 +- .../Tests/ContainerOcclusionTest.cs | 15 +- .../Tests/ContrabandTest.cs | 7 +- .../Tests/Damageable/DamageableTest.cs | 6 +- .../Tests/Damageable/MobThresholdsTest.cs | 7 +- .../Tests/Damageable/StaminaComponentTest.cs | 7 +- .../Tests/DeleteInventoryTest.cs | 6 +- .../DestructibleDamageGroupTest.cs | 6 +- .../DestructibleDamageTypeTest.cs | 6 +- .../DestructibleDestructionTest.cs | 6 +- .../DestructibleThresholdActivationTest.cs | 6 +- .../Tests/DeviceLinking/DeviceLinkingTest.cs | 7 +- .../Tests/DeviceNetwork/DeviceNetworkTest.cs | 14 +- .../Tests/Disposal/DisposalUnitTest.cs | 7 +- .../Tests/DoAfter/DoAfterServerTest.cs | 19 +- .../Tests/Doors/AirlockTest.cs | 12 +- .../Tests/DummyIconTest.cs | 6 +- Content.IntegrationTests/Tests/EntityTest.cs | 48 +++-- .../Tests/Explosion/ExplosionPrototypeTest.cs | 7 +- .../Tests/FillLevelSpriteTest.cs | 7 +- .../Tests/Fluids/AbsorbentTest.cs | 13 +- .../Tests/Fluids/FluidSpillTest.cs | 7 +- .../Tests/Fluids/PuddleTest.cs | 12 +- .../Tests/FollowerSystemTest.cs | 6 +- .../Components/ActionBlocking/HandCuffTest.cs | 7 +- .../EntityPrototypeComponentsTest.cs | 11 +- .../Components/Mobs/AlertsComponentTests.cs | 17 +- .../Tests/GameRules/AntagPreferenceTest.cs | 18 +- .../Tests/GameRules/FailAndStartPresetTest.cs | 20 +- .../Tests/GameRules/NukeOpsTest.cs | 21 +- .../Tests/GameRules/RuleMaxTimeRestartTest.cs | 9 +- .../Tests/GameRules/SecretStartsTest.cs | 9 +- .../Tests/GameRules/StartEndGameRulesTest.cs | 17 +- .../Tests/GameRules/TraitorRuleTest.cs | 22 +- .../Tests/Gibbing/GibTest.cs | 7 +- .../Tests/Gravity/WeightlessStatusTests.cs | 7 +- .../Tests/GravityGridTest.cs | 7 +- .../Tests/Guidebook/DocumentParsingTest.cs | 7 +- .../Guidebook/GuideEntryPrototypeTests.cs | 7 +- .../Tests/Hands/HandTests.cs | 23 +-- .../Tests/HumanInventoryUniformSlotsTest.cs | 7 +- .../Humanoid/HideablePrototypeValidation.cs | 12 +- .../Tests/Humanoid/HumanoidProfileTests.cs | 7 +- .../Click/InteractionSystemTests.cs | 18 +- .../Tests/Interaction/InRangeUnobstructed.cs | 7 +- .../Tests/Interaction/InteractionTest.cs | 2 +- .../Tests/Internals/AutoInternalsTests.cs | 11 +- .../Tests/InventoryHelpersTest.cs | 7 +- .../Tests/Lathe/LatheTest.cs | 11 +- .../Tests/Linter/StaticFieldValidationTest.cs | 7 +- .../Tests/Lobby/CharacterCreationTest.cs | 8 +- .../Tests/Lobby/ServerReloginTest.cs | 17 +- .../EntityPrototypeLocalizationTest.cs | 7 +- .../LocalizedDatasetPrototypeTest.cs | 7 +- .../Tests/MachineBoardTest.cs | 15 +- .../Tests/MagazineVisualsSpriteTest.cs | 7 +- .../Tests/Mapping/MappingTests.cs | 9 +- .../Tests/MappingEditorTest.cs | 10 +- .../Tests/Markings/MarkingManagerTests.cs | 23 +-- .../Tests/Markings/MarkingsViewModelTests.cs | 2 +- .../Tests/MaterialArbitrageTest.cs | 6 +- .../Tests/Materials/MaterialTests.cs | 7 +- .../Tests/Minds/GhostRoleTests.cs | 20 +- .../Tests/Minds/GhostTests.cs | 28 ++- .../Minds/MindTest.DeleteAllThenGhost.cs | 10 +- .../Tests/Minds/MindTests.EntityDeletion.cs | 24 +-- .../Tests/Minds/MindTests.Helpers.cs | 18 +- .../Tests/Minds/MindTests.ReconnectTests.cs | 20 +- .../Tests/Minds/MindTests.cs | 44 +--- .../Tests/Minds/RoleTests.cs | 14 +- Content.IntegrationTests/Tests/NPC/NPCTest.cs | 7 +- .../Tests/Networking/NetworkIdsMatchTest.cs | 6 +- .../Tests/Networking/PvsCommandTest.cs | 9 +- .../Tests/Networking/ReconnectTest.cs | 6 +- .../Networking/SimplePredictReconcileTest.cs | 6 +- .../Tests/Physics/AnchorPrototypeTest.cs | 7 +- .../Tests/PostMapInitTest.cs | 68 ++----- .../Tests/Power/PowerStatePrototypeTest.cs | 7 +- .../Tests/Power/PowerStateTest.cs | 15 +- .../Tests/Power/PowerTest.cs | 63 ++---- .../Tests/Power/StationPowerTests.cs | 22 +- .../Tests/Preferences/LoadoutTests.cs | 15 +- .../Tests/Preferences/ServerDbSqliteTests.cs | 20 +- .../Tests/Procedural/DungeonTests.cs | 11 +- .../Tests/PrototypeSaveTest.cs | 6 +- .../Tests/PrototypeTests/PrototypeTests.cs | 15 +- .../PrototypeTests/PrototypeUploadTest.cs | 7 +- .../Tests/Puller/PullerTest.cs | 7 +- .../Tests/Replays/ReplayTests.cs | 18 +- .../Tests/ResearchTest.cs | 11 +- .../Tests/ResettingEntitySystemTests.cs | 20 +- .../Tests/Respirator/LungTest.cs | 11 +- .../Tests/RestartRoundTest.cs | 20 +- .../Tests/Roles/StartingGearStorageTests.cs | 10 +- .../Tests/Round/JobTest.cs | 42 ++-- .../Tests/RoundEndTest.cs | 19 +- Content.IntegrationTests/Tests/SalvageTest.cs | 14 +- .../Tests/SaveLoadMapTest.cs | 11 +- .../Tests/SaveLoadSaveTest.cs | 14 +- .../Tests/Serialization/SerializationTest.cs | 7 +- .../Tests/Shuttle/DockTest.cs | 11 +- Content.IntegrationTests/Tests/ShuttleTest.cs | 6 +- .../Tests/Sprite/ItemSpriteTest.cs | 8 +- Content.IntegrationTests/Tests/StartTest.cs | 7 +- .../Tests/Station/EvacShuttleTest.cs | 12 +- .../Tests/Station/JobTests.cs | 6 +- .../Tests/Station/StationJobsTest.cs | 12 +- .../Tests/Storage/EntityStorageTests.cs | 7 +- .../Tests/Storage/StorageTest.cs | 19 +- Content.IntegrationTests/Tests/StoreTests.cs | 25 ++- Content.IntegrationTests/Tests/Tag/TagTest.cs | 6 +- .../Tests/Tiles/TileStackRecursionTest.cs | 6 +- .../Tests/UserInterface/UiControlTest.cs | 10 +- .../Utility/EntitySystemExtensionsTest.cs | 6 +- .../Tests/Utility/EntityWhitelistTest.cs | 6 +- .../Tests/VendingMachineRestockTest.cs | 21 +- .../Tests/Wires/WireLayoutTest.cs | 9 +- .../WizdenContentFreeze.cs | 7 +- .../Tests/XenoArtifactTest.cs | 33 +-- Content.MapRenderer/Program.cs | 2 +- .../Trigger/Systems/TriggerSystem.cs | 3 + 163 files changed, 1005 insertions(+), 1317 deletions(-) diff --git a/Content.Benchmarks/ComponentQueryBenchmark.cs b/Content.Benchmarks/ComponentQueryBenchmark.cs index bfe367790a..c9aebf7ba3 100644 --- a/Content.Benchmarks/ComponentQueryBenchmark.cs +++ b/Content.Benchmarks/ComponentQueryBenchmark.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -44,7 +45,7 @@ public class ComponentQueryBenchmark ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(typeof(QueryBenchSystem).Assembly); - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); _entMan = _pair.Server.ResolveDependency(); _itemQuery = _entMan.GetEntityQuery(); diff --git a/Content.Benchmarks/DeltaPressureBenchmark.cs b/Content.Benchmarks/DeltaPressureBenchmark.cs index 8d4929c47f..dac79e0376 100644 --- a/Content.Benchmarks/DeltaPressureBenchmark.cs +++ b/Content.Benchmarks/DeltaPressureBenchmark.cs @@ -1,3 +1,4 @@ +using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; @@ -68,7 +69,7 @@ public class DeltaPressureBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; var mapdata = await _pair.CreateTestMap(); diff --git a/Content.Benchmarks/DestructibleBenchmark.cs b/Content.Benchmarks/DestructibleBenchmark.cs index aa759c35fc..58c834c0ae 100644 --- a/Content.Benchmarks/DestructibleBenchmark.cs +++ b/Content.Benchmarks/DestructibleBenchmark.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; @@ -69,7 +70,7 @@ public class DestructibleBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; _entMan = server.ResolveDependency(); diff --git a/Content.Benchmarks/DeviceNetworkingBenchmark.cs b/Content.Benchmarks/DeviceNetworkingBenchmark.cs index bb2a22312e..fcde4feb64 100644 --- a/Content.Benchmarks/DeviceNetworkingBenchmark.cs +++ b/Content.Benchmarks/DeviceNetworkingBenchmark.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; @@ -60,7 +61,7 @@ public class DeviceNetworkingBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(typeof(DeviceNetworkingBenchmark).Assembly); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; await server.WaitPost(() => diff --git a/Content.Benchmarks/GasReactionBenchmark.cs b/Content.Benchmarks/GasReactionBenchmark.cs index 9ed30373d1..f298869366 100644 --- a/Content.Benchmarks/GasReactionBenchmark.cs +++ b/Content.Benchmarks/GasReactionBenchmark.cs @@ -1,3 +1,4 @@ +using System.IO; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; @@ -51,7 +52,7 @@ public class GasReactionBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; // Create test map and grid diff --git a/Content.Benchmarks/HeatCapacityBenchmark.cs b/Content.Benchmarks/HeatCapacityBenchmark.cs index cef5bc10c7..18366306d7 100644 --- a/Content.Benchmarks/HeatCapacityBenchmark.cs +++ b/Content.Benchmarks/HeatCapacityBenchmark.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.IO; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; using Content.IntegrationTests.Pair; @@ -27,7 +28,7 @@ public class HeatCapacityBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); await _pair.Connect(); _cEntMan = _pair.Client.ResolveDependency(); _sEntMan = _pair.Server.ResolveDependency(); diff --git a/Content.Benchmarks/MapLoadBenchmark.cs b/Content.Benchmarks/MapLoadBenchmark.cs index 261d408aac..9ea95c84b6 100644 --- a/Content.Benchmarks/MapLoadBenchmark.cs +++ b/Content.Benchmarks/MapLoadBenchmark.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -29,7 +30,7 @@ public class MapLoadBenchmark ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); var server = _pair.Server; Paths = server.ResolveDependency() diff --git a/Content.Benchmarks/PvsBenchmark.cs b/Content.Benchmarks/PvsBenchmark.cs index 51a013539e..af1ec8d9ef 100644 --- a/Content.Benchmarks/PvsBenchmark.cs +++ b/Content.Benchmarks/PvsBenchmark.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.IO; using System.Linq; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -50,7 +51,7 @@ public class PvsBenchmark #endif PoolManager.Startup(); - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); _entMan = _pair.Server.ResolveDependency(); _pair.Server.CfgMan.SetCVar(CVars.NetPVS, true); _pair.Server.CfgMan.SetCVar(CVars.ThreadParallelCount, 0); diff --git a/Content.Benchmarks/RaiseEventBenchmark.cs b/Content.Benchmarks/RaiseEventBenchmark.cs index e3d377ccb3..99e032d0b5 100644 --- a/Content.Benchmarks/RaiseEventBenchmark.cs +++ b/Content.Benchmarks/RaiseEventBenchmark.cs @@ -1,4 +1,5 @@ #nullable enable +using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; @@ -21,7 +22,7 @@ public class RaiseEventBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(typeof(BenchSystem).Assembly); - _pair = PoolManager.GetServerClient().GetAwaiter().GetResult(); + _pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); var entMan = _pair.Server.EntMan; var fact = _pair.Server.ResolveDependency(); var bus = (EntityEventBus)entMan.EventBus; diff --git a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs index 9b878eac40..b6d5427480 100644 --- a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs +++ b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.IO; +using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Content.IntegrationTests; using Content.IntegrationTests.Pair; @@ -36,7 +37,7 @@ public class SpawnEquipDeleteBenchmark { ProgramShared.PathOffset = "../../../../"; PoolManager.Startup(); - _pair = await PoolManager.GetServerClient(); + _pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); var server = _pair.Server; var mapData = await _pair.CreateTestMap(); diff --git a/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs b/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs index a0c8c775b1..0279673910 100644 --- a/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs +++ b/Content.IntegrationTests/Tests/Access/AccessReaderTest.cs @@ -1,16 +1,17 @@ +#nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Content.Shared.Access; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Robust.Shared.GameObjects; -using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Access { - [TestFixture] [TestOf(typeof(AccessReaderComponent))] - public sealed class AccessReaderTest + public sealed class AccessReaderTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -21,91 +22,85 @@ namespace Content.IntegrationTests.Tests.Access - type: AccessReader "; + [SidedDependency(Side.Server)] private readonly AccessReaderSystem _system = null!; + [Test] + [RunOnSide(Side.Server)] public async Task TestTags() { - await using var pair = await PoolManager.GetServerClient(); - var server = pair.Server; - var entityManager = server.ResolveDependency(); + var ent = SSpawn("TestAccessReader"); + var reader = new Entity(ent, SComp(ent)); - await server.WaitAssertion(() => + // test empty + Assert.Multiple(() => { - var system = entityManager.System(); - var ent = entityManager.SpawnEntity("TestAccessReader", MapCoordinates.Nullspace); - var reader = new Entity(ent, entityManager.GetComponent(ent)); - - // test empty - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "Bar" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True); - }); - - // test deny - system.AddDenyTag(reader, "A"); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "Foo" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True); - }); - system.ClearDenyTags(reader); - - // test one list - system.TryAddAccess(reader, "A"); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); - }); - system.TryClearAccesses(reader); - - // test one list - two items - system.TryAddAccess(reader, new HashSet> { "A", "B" }); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); - }); - system.TryClearAccesses(reader); - - // test two list - var accesses = new List>>() { - new HashSet> () { "A" }, - new HashSet> () { "B", "C" } - }; - system.TryAddAccesses(reader, accesses); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "C", "B" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "C", "B", "A" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); - }); - system.TryClearAccesses(reader); - - // test deny list - system.TryAddAccess(reader, new HashSet> { "A" }); - system.AddDenyTag(reader, "B"); - Assert.Multiple(() => - { - Assert.That(system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); - Assert.That(system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.False); - Assert.That(system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); - }); - system.TryClearAccesses(reader); - system.ClearDenyTags(reader); + Assert.That(_system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "Bar" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True); }); - await pair.CleanReturnAsync(); + + // test deny + _system.AddDenyTag(reader, "A"); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "Foo" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "Foo" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.True); + }); + _system.ClearDenyTags(reader); + + // test one list + _system.TryAddAccess(reader, "A"); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); + }); + _system.TryClearAccesses(reader); + + // test one list - two items + _system.TryAddAccess(reader, new HashSet> { "A", "B" }); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); + }); + _system.TryClearAccesses(reader); + + // test two list + var accesses = new List>>() { + new HashSet> () { "A" }, + new HashSet> () { "B", "C" } + }; + _system.TryAddAccesses(reader, accesses); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "C", "B" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "C", "B", "A" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); + }); + _system.TryClearAccesses(reader); + + // test deny list + _system.TryAddAccess(reader, new HashSet> { "A" }); + _system.AddDenyTag(reader, "B"); + Assert.Multiple(() => + { + Assert.That(_system.AreAccessTagsAllowed(new List> { "A" }, reader), Is.True); + Assert.That(_system.AreAccessTagsAllowed(new List> { "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(new List> { "A", "B" }, reader), Is.False); + Assert.That(_system.AreAccessTagsAllowed(Array.Empty>(), reader), Is.False); + }); + _system.TryClearAccesses(reader); + _system.ClearDenyTags(reader); } } diff --git a/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs b/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs index 45addff00b..9680e48939 100644 --- a/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs +++ b/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs @@ -1,4 +1,7 @@ +#nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Content.Shared.Actions; using Content.Shared.Eye; using Robust.Server.GameObjects; @@ -7,15 +10,18 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Actions; [TestFixture] -public sealed class ActionPvsDetachTest +public sealed class ActionPvsDetachTest : GameTest { + [SidedDependency(Side.Server)] private readonly SharedActionsSystem _sActionsSys = null!; + [SidedDependency(Side.Client)] private readonly SharedActionsSystem _cActionsSys = null!; + [Test] public async Task TestActionDetach() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); - var (server, client) = pair; - var sys = server.System(); - var cSys = client.System(); + var pair = Pair; + var (server, client) = (Server, Client); + var sys = _sActionsSys; + var cSys = _cActionsSys; // Spawn mob that has some actions EntityUid ent = default; @@ -60,6 +66,5 @@ public sealed class ActionPvsDetachTest Assert.That(cSys.GetActions(cEnt).Count(), Is.EqualTo(initActions)); await server.WaitPost(() => server.EntMan.DeleteEntity(map.MapUid)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs index 0dbec0c83a..1fa005496e 100644 --- a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs +++ b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs @@ -1,4 +1,6 @@ +#nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Actions; using Content.Shared.Actions.Components; using Content.Shared.CombatMode; @@ -11,15 +13,17 @@ namespace Content.IntegrationTests.Tests.Actions; /// This tests checks that actions properly get added to an entity's actions component.. /// [TestFixture] -public sealed class ActionsAddedTest +public sealed class ActionsAddedTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings { Connected = true, DummyTicker = false }; + // TODO add magboot test (inventory action) // TODO add ghost toggle-fov test (client-side action) [Test] public async Task TestCombatActionsAdded() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false }); + var pair = Pair; var server = pair.Server; var client = pair.Client; var sEntMan = server.ResolveDependency(); @@ -67,7 +71,5 @@ public sealed class ActionsAddedTest // required, because integration tests do not respect the [NonSerialized] attribute and will simply events by reference. Assert.That(ReferenceEquals(sAct.Comp, cAct.Comp), Is.False); Assert.That(ReferenceEquals(sQuery.GetComponent(sAct).Event, cQuery.GetComponent(cAct).Event), Is.False); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs index 772af337a1..71cb0fd7ed 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/AddTests.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +#nullable enable +using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Administration.Logs; using Content.Server.Database; using Content.Server.GameTicking; @@ -12,9 +14,9 @@ namespace Content.IntegrationTests.Tests.Administration.Logs; [TestFixture] [TestOf(typeof(AdminLogSystem))] -public sealed class AddTests +public sealed class AddTests : GameTest { - public static PoolSettings LogTestSettings = new() + public override PoolSettings PoolSettings => new() { AdminLogsEnabled = true, DummyTicker = false, @@ -24,7 +26,7 @@ public sealed class AddTests [Test] public async Task AddAndGetSingleLog() { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -33,7 +35,7 @@ public sealed class AddTests var guid = Guid.NewGuid(); await pair.CreateTestMap(); - var coordinates = pair.TestMap.GridCoords; + var coordinates = pair.TestMap!.GridCoords; await server.WaitPost(() => { var entity = sEntities.SpawnEntity(null, coordinates); @@ -62,14 +64,12 @@ public sealed class AddTests return false; }); - - await pair.CleanReturnAsync(); } [Test] public async Task AddAndGetUnformattedLog() { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sDatabase = server.ResolveDependency(); @@ -127,15 +127,13 @@ public sealed class AddTests json.Dispose(); } - - await pair.CleanReturnAsync(); } [Test] [TestCase(500)] public async Task BulkAddLogs(int amount) { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -158,14 +156,12 @@ public sealed class AddTests var messages = await sAdminLogSystem.CurrentRoundLogs(); return messages.Count >= amount; }); - - await pair.CleanReturnAsync(); } [Test] public async Task AddPlayerSessionLog() { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); + var pair = Pair; var server = pair.Server; var sPlayers = server.ResolveDependency(); @@ -195,20 +191,91 @@ public sealed class AddTests Assert.That(logs.First().Players, Does.Contain(playerGuid)); return true; }); - await pair.CleanReturnAsync(); } + [Test] + public async Task DuplicatePlayerDoesNotThrowTest() + { + var pair = Pair; + var server = pair.Server; + + var sPlayers = server.ResolveDependency(); + var sAdminLogSystem = server.ResolveDependency(); + + var guid = Guid.NewGuid(); + + await server.WaitPost(() => + { + var player = sPlayers.Sessions.Single(); + + sAdminLogSystem.Add(LogType.Unknown, $"{player} {player} test log: {guid}"); + }); + + await PoolManager.WaitUntil(server, async () => + { + var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter + { + Search = guid.ToString() + }); + + if (logs.Count == 0) + { + return false; + } + + return true; + }); + } + + [Test] + public async Task DuplicatePlayerIdDoesNotThrowTest() + { + var pair = Pair; + var server = pair.Server; + + var sPlayers = server.ResolveDependency(); + + var sAdminLogSystem = server.ResolveDependency(); + + var guid = Guid.NewGuid(); + + await server.WaitPost(() => + { + var player = sPlayers.Sessions.Single(); + + sAdminLogSystem.Add(LogType.Unknown, $"{player:first} {player:second} test log: {guid}"); + }); + + await PoolManager.WaitUntil(server, async () => + { + var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter + { + Search = guid.ToString() + }); + + if (logs.Count == 0) + { + return false; + } + + return true; + }); + } +} + +public sealed class PreRoundAddTests : GameTest +{ + public override PoolSettings PoolSettings => new PoolSettings + { + Dirty = true, + InLobby = true, + AdminLogsEnabled = true + }; + [Test] public async Task PreRoundAddAndGetSingle() { - var setting = new PoolSettings - { - Dirty = true, - InLobby = true, - AdminLogsEnabled = true - }; - - await using var pair = await PoolManager.GetServerClient(setting); + var pair = Pair; var server = pair.Server; var sDatabase = server.ResolveDependency(); @@ -262,81 +329,6 @@ public sealed class AddTests json.Dispose(); } - await pair.CleanReturnAsync(); } - [Test] - public async Task DuplicatePlayerDoesNotThrowTest() - { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); - var server = pair.Server; - - var sPlayers = server.ResolveDependency(); - var sAdminLogSystem = server.ResolveDependency(); - - var guid = Guid.NewGuid(); - - await server.WaitPost(() => - { - var player = sPlayers.Sessions.Single(); - - sAdminLogSystem.Add(LogType.Unknown, $"{player} {player} test log: {guid}"); - }); - - await PoolManager.WaitUntil(server, async () => - { - var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter - { - Search = guid.ToString() - }); - - if (logs.Count == 0) - { - return false; - } - - return true; - }); - - await pair.CleanReturnAsync(); - Assert.Pass(); - } - - [Test] - public async Task DuplicatePlayerIdDoesNotThrowTest() - { - await using var pair = await PoolManager.GetServerClient(LogTestSettings); - var server = pair.Server; - - var sPlayers = server.ResolveDependency(); - - var sAdminLogSystem = server.ResolveDependency(); - - var guid = Guid.NewGuid(); - - await server.WaitPost(() => - { - var player = sPlayers.Sessions.Single(); - - sAdminLogSystem.Add(LogType.Unknown, $"{player:first} {player:second} test log: {guid}"); - }); - - await PoolManager.WaitUntil(server, async () => - { - var logs = await sAdminLogSystem.CurrentRoundLogs(new LogFilter - { - Search = guid.ToString() - }); - - if (logs.Count == 0) - { - return false; - } - - return true; - }); - - await pair.CleanReturnAsync(); - Assert.Pass(); - } } diff --git a/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs index 6f907f425e..2517defe96 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/FilterTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Administration.Logs; using Content.Shared.Administration.Logs; using Content.Shared.Database; @@ -7,14 +8,21 @@ namespace Content.IntegrationTests.Tests.Administration.Logs; [TestFixture] [TestOf(typeof(AdminLogSystem))] -public sealed class FilterTests +public sealed class FilterTests : GameTest { + public override PoolSettings PoolSettings => new() + { + AdminLogsEnabled = true, + DummyTicker = false, + Connected = true + }; + [Test] [TestCase(DateOrder.Ascending)] [TestCase(DateOrder.Descending)] public async Task Date(DateOrder order) { - await using var pair = await PoolManager.GetServerClient(AddTests.LogTestSettings); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -96,6 +104,5 @@ public sealed class FilterTests return firstFound && secondFound; }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Administration/Logs/QueryTests.cs b/Content.IntegrationTests/Tests/Administration/Logs/QueryTests.cs index 5a58757d53..55b36ebae1 100644 --- a/Content.IntegrationTests/Tests/Administration/Logs/QueryTests.cs +++ b/Content.IntegrationTests/Tests/Administration/Logs/QueryTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Administration.Logs; using Content.Server.GameTicking; using Content.Shared.Database; @@ -11,12 +12,19 @@ namespace Content.IntegrationTests.Tests.Administration.Logs; [TestFixture] [TestOf(typeof(AdminLogSystem))] -public sealed class QueryTests +public sealed class QueryTests : GameTest { + public override PoolSettings PoolSettings => new() + { + AdminLogsEnabled = true, + DummyTicker = false, + Connected = true + }; + [Test] public async Task QuerySingleLog() { - await using var pair = await PoolManager.GetServerClient(AddTests.LogTestSettings); + var pair = Pair; var server = pair.Server; var sSystems = server.ResolveDependency(); @@ -55,7 +63,5 @@ public sealed class QueryTests return false; }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs index b74c35ba11..2eaa073819 100644 --- a/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/AlarmThresholdTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Atmos.Monitor; using Robust.Shared.Prototypes; @@ -5,7 +6,7 @@ namespace Content.IntegrationTests.Tests.Atmos { [TestFixture] [TestOf(typeof(AtmosAlarmThreshold))] - public sealed class AlarmThresholdTest + public sealed class AlarmThresholdTest : GameTest { private const string AlarmThresholdTestDummyId = "AlarmThresholdTestDummy"; @@ -26,7 +27,7 @@ namespace Content.IntegrationTests.Tests.Atmos [Test] public async Task TestAlarmThreshold() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -136,7 +137,6 @@ namespace Content.IntegrationTests.Tests.Atmos Assert.That(alarmType, Is.EqualTo(AtmosAlarmType.Normal)); } }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs b/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs index 4112acac50..d44bfe7ae4 100644 --- a/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/ConstantsTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Atmos.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Atmos.Prototypes; @@ -6,12 +7,12 @@ using Content.Shared.Atmos.Prototypes; namespace Content.IntegrationTests.Tests.Atmos; [TestOf(typeof(Atmospherics))] -public sealed class ConstantsTest +public sealed class ConstantsTest : GameTest { [Test] public async Task TotalGasesTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.EntMan; var protoManager = server.ProtoMan; @@ -42,7 +43,6 @@ public sealed class ConstantsTest } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs b/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs index 07caf447bd..eda9061281 100644 --- a/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/GasArrayTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Robust.Shared.GameObjects; @@ -8,7 +9,7 @@ namespace Content.IntegrationTests.Tests.Atmos; [TestFixture] [TestOf(typeof(Atmospherics))] -public sealed class GasArrayTest +public sealed class GasArrayTest : GameTest { private const string GasTankTestDummyId = "GasTankTestDummy"; @@ -42,7 +43,7 @@ public sealed class GasArrayTest [Test] public async Task TestGasArrayDeserialization() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var compFactory = server.ResolveDependency(); @@ -80,6 +81,5 @@ public sealed class GasArrayTest } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs b/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs index 1cb8fd8b6f..70bbd0ac0c 100644 --- a/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/GasMixtureTest.cs @@ -1,4 +1,5 @@ -using Content.Server.Atmos; +using Content.IntegrationTests.Fixtures; +using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; using Content.Shared.Atmos; using Robust.Shared.GameObjects; @@ -7,12 +8,12 @@ namespace Content.IntegrationTests.Tests.Atmos { [TestFixture] [TestOf(typeof(GasMixture))] - public sealed class GasMixtureTest + public sealed class GasMixtureTest : GameTest { [Test] public async Task TestMerge() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var atmosphereSystem = server.ResolveDependency().GetEntitySystem(); @@ -56,8 +57,6 @@ namespace Content.IntegrationTests.Tests.Atmos Assert.That(a.GetMoles(Gas.Oxygen), Is.EqualTo(50)); }); }); - - await pair.CleanReturnAsync(); } [Test] @@ -69,7 +68,7 @@ namespace Content.IntegrationTests.Tests.Atmos [TestCase(Atmospherics.BreathPercentage)] public async Task RemoveRatio(float ratio) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitAssertion(() => @@ -103,8 +102,6 @@ namespace Content.IntegrationTests.Tests.Atmos Assert.That(a.GetMoles(Gas.Nitrogen), Is.EqualTo(100 - b.GetMoles(Gas.Nitrogen))); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs b/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs index 45ccddfad9..9b43e85396 100644 --- a/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs @@ -1,4 +1,5 @@ using Content.Server.Atmos.EntitySystems; +using Content.IntegrationTests.Fixtures; using Content.Server.Atmos.Piping.EntitySystems; using Content.Shared.Atmos.Components; using Robust.Shared.GameObjects; @@ -6,14 +7,14 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Atmos; [TestFixture] -public sealed class GridJoinTest +public sealed class GridJoinTest : GameTest { private const string CanisterProtoId = "AirCanister"; [Test] public async Task TestGridJoinAtmosphere() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -46,7 +47,5 @@ public sealed class GridJoinTest // Make sure that the canister is now properly tracked as on-grid Assert.That(atmosDeviceSystem.IsJoinedOffGrid(canisterEnt), Is.False); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs b/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs index 406f7fa10d..7269b1b473 100644 --- a/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs +++ b/Content.IntegrationTests/Tests/Atmos/SharedGasSpecificHeatsTest.cs @@ -37,7 +37,7 @@ public sealed class SharedGasSpecificHeatsTest { Connected = true, }; - _pair = await PoolManager.GetServerClient(poolSettings); + _pair = await PoolManager.GetServerClient(poolSettings, new NUnitTestContextWrap(TestContext.CurrentContext, TestContext.Out)); _sEntMan = Server.ResolveDependency(); _cEntMan = Client.ResolveDependency(); diff --git a/Content.IntegrationTests/Tests/Body/GibbingTest.cs b/Content.IntegrationTests/Tests/Body/GibbingTest.cs index a727487940..a3f3c1bcc3 100644 --- a/Content.IntegrationTests/Tests/Body/GibbingTest.cs +++ b/Content.IntegrationTests/Tests/Body/GibbingTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Body; using Content.Shared.Gibbing; using Robust.Shared.GameObjects; @@ -6,7 +7,7 @@ namespace Content.IntegrationTests.Tests.Body; [TestFixture] [TestOf(typeof(GibbableOrganSystem))] -public sealed class GibletTest +public sealed class GibletTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -33,7 +34,7 @@ public sealed class GibletTest [Test] public async Task GibletCountTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -54,7 +55,5 @@ public sealed class GibletTest Assert.That(entityManager.HasComponent(giblet), Is.True); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Body/HandOrganTest.cs b/Content.IntegrationTests/Tests/Body/HandOrganTest.cs index 560dfbf64a..3d885186f1 100644 --- a/Content.IntegrationTests/Tests/Body/HandOrganTest.cs +++ b/Content.IntegrationTests/Tests/Body/HandOrganTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Body; using Content.Shared.Hands.Components; using Robust.Shared.Containers; @@ -9,7 +10,7 @@ namespace Content.IntegrationTests.Tests.Body; [TestFixture] [TestOf(typeof(HandOrganSystem))] -public sealed class HandOrganTest +public sealed class HandOrganTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -46,7 +47,7 @@ public sealed class HandOrganTest [Test] public async Task HandInsertionAndRemovalTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -81,7 +82,5 @@ public sealed class HandOrganTest Assert.That(hands.Count, Is.EqualTo(expectedCount)); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs index d9cce764ab..444bd159f3 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs @@ -12,7 +12,7 @@ public sealed partial class BuckleTest [Test] public async Task BuckleInteractUnbuckleOther() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -55,14 +55,12 @@ public sealed partial class BuckleTest Assert.That(strap.BuckledEntities, Does.Not.Contain(victim)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task BuckleInteractBuckleUnbuckleSelf() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -102,7 +100,5 @@ public sealed partial class BuckleTest Assert.That(strap.BuckledEntities, Does.Not.Contain(user)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index b42f42922a..9c12da3fe8 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Shared.Buckle; using Content.Shared.ActionBlocker; using Content.Shared.Buckle.Components; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Buckle [TestFixture] [TestOf(typeof(BuckleComponent))] [TestOf(typeof(StrapComponent))] - public sealed partial class BuckleTest + public sealed partial class BuckleTest : GameTest { private const string BuckleDummyId = "BuckleDummy"; private const string StrapDummyId = "StrapDummy"; @@ -50,7 +51,7 @@ namespace Content.IntegrationTests.Tests.Buckle [Test] public async Task BuckleUnbuckleCooldownRangeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -228,14 +229,12 @@ namespace Content.IntegrationTests.Tests.Buckle Assert.That(strap.BuckledEntities, Is.Empty); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task BuckledDyingDropItemsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -298,14 +297,12 @@ namespace Content.IntegrationTests.Tests.Buckle buckleSystem.Unbuckle(human, human); Assert.That(buckle.Buckled, Is.False); }); - - await pair.CleanReturnAsync(); } [Test] public async Task ForceUnbuckleBuckleTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -373,7 +370,6 @@ namespace Content.IntegrationTests.Tests.Buckle Assert.That(buckle.Buckled); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs index df85e61550..0c8fc2f0fb 100644 --- a/Content.IntegrationTests/Tests/CargoTest.cs +++ b/Content.IntegrationTests/Tests/CargoTest.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Cargo.Components; using Content.Server.Cargo.Systems; using Content.Server.Nutrition.Components; @@ -17,7 +18,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class CargoTest +public sealed class CargoTest : GameTest { private static readonly HashSet> Ignored = [ @@ -28,7 +29,7 @@ public sealed class CargoTest [Test] public async Task NoCargoOrderArbitrage() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -54,13 +55,11 @@ public sealed class CargoTest } }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task NoCargoBountyArbitrageTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -94,14 +93,12 @@ public sealed class CargoTest mapSystem.DeleteMap(mapId); }); - - await pair.CleanReturnAsync(); } [Test] public async Task NoStaticPriceAndStackPrice() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoManager = server.ProtoMan; @@ -133,8 +130,6 @@ public sealed class CargoTest } } }); - - await pair.CleanReturnAsync(); } /// @@ -144,7 +139,7 @@ public sealed class CargoTest [Test] public async Task NoSliceableBountyArbitrageTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -209,8 +204,6 @@ public sealed class CargoTest } mapSystem.DeleteMap(mapId); }); - - await pair.CleanReturnAsync(); } [TestPrototypes] @@ -233,7 +226,7 @@ public sealed class CargoTest [Test] public async Task StackPrice() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -245,14 +238,12 @@ public sealed class CargoTest var price = priceSystem.GetPrice(ent); Assert.That(price, Is.EqualTo(100.0)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task MobPrice() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var componentFactory = pair.Server.ResolveDependency(); @@ -266,7 +257,5 @@ public sealed class CargoTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs b/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs index 59948c8b17..9ac45549a2 100644 --- a/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/ReagentDataTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Tests.Interaction; using Content.Shared.Chemistry.Reagent; using Robust.Shared.Reflection; @@ -8,12 +9,12 @@ namespace Content.IntegrationTests.Tests.Chemistry; [TestFixture] [TestOf(typeof(ReagentData))] -public sealed class ReagentDataTest +public sealed class ReagentDataTest : GameTest { [Test] public async Task ReagentDataIsSerializable() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var reflection = pair.Server.ResolveDependency(); Assert.Multiple(() => @@ -24,7 +25,5 @@ public sealed class ReagentDataTest Assert.That(instance.HasCustomAttribute(), $"{instance} must have the serializable attribute."); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs index 5b5829d386..f188fd0f66 100644 --- a/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; @@ -9,7 +10,7 @@ namespace Content.IntegrationTests.Tests.Chemistry; [TestFixture] [TestOf(typeof(ChemicalReactionSystem))] -public sealed class SolutionRoundingTest +public sealed class SolutionRoundingTest : GameTest { // This test tests two things: // * A rounding error in reaction code while I was making chloral hydrate @@ -72,7 +73,7 @@ public sealed class SolutionRoundingTest [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -121,7 +122,5 @@ public sealed class SolutionRoundingTest Is.EqualTo((FixedPoint2) 30)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs index 6f50f54103..ffaca012a3 100644 --- a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.FixedPoint; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Chemistry; // reactions can change this assumption [TestFixture] [TestOf(typeof(SharedSolutionContainerSystem))] -public sealed class SolutionSystemTests +public sealed class SolutionSystemTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -53,7 +54,7 @@ public sealed class SolutionSystemTests [Test] public async Task TryAddTwoNonReactiveReagent() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -88,8 +89,6 @@ public sealed class SolutionSystemTests Assert.That(oil, Is.EqualTo(oilQuantity)); }); }); - - await pair.CleanReturnAsync(); } // This test mimics current behavior @@ -97,7 +96,7 @@ public sealed class SolutionSystemTests [Test] public async Task TryAddTooMuchNonReactiveReagent() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -133,15 +132,13 @@ public sealed class SolutionSystemTests Assert.That(oil, Is.EqualTo(FixedPoint2.Zero)); }); }); - - await pair.CleanReturnAsync(); } // Unlike TryAddSolution this adds and two solution without then splits leaving only threshold in original [Test] public async Task TryMixAndOverflowTooMuchReagent() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; @@ -188,15 +185,13 @@ public sealed class SolutionSystemTests Assert.That(oilOverFlow, Is.EqualTo(oilQuantity - oilMix)); }); }); - - await pair.CleanReturnAsync(); } // TryMixAndOverflow will fail if Threshold larger than MaxVolume [Test] public async Task TryMixAndOverflowTooBigOverflow() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -226,14 +221,12 @@ public sealed class SolutionSystemTests .TryMixAndOverflow(solutionEnt.Value, oilAdded, threshold, out _), Is.False); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestTemperatureCalculations() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); const float temp = 100.0f; @@ -264,7 +257,5 @@ public sealed class SolutionSystemTests solutionOne.AddSolution(solutionTwo, protoMan); Assert.That(solutionOne.GetHeatCapacity(protoMan) * solutionOne.Temperature, Is.EqualTo(thermalEnergyOne + thermalEnergyTwo)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs index 0037670556..e720387425 100644 --- a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs @@ -5,6 +5,7 @@ using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Utility; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Shared.Chemistry.EntitySystems; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Chemistry { [TestFixture] [TestOf(typeof(ReactionPrototype))] - public sealed class TryAllReactionsTest + public sealed class TryAllReactionsTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -33,7 +34,7 @@ namespace Content.IntegrationTests.Tests.Chemistry [Description("Tries an individual reaction to see if it succeeds.")] public async Task TryReaction(string reaction) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -134,8 +135,6 @@ namespace Content.IntegrationTests.Tests.Chemistry server.EntMan.DeleteEntity(beaker); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs b/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs index e2bff03501..037224fec5 100644 --- a/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs +++ b/Content.IntegrationTests/Tests/Cleanup/EuiManagerTest.cs @@ -1,35 +1,37 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Administration.UI; using Content.Server.EUI; using Robust.Server.Player; namespace Content.IntegrationTests.Tests.Cleanup; -public sealed class EuiManagerTest +public sealed class EuiManagerTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + Connected = true, + Dirty = true + }; + [Test] + [Retry(2)] + // Even though we are using the server EUI here, we actually want to see if the client EUIManager crashes public async Task EuiManagerRecycleWithOpenWindowTest() { - // Even though we are using the server EUI here, we actually want to see if the client EUIManager crashes - for (var i = 0; i < 2; i++) + var pair = Pair; + var server = pair.Server; + + var sPlayerManager = server.ResolveDependency(); + var eui = server.ResolveDependency(); + + await server.WaitAssertion(() => { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true - }); - var server = pair.Server; + var clientSession = sPlayerManager.Sessions.Single(); + var ui = new AdminAnnounceEui(); + eui.OpenEui(ui, clientSession); + }); - var sPlayerManager = server.ResolveDependency(); - var eui = server.ResolveDependency(); - - await server.WaitAssertion(() => - { - var clientSession = sPlayerManager.Sessions.Single(); - var ui = new AdminAnnounceEui(); - eui.OpenEui(ui, clientSession); - }); - await pair.CleanReturnAsync(); - } + await RunUntilSynced(); } } diff --git a/Content.IntegrationTests/Tests/ClickableTest.cs b/Content.IntegrationTests/Tests/ClickableTest.cs index aaac421ed1..fb20cd9038 100644 --- a/Content.IntegrationTests/Tests/ClickableTest.cs +++ b/Content.IntegrationTests/Tests/ClickableTest.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Client.Clickable; +using Content.IntegrationTests.Fixtures; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class ClickableTest + public sealed class ClickableTest : GameTest { private const double DirSouth = 0; private const double DirNorth = Math.PI; @@ -44,7 +45,7 @@ namespace Content.IntegrationTests.Tests [TestCase("ClickTestRotatingCornerInvisibleNoRot", 0.25f, 0.25f, DirSouthEastJustShy, 1, ExpectedResult = true)] public async Task Test(string prototype, float clickPosX, float clickPosY, double angle, float scale) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -66,7 +67,7 @@ namespace Content.IntegrationTests.Tests }); // Let client sync up. - await pair.RunTicksSync(5); + await RunUntilSynced(); var hit = false; var clientEnt = clientEntManager.GetEntity(serverEntManager.GetNetEntity(serverEnt)); @@ -89,8 +90,6 @@ namespace Content.IntegrationTests.Tests serverEntManager.DeleteEntity(serverEnt); }); - await pair.CleanReturnAsync(); - return hit; } } diff --git a/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs b/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs index 9edc115eee..c21bcf922a 100644 --- a/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs @@ -1,8 +1,9 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Cloning; namespace Content.IntegrationTests.Tests.Cloning; -public sealed class CloningSettingsPrototypeTest +public sealed class CloningSettingsPrototypeTest : GameTest { /// /// Checks that the components named in every are valid components known to the server. @@ -12,7 +13,7 @@ public sealed class CloningSettingsPrototypeTest [Test] public async Task ValidatePrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; var compFactory = server.EntMan.ComponentFactory; @@ -40,7 +41,5 @@ public sealed class CloningSettingsPrototypeTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs index 3fa7e64f1a..566edd47ca 100644 --- a/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs +++ b/Content.IntegrationTests/Tests/Commands/ForceMapTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Maps; using Content.Shared.CCVar; using Robust.Shared.Configuration; @@ -6,7 +7,7 @@ using Robust.Shared.Console; namespace Content.IntegrationTests.Tests.Commands; [TestFixture] -public sealed class ForceMapTest +public sealed class ForceMapTest : GameTest { private const string DefaultMapName = "Empty"; private const string BadMapName = "asdf_asd-fa__sdfAsd_f"; // Hopefully no one ever names a map this... @@ -44,7 +45,7 @@ public sealed class ForceMapTest [Test] public async Task TestForceMapCommand() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -82,7 +83,5 @@ public sealed class ForceMapTest // Cleanup configManager.SetCVar(CCVars.GameMap, DefaultMapName); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs b/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs index d430325e31..eb18d8e1ae 100644 --- a/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs +++ b/Content.IntegrationTests/Tests/Commands/ObjectiveCommandsTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Objectives; using Content.Shared.Mind; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Player; namespace Content.IntegrationTests.Tests.Commands; -public sealed class ObjectiveCommandsTest +public sealed class ObjectiveCommandsTest : GameTest { private const string ObjectiveProtoId = "MindCommandsTestObjective"; @@ -27,6 +28,11 @@ public sealed class ObjectiveCommandsTest - type: DieCondition """; + public override PoolSettings PoolSettings => new () + { + Connected = false + }; + /// /// Creates a dummy session, and assigns it a mind, then /// tests using addobjective, lsobjectives, @@ -35,7 +41,7 @@ public sealed class ObjectiveCommandsTest [Test] public async Task AddListRemoveObjectiveTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; var playerMan = server.ResolveDependency(); @@ -66,7 +72,5 @@ public sealed class ObjectiveCommandsTest await pair.WaitCommand($"rmobjective {playerSession.Name} 0"); Assert.That(mindComp.Objectives, Is.Empty, "rmobjective failed to remove objective"); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs index 5f77af1b10..2d84eebf96 100644 --- a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs +++ b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Database; using Robust.Server.Console; using Robust.Server.Player; @@ -8,14 +9,14 @@ namespace Content.IntegrationTests.Tests.Commands { [TestFixture] [TestOf(typeof(PardonCommand))] - public sealed class PardonCommand + public sealed class PardonCommand : GameTest { private static readonly TimeSpan MarginOfError = TimeSpan.FromMinutes(1); [Test] public async Task PardonTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -148,8 +149,6 @@ namespace Content.IntegrationTests.Tests.Commands await client.WaitPost(() => netMan.ClientConnect(null!, 0, null!)); await pair.RunTicksSync(5); Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(1)); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs index 308f2797c4..15d93c40ae 100644 --- a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs @@ -1,4 +1,5 @@ -using Content.Shared.Administration.Systems; +using Content.IntegrationTests.Fixtures; +using Content.Shared.Administration.Systems; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -14,7 +15,7 @@ namespace Content.IntegrationTests.Tests.Commands { [TestFixture] [TestOf(typeof(RejuvenateSystem))] - public sealed class RejuvenateTest + public sealed class RejuvenateTest : GameTest { private static readonly ProtoId TestDamageGroup = "Toxin"; @@ -36,7 +37,7 @@ namespace Content.IntegrationTests.Tests.Commands [Test] public async Task RejuvenateDeadTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); var prototypeManager = server.ResolveDependency(); @@ -92,7 +93,6 @@ namespace Content.IntegrationTests.Tests.Commands Assert.That(damSystem.GetTotalDamage((human, damageable)), Is.EqualTo(FixedPoint2.Zero)); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs index 72a05b5246..bcacc8011b 100644 --- a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.GameTicking.Commands; using Content.Shared.CCVar; @@ -10,25 +11,27 @@ namespace Content.IntegrationTests.Tests.Commands { [TestFixture] [TestOf(typeof(RestartRoundNowCommand))] - public sealed class RestartRoundNowTest + public sealed class RestartRoundNowTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Dirty = true + }; + [Test] [TestCase(true)] [TestCase(false)] public async Task RestartRoundAfterStart(bool lobbyEnabled) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Dirty = true - }); + var pair = Pair; var server = pair.Server; var configManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var gameTicker = entityManager.System(); - await pair.RunTicksSync(5); + await pair.RunUntilSynced(); GameTick tickBeforeRestart = default; @@ -58,8 +61,7 @@ namespace Content.IntegrationTests.Tests.Commands Assert.That(tickBeforeRestart, Is.LessThan(tickAfterRestart)); }); - await pair.RunTicksSync(5); - await pair.CleanReturnAsync(); + await pair.RunUntilSynced(); } } } diff --git a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs index 32f1f6128e..f78bd4949e 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 @@ using Robust.Shared.Prototypes; 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 @@ public sealed class SuicideCommandTests !ghostComp.CanReturnToBody); }); }); - - await pair.CleanReturnAsync(); } /// @@ -115,12 +116,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicideWhileDamaged() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -166,8 +162,6 @@ public sealed class SuicideCommandTests Assert.That(damageableSystem.GetTotalDamage(player), Is.EqualTo(lethalDamageThreshold)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -177,12 +171,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicideWhenCannotSuicide() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -217,8 +206,6 @@ public sealed class SuicideCommandTests !ghostComp.CanReturnToBody); }); }); - - await pair.CleanReturnAsync(); } @@ -228,12 +215,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicideByHeldItem() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -292,8 +274,6 @@ public sealed class SuicideCommandTests Assert.That(damageableSystem.GetAllDamage((player, damageableComp)).DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -303,12 +283,7 @@ public sealed class SuicideCommandTests [Test] public async Task TestSuicideByHeldItemSpreadDamage() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var consoleHost = server.ResolveDependency(); var entManager = server.ResolveDependency(); @@ -367,7 +342,5 @@ public sealed class SuicideCommandTests Assert.That(damageableSystem.GetAllDamage((player, damageableComp)).DamageDict["Slash"], Is.EqualTo(lethalDamageThreshold / 2)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ConfigPresetTests.cs b/Content.IntegrationTests/Tests/ConfigPresetTests.cs index ebeea7f391..224a5677c0 100644 --- a/Content.IntegrationTests/Tests/ConfigPresetTests.cs +++ b/Content.IntegrationTests/Tests/ConfigPresetTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using Content.IntegrationTests.Fixtures; using Content.Server.Entry; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; @@ -7,12 +8,12 @@ using Robust.Shared.ContentPack; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class ConfigPresetTests +public sealed class ConfigPresetTests : GameTest { [Test] public async Task TestLoadAll() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var resources = server.ResolveDependency(); @@ -70,7 +71,5 @@ public sealed class ConfigPresetTests Assert.Fail($"CVar {name} was not reset to its original value."); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs b/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs index c59b42d638..8c68e5b192 100644 --- a/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs +++ b/Content.IntegrationTests/Tests/Construction/ConstructionActionValid.cs @@ -1,4 +1,5 @@ using System.Text; +using Content.IntegrationTests.Fixtures; using Content.Server.Construction.Completions; using Content.Shared.Construction; using Content.Shared.Construction.Prototypes; @@ -7,7 +8,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Construction { [TestFixture] - public sealed class ConstructionActionValid + public sealed class ConstructionActionValid : GameTest { private bool IsValid(IGraphAction action, IPrototypeManager protoMan, out string prototype) { @@ -47,7 +48,7 @@ namespace Content.IntegrationTests.Tests.Construction [Test] public async Task ConstructionGraphSpawnPrototypeValid() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -84,13 +85,12 @@ namespace Content.IntegrationTests.Tests.Construction }); Assert.That(valid, Is.True, $"One or more SpawnPrototype actions specified invalid entity prototypes!\n{message}"); - await pair.CleanReturnAsync(); } [Test] public async Task ConstructionGraphEdgeValid() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -118,7 +118,6 @@ namespace Content.IntegrationTests.Tests.Construction }); Assert.That(valid, Is.True, $"One or more edges specified invalid node targets!\n{message}"); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs b/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs index 9441443b22..49cc76e945 100644 --- a/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Server.Construction.Components; using Content.Shared.Construction.Prototypes; @@ -7,7 +8,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Construction { [TestFixture] - public sealed class ConstructionPrototypeTest + public sealed class ConstructionPrototypeTest : GameTest { // discount linter for construction graphs // TODO: Create serialization validators for these? @@ -25,7 +26,7 @@ namespace Content.IntegrationTests.Tests.Construction [Description("Tests that a given entity specifies a valid node for construction, and optionally a valid one for deconstruction.")] public async Task ConstructionComponentValid(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -49,8 +50,6 @@ namespace Content.IntegrationTests.Tests.Construction $"Invalid deconstruction node \"{target}\" on graph \"{graph.ID}\" for construction entity \"{proto.ID}\"!"); } }); - - await pair.CleanReturnAsync(); } [Test] @@ -59,7 +58,7 @@ namespace Content.IntegrationTests.Tests.Construction [Description("Tests that a given construction prototype has a valid starting and target node, and a valid path between them.")] public async Task ConstructionFormsValidGraph(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -95,7 +94,6 @@ namespace Content.IntegrationTests.Tests.Construction $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an entity prototype ({next.Entity}) without a ConstructionComponent."); #pragma warning restore NUnit2045 }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs index 37c4b0c9b5..cb7e401601 100644 --- a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs +++ b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Storage.EntitySystems; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Maths; namespace Content.IntegrationTests.Tests { - public sealed class ContainerOcclusionTest + public sealed class ContainerOcclusionTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -34,7 +35,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task TestA() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -69,14 +70,12 @@ namespace Content.IntegrationTests.Tests Assert.That(light.ContainerOccluded); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestB() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -112,14 +111,12 @@ namespace Content.IntegrationTests.Tests Assert.That(light.ContainerOccluded, Is.False); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestAb() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -157,8 +154,6 @@ namespace Content.IntegrationTests.Tests Assert.That(light.ContainerOccluded); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/ContrabandTest.cs b/Content.IntegrationTests/Tests/ContrabandTest.cs index c52ef293e1..9f0fb1d499 100644 --- a/Content.IntegrationTests/Tests/ContrabandTest.cs +++ b/Content.IntegrationTests/Tests/ContrabandTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Contraband; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -5,12 +6,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class ContrabandTest +public sealed class ContrabandTest : GameTest { [Test] public async Task EntityShowDepartmentsAndJobs() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var client = pair.Client; var protoMan = client.ResolveDependency(); var componentFactory = client.ResolveDependency(); @@ -41,7 +42,5 @@ public sealed class ContrabandTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs index 9777054625..758bbe0a50 100644 --- a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Damageable [TestFixture] [TestOf(typeof(DamageableComponent))] [TestOf(typeof(DamageableSystem))] - public sealed class DamageableTest + public sealed class DamageableTest : GameTest { private const string TestDamageableEntityId = "TestDamageableEntityId"; private const string TestGroup1 = "TestGroup1"; @@ -95,7 +96,7 @@ namespace Content.IntegrationTests.Tests.Damageable [Test] public async Task TestDamageableComponents() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntityManager = server.ResolveDependency(); @@ -254,7 +255,6 @@ namespace Content.IntegrationTests.Tests.Damageable sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, -100)); Assert.That(sDamageableSystem.GetTotalDamage(ent), Is.EqualTo(FixedPoint2.Zero)); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Damageable/MobThresholdsTest.cs b/Content.IntegrationTests/Tests/Damageable/MobThresholdsTest.cs index b359f05569..c4179135cd 100644 --- a/Content.IntegrationTests/Tests/Damageable/MobThresholdsTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/MobThresholdsTest.cs @@ -1,10 +1,11 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Shared.Alert; using Content.Shared.Mobs.Components; namespace Content.IntegrationTests.Tests.Damageable; -public sealed class MobThresholdsTest +public sealed class MobThresholdsTest : GameTest { private static string[] _entitiesWithThresholds = GameDataScrounger.EntitiesWithComponent("MobThresholds"); @@ -14,7 +15,7 @@ public sealed class MobThresholdsTest [Description("Ensures every entity with mob thresholds has valid mob state configuration corresponding to some AlertPrototype.")] public async Task ValidateMobThresholds(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; @@ -33,7 +34,5 @@ public sealed class MobThresholdsTest Assert.That(alertStates, Does.Contain(state), $"{proto.ID} does not have an alert state for mob state {state}"); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Damageable/StaminaComponentTest.cs b/Content.IntegrationTests/Tests/Damageable/StaminaComponentTest.cs index 09be373a4c..b4a278529d 100644 --- a/Content.IntegrationTests/Tests/Damageable/StaminaComponentTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/StaminaComponentTest.cs @@ -1,11 +1,12 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Shared.Damage.Components; using Content.Shared.FixedPoint; namespace Content.IntegrationTests.Tests.Damageable; -public sealed class StaminaComponentTest +public sealed class StaminaComponentTest : GameTest { private static string[] _entitiesWithStamina = GameDataScrounger.EntitiesWithComponent("Stamina"); @@ -15,7 +16,7 @@ public sealed class StaminaComponentTest [Description("Ensures every entity with Stamina has a valid stamina configuration.")] public async Task ValidateStamina(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; @@ -46,7 +47,5 @@ public sealed class StaminaComponentTest #pragma warning restore NUnit2041 } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/DeleteInventoryTest.cs b/Content.IntegrationTests/Tests/DeleteInventoryTest.cs index 49d54bbecf..bbaf11a052 100644 --- a/Content.IntegrationTests/Tests/DeleteInventoryTest.cs +++ b/Content.IntegrationTests/Tests/DeleteInventoryTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Clothing.Components; using Content.Shared.Clothing.EntitySystems; using Content.Shared.Inventory; @@ -7,14 +8,14 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class DeleteInventoryTest + public sealed class DeleteInventoryTest : GameTest { // Test that when deleting an entity with an InventoryComponent, // any equipped items also get deleted. [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); var entMgr = server.ResolveDependency(); @@ -44,7 +45,6 @@ namespace Content.IntegrationTests.Tests // Assert that child item was also deleted. Assert.That(item.Deleted, Is.True); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs index f5010eefdc..a83891b8fc 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -13,12 +14,12 @@ namespace Content.IntegrationTests.Tests.Destructible [TestFixture] [TestOf(typeof(DamageGroupTrigger))] [TestOf(typeof(AndTrigger))] - public sealed class DestructibleDamageGroupTest + public sealed class DestructibleDamageGroupTest : GameTest { [Test] public async Task AndTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -193,7 +194,6 @@ namespace Content.IntegrationTests.Tests.Destructible // No new thresholds reached as triggers once is set to true and it already triggered before Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs index 70baaea95a..47f44ad496 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; @@ -12,12 +13,12 @@ namespace Content.IntegrationTests.Tests.Destructible [TestFixture] [TestOf(typeof(DamageTypeTrigger))] [TestOf(typeof(AndTrigger))] - public sealed class DestructibleDamageTypeTest + public sealed class DestructibleDamageTypeTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -188,7 +189,6 @@ namespace Content.IntegrationTests.Tests.Destructible // No new thresholds reached as triggers once is set to true and it already triggered before Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs index df98294ee9..0aa0449682 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Destructible.Thresholds.Behaviors; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; @@ -10,12 +11,12 @@ using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototy namespace Content.IntegrationTests.Tests.Destructible { - public sealed class DestructibleDestructionTest + public sealed class DestructibleDestructionTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -89,7 +90,6 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(found, Is.True, $"Unable to find {SpawnedEntityId} nearby for destructible test; found {entitiesInRange.Count} entities."); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs index 4460affedf..da56a43a4a 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Destructible; using Content.Server.Destructible.Thresholds; using Content.Server.Destructible.Thresholds.Behaviors; @@ -19,12 +20,12 @@ namespace Content.IntegrationTests.Tests.Destructible [TestFixture] [TestOf(typeof(DestructibleComponent))] [TestOf(typeof(DamageThreshold))] - public sealed class DestructibleThresholdActivationTest + public sealed class DestructibleThresholdActivationTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntityManager = server.ResolveDependency(); @@ -289,7 +290,6 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/DeviceLinking/DeviceLinkingTest.cs b/Content.IntegrationTests/Tests/DeviceLinking/DeviceLinkingTest.cs index c62b46ab6d..7a2a880bae 100644 --- a/Content.IntegrationTests/Tests/DeviceLinking/DeviceLinkingTest.cs +++ b/Content.IntegrationTests/Tests/DeviceLinking/DeviceLinkingTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Server.DeviceLinking.Systems; using Content.Shared.DeviceLinking; @@ -7,7 +8,7 @@ using Robust.Shared.Maths; namespace Content.IntegrationTests.Tests.DeviceLinking; -public sealed class DeviceLinkingTest +public sealed class DeviceLinkingTest : GameTest { private const string PortTesterProtoId = "DeviceLinkingSinkPortTester"; @@ -29,7 +30,7 @@ public sealed class DeviceLinkingTest [Description("Ensures all devices that can sink signals will not cause exceptions when signaled.")] public async Task DeviceLinkSinkAllPortsTest(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; var compFact = server.ResolveDependency(); @@ -77,7 +78,5 @@ public sealed class DeviceLinkingTest } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs index fdc0e1a4d4..6ecdffc58c 100644 --- a/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs +++ b/Content.IntegrationTests/Tests/DeviceNetwork/DeviceNetworkTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; using Content.Shared.DeviceNetwork; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork [TestOf(typeof(DeviceNetworkComponent))] [TestOf(typeof(WiredNetworkComponent))] [TestOf(typeof(WirelessNetworkComponent))] - public sealed class DeviceNetworkTest + public sealed class DeviceNetworkTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -50,7 +51,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork [Test] public async Task NetworkDeviceSendAndReceive() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -104,13 +105,12 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork { Assert.That(payload, Is.EquivalentTo(deviceNetTestSystem.LastPayload)); }); - await pair.CleanReturnAsync(); } [Test] public async Task WirelessNetworkDeviceSendAndReceive() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; @@ -188,14 +188,12 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork { Assert.That(payload, Is.Not.EqualTo(deviceNetTestSystem.LastPayload).AsCollection); }); - - await pair.CleanReturnAsync(); } [Test] public async Task WiredNetworkDeviceSendAndReceive() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; @@ -271,8 +269,6 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork { Assert.That(payload, Is.EqualTo(deviceNetTestSystem.LastPayload).AsCollection); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index 52b669b09d..f7b20819c2 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -1,6 +1,7 @@ #nullable enable annotations using System.Linq; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Disposal.Unit; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -16,7 +17,7 @@ namespace Content.IntegrationTests.Tests.Disposal [TestOf(typeof(DisposalHolderComponent))] [TestOf(typeof(DisposalEntryComponent))] [TestOf(typeof(DisposalUnitComponent))] - public sealed class DisposalUnitTest + public sealed class DisposalUnitTest : GameTest { [Reflect(false)] private sealed class DisposalUnitTestSystem : EntitySystem @@ -145,7 +146,7 @@ namespace Content.IntegrationTests.Tests.Disposal [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -240,8 +241,6 @@ namespace Content.IntegrationTests.Tests.Disposal // Re-pressurizing Flush(disposalUnit, unitComponent, false, disposalSystem); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs b/Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs index 32f8b58542..d9619076e0 100644 --- a/Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs +++ b/Content.IntegrationTests/Tests/DoAfter/DoAfterServerTest.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Robust.Shared.GameObjects; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.DoAfter { [TestFixture] [TestOf(typeof(DoAfterComponent))] - public sealed partial class DoAfterServerTest + public sealed partial class DoAfterServerTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -35,7 +36,7 @@ namespace Content.IntegrationTests.Tests.DoAfter [Test] public async Task TestSerializable() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); var refMan = server.ResolveDependency(); @@ -55,14 +56,12 @@ namespace Content.IntegrationTests.Tests.DoAfter } }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestFinished() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -84,14 +83,12 @@ namespace Content.IntegrationTests.Tests.DoAfter await server.WaitRunTicks(1); Assert.That(ev.Cancelled, Is.False); - - await pair.CleanReturnAsync(); } [Test] public async Task TestCancelled() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.EntMan; var timing = server.ResolveDependency(); @@ -113,8 +110,6 @@ namespace Content.IntegrationTests.Tests.DoAfter await server.WaitRunTicks(3); Assert.That(ev.Cancelled); - - await pair.CleanReturnAsync(); } /// @@ -124,7 +119,7 @@ namespace Content.IntegrationTests.Tests.DoAfter [Test] public async Task TestGetInteractingEntities() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.EntMan; var timing = server.ResolveDependency(); @@ -175,8 +170,6 @@ namespace Content.IntegrationTests.Tests.DoAfter entityManager.DeleteEntity(target); entityManager.DeleteEntity(target2); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs index 69fe66039b..af03de169f 100644 --- a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs +++ b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Doors.Systems; using Content.Shared.Doors.Components; using Robust.Shared.GameObjects; @@ -11,7 +12,7 @@ namespace Content.IntegrationTests.Tests.Doors { [TestFixture] [TestOf(typeof(AirlockComponent))] - public sealed class AirlockTest + public sealed class AirlockTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -54,7 +55,7 @@ namespace Content.IntegrationTests.Tests.Doors [Test] public async Task OpenCloseDestroyTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -104,16 +105,12 @@ namespace Content.IntegrationTests.Tests.Doors entityManager.DeleteEntity(airlock); }); }); - - server.RunTicks(5); - - await pair.CleanReturnAsync(); } [Test] public async Task AirlockBlockTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -179,7 +176,6 @@ namespace Content.IntegrationTests.Tests.Doors { Assert.That(Math.Abs(xformSystem.GetWorldPosition(airlockPhysicsDummy).X - 1), Is.GreaterThan(0.01f)); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/DummyIconTest.cs b/Content.IntegrationTests/Tests/DummyIconTest.cs index 62197bb319..dcb5d315dc 100644 --- a/Content.IntegrationTests/Tests/DummyIconTest.cs +++ b/Content.IntegrationTests/Tests/DummyIconTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Robust.Client.GameObjects; using Robust.Client.ResourceManagement; using Robust.Shared.Prototypes; @@ -7,12 +8,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class DummyIconTest + public sealed class DummyIconTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var client = pair.Client; var prototypeManager = client.ResolveDependency(); var resourceCache = client.ResolveDependency(); @@ -32,7 +33,6 @@ namespace Content.IntegrationTests.Tests proto.ID); } }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 9b0e7729f5..59dfec1060 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Robust.Shared; using Robust.Shared.Audio.Components; using Robust.Shared.Configuration; @@ -16,17 +18,28 @@ namespace Content.IntegrationTests.Tests { [TestFixture] [TestOf(typeof(EntityUid))] - public sealed class EntityTest + public sealed class EntityTest : GameTest { private static readonly ProtoId SpawnerCategory = "Spawner"; + public override PoolSettings PoolSettings => new() + { + Connected = true, + Dirty = true + }; + + public static PoolSettings Disconnected => new() + { + Dirty = true, + }; + [Test] + [PairConfig(nameof(Disconnected))] public async Task SpawnAndDeleteAllEntitiesOnDifferentMaps() { // This test dirties the pair as it simply deletes ALL entities when done. Overhead of restarting the round // is minimal relative to the rest of the test. - var settings = new PoolSettings { Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var server = pair.Server; var entityMan = server.ResolveDependency(); @@ -79,17 +92,14 @@ namespace Content.IntegrationTests.Tests Assert.That(entityMan.EntityCount, Is.Zero); }); - - await pair.CleanReturnAsync(); } [Test] + [PairConfig(nameof(Disconnected))] public async Task SpawnAndDeleteAllEntitiesInTheSameSpot() { - // This test dirties the pair as it simply deletes ALL entities when done. Overhead of restarting the round - // is minimal relative to the rest of the test. - var settings = new PoolSettings { Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; + Assert.That(pair.Client.Session, Is.Null); var server = pair.Server; var map = await pair.CreateTestMap(); @@ -134,8 +144,6 @@ namespace Content.IntegrationTests.Tests Assert.That(entityMan.EntityCount, Is.Zero); }); - - await pair.CleanReturnAsync(); } /// @@ -145,10 +153,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task SpawnAndDirtyAllEntities() { - // This test dirties the pair as it simply deletes ALL entities when done. Overhead of restarting the round - // is minimal relative to the rest of the test. - var settings = new PoolSettings { Connected = true, Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -182,7 +187,7 @@ namespace Content.IntegrationTests.Tests } }); - await pair.RunTicksSync(15); + await pair.RunUntilSynced(); // Make sure the client actually received the entities // 500 is completely arbitrary. Note that the client & sever entity counts aren't expected to match. @@ -209,8 +214,6 @@ namespace Content.IntegrationTests.Tests Assert.That(sEntMan.EntityCount, Is.Zero); }); - - await pair.CleanReturnAsync(); } /// @@ -230,8 +233,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task SpawnAndDeleteEntityCountTest() { - var settings = new PoolSettings { Connected = true, Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var mapSys = pair.Server.System(); var server = pair.Server; var client = pair.Client; @@ -317,8 +319,6 @@ namespace Content.IntegrationTests.Tests BuildDiffString(clientEntities, Entities(client.EntMan), client.EntMan)); } }); - - await pair.CleanReturnAsync(); } private static string BuildDiffString(IEnumerable oldEnts, IEnumerable newEnts, IEntityManager entMan) @@ -392,7 +392,7 @@ namespace Content.IntegrationTests.Tests "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 +445,6 @@ namespace Content.IntegrationTests.Tests } }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs b/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs index 01f52f7d83..7a486e6839 100644 --- a/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Explosion/ExplosionPrototypeTest.cs @@ -1,9 +1,10 @@ +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Utility; using Content.Shared.Explosion; namespace Content.IntegrationTests.Tests.Explosion; -public sealed class ExplosionPrototypeTest +public sealed class ExplosionPrototypeTest : GameTest { private static string[] _explosionKinds = GameDataScrounger.PrototypesOfKind(); @@ -13,7 +14,7 @@ public sealed class ExplosionPrototypeTest [Description("Ensures various properties of ExplosionPrototype are correctly configured.")] public async Task Validate(string protoKey) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; @@ -40,7 +41,5 @@ public sealed class ExplosionPrototypeTest Assert.That(proto.IntensityPerState, Is.Positive); Assert.That(proto.FireStates, Is.Positive); } - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs b/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs index 99354e16c1..142ec21ddb 100644 --- a/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs +++ b/Content.IntegrationTests/Tests/FillLevelSpriteTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Prototypes; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests; /// Tests to see if any entity prototypes specify solution fill level sprites that don't exist. /// [TestFixture] -public sealed class FillLevelSpriteTest +public sealed class FillLevelSpriteTest : GameTest { private static readonly string[] HandStateNames = ["left", "right"]; private static readonly string[] EquipStateNames = ["back", "suitstorage"]; @@ -20,7 +21,7 @@ public sealed class FillLevelSpriteTest [Test] public async Task FillLevelSpritesExist() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var client = pair.Client; var protoMan = client.ResolveDependency(); var componentFactory = client.ResolveDependency(); @@ -101,7 +102,5 @@ public sealed class FillLevelSpriteTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs b/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs index 1afed38966..3d9ff61a2d 100644 --- a/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/AbsorbentTest.cs @@ -7,12 +7,13 @@ using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; namespace Content.IntegrationTests.Tests.Fluids; [TestFixture] [TestOf(typeof(AbsorbentComponent))] -public sealed class AbsorbentTest +public sealed class AbsorbentTest : GameTest { private const string UserDummyId = "UserDummy"; private const string AbsorbentDummyId = "AbsorbentDummy"; @@ -73,7 +74,7 @@ public sealed class AbsorbentTest [TestCaseSource(nameof(TestCasesToRun))] public async Task AbsorbentOnRefillableTest(TestSolutionCase testCase) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -123,15 +124,12 @@ public sealed class AbsorbentTest Assert.That(VolumeOfPrototypeInComposition(refillableComposition, NonEvaporablePrototypeId), Is.EqualTo(testCase.ExpectedRefillableSolution.VolumeOfNonEvaporable)); }); }); - await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } [TestCaseSource(nameof(TestCasesToRunOnSmallRefillable))] public async Task AbsorbentOnSmallRefillableTest(TestSolutionCase testCase) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -180,9 +178,6 @@ public sealed class AbsorbentTest Assert.That(VolumeOfPrototypeInComposition(refillableComposition, NonEvaporablePrototypeId), Is.EqualTo(testCase.ExpectedRefillableSolution.VolumeOfNonEvaporable)); }); }); - await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } private static FixedPoint2 VolumeOfPrototypeInComposition(Dictionary composition, string prototypeId) diff --git a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs index d6f9bf3598..c073020e9c 100644 --- a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.Fluids.EntitySystems; using Content.Server.Spreader; using Content.Shared.Chemistry.Components; @@ -14,7 +15,7 @@ namespace Content.IntegrationTests.Tests.Fluids; [TestFixture] [TestOf(typeof(SpreaderSystem))] -public sealed class FluidSpill +public sealed class FluidSpill : GameTest { private static PuddleComponent? GetPuddle(IEntityManager entityManager, Entity mapGrid, Vector2i pos) { @@ -36,7 +37,7 @@ public sealed class FluidSpill [Test] public async Task SpillCorner() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -110,7 +111,5 @@ public sealed class FluidSpill } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs index ee2d0cb1f7..71fbefa241 100644 --- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Coordinates; @@ -10,12 +11,12 @@ namespace Content.IntegrationTests.Tests.Fluids { [TestFixture] [TestOf(typeof(PuddleComponent))] - public sealed class PuddleTest + public sealed class PuddleTest : GameTest { [Test] public async Task TilePuddleTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -32,15 +33,12 @@ namespace Content.IntegrationTests.Tests.Fluids Assert.That(spillSystem.TrySpillAt(coordinates, solution, out _), Is.True); }); - await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } [Test] public async Task SpaceNoPuddleTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -69,8 +67,6 @@ namespace Content.IntegrationTests.Tests.Fluids Assert.That(spillSystem.TrySpillAt(coordinates, solution, out _), Is.False); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/FollowerSystemTest.cs b/Content.IntegrationTests/Tests/FollowerSystemTest.cs index f4447426c7..464b3306f2 100644 --- a/Content.IntegrationTests/Tests/FollowerSystemTest.cs +++ b/Content.IntegrationTests/Tests/FollowerSystemTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Shared.Follower; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Map; namespace Content.IntegrationTests.Tests; [TestFixture, TestOf(typeof(FollowerSystem))] -public sealed class FollowerSystemTest +public sealed class FollowerSystemTest : GameTest { /// /// This test ensures that deleting a map while an entity follows another doesn't throw any exceptions. @@ -15,7 +16,7 @@ public sealed class FollowerSystemTest [Test] public async Task FollowerMapDeleteTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -44,6 +45,5 @@ public sealed class FollowerSystemTest entMan.DeleteEntity(mapSys.GetMap(map)); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs index dae3203f9f..97681ad469 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.Cuffs; using Content.Shared.Cuffs.Components; using Content.Shared.Hands.Components; @@ -10,7 +11,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking [TestFixture] [TestOf(typeof(CuffableComponent))] [TestOf(typeof(HandcuffComponent))] - public sealed class HandCuffTest + public sealed class HandCuffTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -40,7 +41,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; EntityUid human; @@ -98,8 +99,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking cuffableSys.TryAddNewCuffs(human, human, secondCuffs, cuffed); Assert.That(cuffed.CuffedHandCount, Is.EqualTo(4), "Player doesn't have correct amount of hands cuffed"); }); - - await pair.CleanReturnAsync(); } private static void AddHand(NetEntity to, IServerConsoleHost host) diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs index ef94cf0f00..3473f1bc9c 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/EntityPrototypeComponentsTest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using Content.IntegrationTests.Fixtures; using Robust.Shared.ContentPack; using Robust.Shared.GameObjects; using Robust.Shared.Utility; @@ -11,12 +12,12 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components { [TestFixture] [TestOf(typeof(Server.Entry.IgnoredComponents))] - public sealed class EntityPrototypeComponentsTest + public sealed class EntityPrototypeComponentsTest : GameTest { [Test] public async Task PrototypesHaveKnownComponents() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -100,7 +101,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components if (unknownComponentsClient.Count + unknownComponentsServer.Count + doubleIgnoredComponents.Count == 0) { - await pair.CleanReturnAsync(); Assert.Pass($"Validated {entitiesValidated} entities with {componentsValidated} components in {paths.Length} files."); return; } @@ -131,11 +131,11 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components [Test] public async Task IgnoredComponentsExistInTheCorrectPlaces() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var client = pair.Client; var serverComponents = server.ResolveDependency(); - var ignoredServerNames = Server.Entry.IgnoredComponents.List; + var ignoredServerNames = Content.Server.Entry.IgnoredComponents.List; var clientComponents = client.ResolveDependency(); var failureMessages = ""; @@ -151,7 +151,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components } } Assert.That(failureMessages, Is.Empty); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs index 6c103bf6ae..71c37076ee 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; @@ -107,8 +110,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs var expectedIDs = new[] { "HumanHealth", "Debug2" }; 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 b215584c57..4a059eb1cc 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 sealed class AntagPreferenceTest 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 b9a02339fb..b8efb64b8b 100644 --- a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.GameTicking.Presets; using Content.Shared.CCVar; @@ -9,7 +10,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class FailAndStartPresetTest +public sealed class FailAndStartPresetTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -52,19 +53,21 @@ public sealed class FailAndStartPresetTest - type: TestRule "; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true + }; + /// /// Test that a nuke ops gamemode can start after failing to start once. /// [Test] public async Task FailAndStartTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -115,7 +118,6 @@ public sealed class FailAndStartPresetTest server.CfgMan.SetCVar(CCVars.GameLobbyFallbackEnabled, true); server.CfgMan.SetCVar(CCVars.GameLobbyDefaultPreset, "secret"); server.System().Run = false; - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs index 53165adca0..f9074f7dc6 100644 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Body.Components; using Content.Server.GameTicking; using Content.Server.GameTicking.Presets; @@ -30,24 +31,27 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class NukeOpsTest +public sealed class NukeOpsTest : GameTest { private static readonly ProtoId SyndicateFaction = "Syndicate"; private static readonly ProtoId NanotrasenFaction = "NanoTrasen"; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true + }; + + /// /// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded. /// [Test] public async Task TryStopNukeOpsFromConstantlyFailing() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -260,6 +264,5 @@ public sealed class NukeOpsTest }); ticker.SetGamePreset((GamePresetPrototype?) null); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs index c805d04a71..0bbf2fae4f 100644 --- a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; @@ -9,12 +10,14 @@ namespace Content.IntegrationTests.Tests.GameRules { [TestFixture] [TestOf(typeof(MaxTimeRestartRuleSystem))] - public sealed class RuleMaxTimeRestartTest + public sealed class RuleMaxTimeRestartTest : GameTest { + public override PoolSettings PoolSettings => new() { InLobby = true }; + [Test] public async Task RestartTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { InLobby = true }); + var pair = Pair; var server = pair.Server; Assert.That(server.EntMan.Count(), Is.Zero); @@ -64,8 +67,6 @@ namespace Content.IntegrationTests.Tests.GameRules { Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs b/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs index 5d7ae8efbf..521b043c68 100644 --- a/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs @@ -1,19 +1,22 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class SecretStartsTest +public sealed class SecretStartsTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings { Dirty = true }; + /// /// Tests that when secret is started, all of the game rules it successfully adds are also started. /// [Test] public async Task TestSecretStarts() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Dirty = true }); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -38,7 +41,5 @@ public sealed class SecretStartsTest // End all rules gameTicker.ClearGameRules(); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/StartEndGameRulesTest.cs b/Content.IntegrationTests/Tests/GameRules/StartEndGameRulesTest.cs index bda931397b..1eef1ab0a1 100644 --- a/Content.IntegrationTests/Tests/GameRules/StartEndGameRulesTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/StartEndGameRulesTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Shared.CCVar; using Robust.Shared.Configuration; @@ -7,19 +8,21 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class StartEndGameRulesTest +public sealed class StartEndGameRulesTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + Dirty = true, + DummyTicker = false + }; + /// /// Tests that all game rules can be added/started/ended at the same time without exceptions. /// [Test] public async Task TestAllConcurrent() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); var gameTicker = server.ResolveDependency().GetEntitySystem(); @@ -47,7 +50,5 @@ public sealed class StartEndGameRulesTest gameTicker.ClearGameRules(); Assert.That(!gameTicker.GetAddedGameRules().Any()); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs b/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs index 97fe1c8762..318c4b6b42 100644 --- a/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Antag.Components; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; @@ -17,23 +18,25 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.GameRules; [TestFixture] -public sealed class TraitorRuleTest +public sealed class TraitorRuleTest : GameTest { private const string TraitorGameRuleProtoId = "Traitor"; private const string TraitorAntagRoleName = "Traitor"; private static readonly ProtoId SyndicateFaction = "Syndicate"; private static readonly ProtoId NanotrasenFaction = "NanoTrasen"; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true, + }; + [Test] public async Task TestTraitorObjectives() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings() - { - Dirty = true, - DummyTicker = false, - Connected = true, - InLobby = true, - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; var entMan = server.EntMan; @@ -123,9 +126,6 @@ public sealed class TraitorRuleTest $"MaxDifficulty exceeded! Objectives: {string.Join(", ", mindComp.Objectives.Select(o => FormatObjective(o, entMan)))}"); Assert.That(mindComp.Objectives, Is.Not.Empty, $"No objectives assigned!"); - - - await pair.CleanReturnAsync(); } private static string FormatObjective(Entity entity, IEntityManager entMan) diff --git a/Content.IntegrationTests/Tests/Gibbing/GibTest.cs b/Content.IntegrationTests/Tests/Gibbing/GibTest.cs index ee0f7a742d..23be8f028a 100644 --- a/Content.IntegrationTests/Tests/Gibbing/GibTest.cs +++ b/Content.IntegrationTests/Tests/Gibbing/GibTest.cs @@ -1,16 +1,17 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Shared.Gibbing; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Body; [TestFixture] -public sealed class GibTest +public sealed class GibTest : GameTest { [Test] public async Task TestGib() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var (server, client) = (pair.Server, pair.Client); var map = await pair.CreateTestMap(); @@ -30,7 +31,5 @@ public sealed class GibTest await pair.RunTicksSync(5); Assert.That(!client.EntMan.EntityExists(nuid)); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs index 0951e7e260..0ef52a12a6 100644 --- a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs +++ b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Gravity; using Content.Shared.Alert; using Content.Shared.Gravity; @@ -8,7 +9,7 @@ namespace Content.IntegrationTests.Tests.Gravity [TestFixture] [TestOf(typeof(GravitySystem))] [TestOf(typeof(GravityGeneratorComponent))] - public sealed class WeightlessStatusTests + public sealed class WeightlessStatusTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -38,7 +39,7 @@ namespace Content.IntegrationTests.Tests.Gravity [Test] public async Task WeightlessStatusTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityManager = server.ResolveDependency(); @@ -86,8 +87,6 @@ namespace Content.IntegrationTests.Tests.Gravity }); await pair.RunTicksSync(10); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/GravityGridTest.cs b/Content.IntegrationTests/Tests/GravityGridTest.cs index 047ec0259a..10804ea201 100644 --- a/Content.IntegrationTests/Tests/GravityGridTest.cs +++ b/Content.IntegrationTests/Tests/GravityGridTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Power.Components; using Content.Shared.Gravity; using Robust.Shared.GameObjects; @@ -11,7 +12,7 @@ namespace Content.IntegrationTests.Tests /// making sure that gravity is applied to the correct grids. [TestFixture] [TestOf(typeof(GravityGeneratorComponent))] - public sealed class GravityGridTest + public sealed class GravityGridTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -31,7 +32,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -96,8 +97,6 @@ namespace Content.IntegrationTests.Tests Assert.That(entityMan.GetComponent(grid2).Enabled, Is.False); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs b/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs index dec2c40c0a..98b7056d00 100644 --- a/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs +++ b/Content.IntegrationTests/Tests/Guidebook/DocumentParsingTest.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Content.Client.Guidebook; using Content.Client.Guidebook.Richtext; +using Content.IntegrationTests.Fixtures; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; @@ -13,7 +14,7 @@ namespace Content.IntegrationTests.Tests.Guidebook; /// [TestFixture] [TestOf(typeof(DocumentParsingManager))] -public sealed class DocumentParsingTest +public sealed class DocumentParsingTest : GameTest { public string TestDocument = @"multiple @@ -45,7 +46,7 @@ whitespace before newlines are ignored. [Test] public async Task ParseTestDocument() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var client = pair.Client; await client.WaitIdleAsync(); var parser = client.ResolveDependency(); @@ -133,8 +134,6 @@ whitespace before newlines are ignored. subTest2.Params.TryGetValue("k", out val); Assert.That(val, Is.EqualTo(@"<>\>=""=<-_?*3.0//")); - - await pair.CleanReturnAsync(); } public sealed class TestControl : Control, IDocumentTag diff --git a/Content.IntegrationTests/Tests/Guidebook/GuideEntryPrototypeTests.cs b/Content.IntegrationTests/Tests/Guidebook/GuideEntryPrototypeTests.cs index 13d0ad1497..016b06abe8 100644 --- a/Content.IntegrationTests/Tests/Guidebook/GuideEntryPrototypeTests.cs +++ b/Content.IntegrationTests/Tests/Guidebook/GuideEntryPrototypeTests.cs @@ -1,5 +1,6 @@ using Content.Client.Guidebook; using Content.Client.Guidebook.Richtext; +using Content.IntegrationTests.Fixtures; using Robust.Shared.ContentPack; using Robust.Shared.Prototypes; using Content.IntegrationTests.Utility; @@ -12,7 +13,7 @@ namespace Content.IntegrationTests.Tests.Guidebook; [TestOf(typeof(GuidebookSystem))] [TestOf(typeof(GuideEntryPrototype))] [TestOf(typeof(DocumentParsingManager))] -public sealed class GuideEntryPrototypeTests +public sealed class GuideEntryPrototypeTests : GameTest { private static string[] _guideEntries = GameDataScrounger.PrototypesOfKind(); @@ -21,7 +22,7 @@ public sealed class GuideEntryPrototypeTests [Description("Ensures a given guidebook entry is valid, checking the document/etc.")] public async Task Validate(string protoKey) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var client = pair.Client; await client.WaitIdleAsync(); var protoMan = client.ResolveDependency(); @@ -36,7 +37,5 @@ public sealed class GuideEntryPrototypeTests Assert.That(parser.TryAddMarkup(new Document(), text), $"Failed to parse the guide entry's document."); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Hands/HandTests.cs b/Content.IntegrationTests/Tests/Hands/HandTests.cs index d5cf75c463..95f053a1cb 100644 --- a/Content.IntegrationTests/Tests/Hands/HandTests.cs +++ b/Content.IntegrationTests/Tests/Hands/HandTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Storage.EntitySystems; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; @@ -10,7 +11,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Hands; [TestFixture] -public sealed class HandTests +public sealed class HandTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -25,14 +26,16 @@ public sealed class HandTests "; + public override PoolSettings PoolSettings => new() + { + Connected = true, + DummyTicker = false + }; + [Test] public async Task TestPickupDrop() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -69,17 +72,12 @@ public sealed class HandTests Assert.That(sys.GetActiveItem((player, hands)), Is.Null); await server.WaitPost(() => mapSystem.DeleteMap(data.MapId)); - await pair.CleanReturnAsync(); } [Test] public async Task TestPickUpThenDropInContainer() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var map = await pair.CreateTestMap(); await pair.RunTicksSync(5); @@ -134,6 +132,5 @@ public sealed class HandTests Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform))); await server.WaitPost(() => mapSystem.DeleteMap(map.MapId)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs b/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs index 929a231159..accdd4667c 100644 --- a/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs +++ b/Content.IntegrationTests/Tests/HumanInventoryUniformSlotsTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Inventory; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ namespace Content.IntegrationTests.Tests // i.e. the interaction between uniforms and the pocket/ID slots. // and also how big items don't fit in pockets. [TestFixture] - public sealed class HumanInventoryUniformSlotsTest + public sealed class HumanInventoryUniformSlotsTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -55,7 +56,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; @@ -130,8 +131,6 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } private static bool IsDescendant(EntityUid descendant, EntityUid parent, IEntityManager entManager) diff --git a/Content.IntegrationTests/Tests/Humanoid/HideablePrototypeValidation.cs b/Content.IntegrationTests/Tests/Humanoid/HideablePrototypeValidation.cs index d95992bda2..4ee65c9c32 100644 --- a/Content.IntegrationTests/Tests/Humanoid/HideablePrototypeValidation.cs +++ b/Content.IntegrationTests/Tests/Humanoid/HideablePrototypeValidation.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Body; using Content.Shared.Clothing.Components; using Content.Shared.Humanoid; @@ -8,13 +9,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Humanoid; [TestFixture] -public sealed class HideablePrototypeValidation +public sealed class HideablePrototypeValidation : GameTest { [Test] public async Task NoOrgansWithoutClothing() { - await using var pair = await PoolManager.GetServerClient(); - + var pair = Pair; var requirements = new Dictionary>(); foreach (var (proto, component) in pair.GetPrototypesWithComponent()) { @@ -42,14 +42,12 @@ public sealed class HideablePrototypeValidation { Assert.That(provided, Does.Contain(key), $"No clothing will hide {key} that can be hidden on {string.Join(", ", requirement.Select(it => it.Id))}"); } - - await pair.CleanReturnAsync(); } [Test] public async Task NoClothingWithoutOrgans() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var requirements = new Dictionary>(); foreach (var (proto, component) in pair.GetPrototypesWithComponent()) @@ -74,7 +72,5 @@ public sealed class HideablePrototypeValidation { Assert.That(provided, Does.Contain(key), $"No organ will hide {key} that can be hidden by {string.Join(", ", requirement.Select(it => it.Id))}"); } - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Humanoid/HumanoidProfileTests.cs b/Content.IntegrationTests/Tests/Humanoid/HumanoidProfileTests.cs index 1e4a094b79..48eb521c5e 100644 --- a/Content.IntegrationTests/Tests/Humanoid/HumanoidProfileTests.cs +++ b/Content.IntegrationTests/Tests/Humanoid/HumanoidProfileTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences; @@ -10,14 +11,14 @@ namespace Content.IntegrationTests.Tests.Humanoid; [TestFixture] [TestOf(typeof(HumanoidProfileSystem))] -public sealed class HumanoidProfileTests +public sealed class HumanoidProfileTests : GameTest { private static readonly ProtoId Vox = "Vox"; [Test] public async Task EnsureValidLoading() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -43,7 +44,5 @@ public sealed class HumanoidProfileTests Assert.That(voiceComponent.Sounds, Is.Not.Null, message: "the MobHuman spawned by this test needs to have sex-specific sound set"); Assert.That(voiceComponent.Sounds![Sex.Female], Is.EqualTo(voiceComponent.EmoteSounds)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs index 6ac40e92a1..e4bd4615c5 100644 --- a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs @@ -1,5 +1,6 @@ #nullable enable annotations using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Interaction; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; @@ -16,7 +17,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click { [TestFixture] [TestOf(typeof(InteractionSystem))] - public sealed class InteractionSystemTests + public sealed class InteractionSystemTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -40,7 +41,7 @@ namespace Content.IntegrationTests.Tests.Interaction.Click [Test] public async Task InteractionTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -101,13 +102,12 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } [Test] public async Task InteractionObstructionTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -168,13 +168,12 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } [Test] public async Task InteractionInRangeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -234,14 +233,13 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } [Test] public async Task InteractionOutOfRangeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -300,13 +298,12 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } [Test] public async Task InsideContainerInteractionBlockTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -388,7 +385,6 @@ namespace Content.IntegrationTests.Tests.Interaction.Click }); testInteractionSystem.ClearHandlers(); - await pair.CleanReturnAsync(); } public sealed class TestInteractionSystem : EntitySystem diff --git a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs index 801433ae72..966f13675d 100644 --- a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs +++ b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Shared.Interaction; using Robust.Server.GameObjects; using Robust.Shared.Containers; @@ -10,7 +11,7 @@ namespace Content.IntegrationTests.Tests.Interaction { [TestFixture] [TestOf(typeof(SharedInteractionSystem))] - public sealed class InRangeUnobstructed + public sealed class InRangeUnobstructed : GameTest { private const string HumanId = "MobHuman"; @@ -27,7 +28,7 @@ namespace Content.IntegrationTests.Tests.Interaction [Test] public async Task EntityEntityTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -109,8 +110,6 @@ namespace Content.IntegrationTests.Tests.Interaction Assert.That(interactionSys.InRangeUnobstructed(mapCoordinates, origin, InteractionRangeDivided15Times3)); }); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 62a03b0abe..5231055c69 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -175,7 +175,7 @@ public abstract partial class InteractionTest [SetUp] public virtual async Task Setup() { - Pair = await PoolManager.GetServerClient(Settings); + Pair = await PoolManager.GetServerClient(Settings, new NUnitTestContextWrap(TestContext.CurrentContext, TestContext.Out)); // server dependencies SEntMan = Server.ResolveDependency(); diff --git a/Content.IntegrationTests/Tests/Internals/AutoInternalsTests.cs b/Content.IntegrationTests/Tests/Internals/AutoInternalsTests.cs index d153536873..d2f67e9830 100644 --- a/Content.IntegrationTests/Tests/Internals/AutoInternalsTests.cs +++ b/Content.IntegrationTests/Tests/Internals/AutoInternalsTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Systems; using Content.Server.Station.Systems; @@ -7,12 +8,12 @@ namespace Content.IntegrationTests.Tests.Internals; [TestFixture] [TestOf(typeof(InternalsSystem))] -public sealed class AutoInternalsTests +public sealed class AutoInternalsTests : GameTest { [Test] public async Task TestInternalsAutoActivateInSpaceForStationSpawn() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -31,14 +32,12 @@ public sealed class AutoInternalsTests server.EntMan.DeleteEntity(dummy); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestInternalsAutoActivateInSpaceForEntitySpawn() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -55,8 +54,6 @@ public sealed class AutoInternalsTests server.EntMan.DeleteEntity(dummy); }); - - await pair.CleanReturnAsync(); } [TestPrototypes] diff --git a/Content.IntegrationTests/Tests/InventoryHelpersTest.cs b/Content.IntegrationTests/Tests/InventoryHelpersTest.cs index 39761ad089..c841e23365 100644 --- a/Content.IntegrationTests/Tests/InventoryHelpersTest.cs +++ b/Content.IntegrationTests/Tests/InventoryHelpersTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Stunnable; using Content.Shared.Inventory; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Map; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class InventoryHelpersTest + public sealed class InventoryHelpersTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -39,7 +40,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task SpawnItemInSlotTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntities = server.ResolveDependency(); @@ -87,8 +88,6 @@ namespace Content.IntegrationTests.Tests #pragma warning restore NUnit2045 sEntities.DeleteEntity(human); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Lathe/LatheTest.cs b/Content.IntegrationTests/Tests/Lathe/LatheTest.cs index c335f8d6c8..f0a5c04237 100644 --- a/Content.IntegrationTests/Tests/Lathe/LatheTest.cs +++ b/Content.IntegrationTests/Tests/Lathe/LatheTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Lathe; using Content.Shared.Materials; using Content.Shared.Prototypes; @@ -11,12 +12,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Lathe; [TestFixture] -public sealed class LatheTest +public sealed class LatheTest : GameTest { [Test] public async Task TestLatheRecipeIngredientsFitLathe() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapData = await pair.CreateTestMap(); @@ -111,14 +112,12 @@ public sealed class LatheTest } }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task AllLatheRecipesValidTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var proto = server.ProtoMan; @@ -131,7 +130,5 @@ public sealed class LatheTest Assert.That(recipe.ResultReagents, Is.Not.Null, $"Recipe '{recipe.ID}' has no result or result reagents."); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs index b75b81ab3c..bd35c1f08e 100644 --- a/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs +++ b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Tag; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -11,12 +12,12 @@ namespace Content.IntegrationTests.Tests.Linter; /// Verify that the yaml linter successfully validates static fields /// [TestFixture] -public sealed class StaticFieldValidationTest +public sealed class StaticFieldValidationTest : GameTest { [Test] public async Task TestStaticFieldValidation() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoMan = pair.Server.ProtoMan; var protos = new Dictionary>(); @@ -49,8 +50,6 @@ public sealed class StaticFieldValidationTest Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos), Has.Count.EqualTo(2)); Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetInvalid), protos), Has.Count.EqualTo(2)); Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayInvalid), protos), Has.Count.EqualTo(2)); - - await pair.CleanReturnAsync(); } [TestPrototypes] diff --git a/Content.IntegrationTests/Tests/Lobby/CharacterCreationTest.cs b/Content.IntegrationTests/Tests/Lobby/CharacterCreationTest.cs index 2fa3c9961c..fb48797616 100644 --- a/Content.IntegrationTests/Tests/Lobby/CharacterCreationTest.cs +++ b/Content.IntegrationTests/Tests/Lobby/CharacterCreationTest.cs @@ -1,4 +1,5 @@ using Content.Client.Lobby; +using Content.IntegrationTests.Fixtures; using Content.Server.Preferences.Managers; using Content.Shared.Humanoid; using Content.Shared.Preferences; @@ -9,12 +10,14 @@ namespace Content.IntegrationTests.Tests.Lobby; [TestFixture] [TestOf(typeof(ClientPreferencesManager))] [TestOf(typeof(ServerPreferencesManager))] -public sealed class CharacterCreationTest +public sealed class CharacterCreationTest : GameTest { + public override PoolSettings PoolSettings => new() { InLobby = true }; + [Test] public async Task CreateDeleteCreateTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { InLobby = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; var user = pair.Client.User!.Value; @@ -72,7 +75,6 @@ public sealed class CharacterCreationTest serverCharacters = serverPrefManager.GetPreferences(user).Characters; Assert.That(serverCharacters, Has.Count.EqualTo(2)); AssertEqual(serverCharacters[1], profile); - await pair.CleanReturnAsync(); } private void AssertEqual(HumanoidCharacterProfile a, HumanoidCharacterProfile b) diff --git a/Content.IntegrationTests/Tests/Lobby/ServerReloginTest.cs b/Content.IntegrationTests/Tests/Lobby/ServerReloginTest.cs index 3dc2075887..e59bc269ad 100644 --- a/Content.IntegrationTests/Tests/Lobby/ServerReloginTest.cs +++ b/Content.IntegrationTests/Tests/Lobby/ServerReloginTest.cs @@ -1,20 +1,23 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.CCVar; using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Network; namespace Content.IntegrationTests.Tests.Lobby; -public sealed class ServerReloginTest +public sealed class ServerReloginTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + Connected = true, + DummyTicker = false + }; + [Test] public async Task Relogin() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; var originalMaxPlayers = 0; @@ -62,7 +65,5 @@ public sealed class ServerReloginTest //Put the cvar back, so other tests can still use this server serverConfig.SetCVar(CCVars.SoftMaxPlayers, originalMaxPlayers); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Localization/EntityPrototypeLocalizationTest.cs b/Content.IntegrationTests/Tests/Localization/EntityPrototypeLocalizationTest.cs index 69d44fd08b..1b8fa16543 100644 --- a/Content.IntegrationTests/Tests/Localization/EntityPrototypeLocalizationTest.cs +++ b/Content.IntegrationTests/Tests/Localization/EntityPrototypeLocalizationTest.cs @@ -1,9 +1,10 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.Localization; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Localization; -public sealed class EntityPrototypeLocalizationTest +public sealed class EntityPrototypeLocalizationTest : GameTest { /// /// An explanation of why LocIds should not be used for entity prototype names/descriptions. @@ -18,7 +19,7 @@ public sealed class EntityPrototypeLocalizationTest [Test] public async Task TestNoManualEntityLocStrings() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; var locMan = server.ResolveDependency(); @@ -44,7 +45,5 @@ public sealed class EntityPrototypeLocalizationTest } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs b/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs index 05f98c3d19..14aeaf648c 100644 --- a/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Localization/LocalizedDatasetPrototypeTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Dataset; using Robust.Shared.Localization; using Robust.Shared.Prototypes; @@ -6,12 +7,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Localization; [TestFixture] -public sealed class LocalizedDatasetPrototypeTest +public sealed class LocalizedDatasetPrototypeTest : GameTest { [Test] public async Task ValidProtoIdsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -36,7 +37,5 @@ public sealed class LocalizedDatasetPrototypeTest Assert.That(localizationMan.HasString(nextId), Is.False, $"LocalizedDataset {proto.ID} with prefix \"{proto.Values.Prefix}\" specifies {proto.Values.Count} entries, but a localized string exists with ID {nextId}! Does count need to be raised?"); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/MachineBoardTest.cs b/Content.IntegrationTests/Tests/MachineBoardTest.cs index e1533bbb8d..9554357ece 100644 --- a/Content.IntegrationTests/Tests/MachineBoardTest.cs +++ b/Content.IntegrationTests/Tests/MachineBoardTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Construction.Components; using Content.Shared.Construction.Components; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; -public sealed class MachineBoardTest +public sealed class MachineBoardTest : GameTest { /// /// A list of machine boards that can be ignored by this test. @@ -32,7 +33,7 @@ public sealed class MachineBoardTest [Test] public async Task TestMachineBoardHasValidMachine() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -60,8 +61,6 @@ public sealed class MachineBoardTest }); } }); - - await pair.CleanReturnAsync(); } /// @@ -71,7 +70,7 @@ public sealed class MachineBoardTest [Test] public async Task TestComputerBoardHasValidComputer() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -100,8 +99,6 @@ public sealed class MachineBoardTest }); } }); - - await pair.CleanReturnAsync(); } /// @@ -111,7 +108,7 @@ public sealed class MachineBoardTest [Test] public async Task TestValidateBoardComponentRequirements() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -136,7 +133,5 @@ public sealed class MachineBoardTest }); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs b/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs index 6d48a668a5..43fea6d6c0 100644 --- a/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs +++ b/Content.IntegrationTests/Tests/MagazineVisualsSpriteTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Content.Client.Weapons.Ranged.Components; +using Content.IntegrationTests.Fixtures; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; @@ -9,12 +10,12 @@ namespace Content.IntegrationTests.Tests; /// Tests all entity prototypes with the MagazineVisualsComponent. /// [TestFixture] -public sealed class MagazineVisualsSpriteTest +public sealed class MagazineVisualsSpriteTest : GameTest { [Test] public async Task MagazineVisualsSpritesExist() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var client = pair.Client; var toTest = new List<(int, string)>(); var protos = pair.GetPrototypesWithComponent(); @@ -67,7 +68,5 @@ public sealed class MagazineVisualsSpriteTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs index be8bad229b..0bf637cf19 100644 --- a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs +++ b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -5,15 +6,18 @@ using Robust.Shared.Map; namespace Content.IntegrationTests.Tests.Mapping; [TestFixture] -public sealed class MappingTests +public sealed class MappingTests : GameTest { + public override PoolSettings PoolSettings => + new() { Dirty = true, Connected = true, DummyTicker = false }; + /// /// Checks that the mapping command creates paused & uninitialized maps. /// [Test] public async Task MappingTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Dirty = true, Connected = true, DummyTicker = false }); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -97,6 +101,5 @@ public sealed class MappingTests Assert.That(server.MetaData(ent).EntityPaused, Is.True); await server.WaitPost(() => entMan.DeleteEntity(map)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/MappingEditorTest.cs b/Content.IntegrationTests/Tests/MappingEditorTest.cs index bd930aef9e..987fe7ad73 100644 --- a/Content.IntegrationTests/Tests/MappingEditorTest.cs +++ b/Content.IntegrationTests/Tests/MappingEditorTest.cs @@ -1,19 +1,17 @@ using Content.Client.Gameplay; using Content.Client.Mapping; +using Content.IntegrationTests.Fixtures; using Robust.Client.State; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class MappingEditorTest +public sealed class MappingEditorTest : GameTest { [Test] public async Task StopHardCodingWidgetsJesusChristTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true - }); + var pair = Pair; var client = pair.Client; var state = client.ResolveDependency(); @@ -35,7 +33,5 @@ public sealed class MappingEditorTest state.RequestStateChange(); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs b/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs index c81ff6a698..af5c6c2018 100644 --- a/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs +++ b/Content.IntegrationTests/Tests/Markings/MarkingManagerTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Shared.Body; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Markings; @@ -9,7 +10,7 @@ namespace Content.IntegrationTests.Tests.Markings; [TestFixture] [TestOf(typeof(MarkingManager))] -public sealed class MarkingManagerTests +public sealed class MarkingManagerTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -76,7 +77,7 @@ public sealed class MarkingManagerTests [Test] public async Task HairConvesion() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -96,14 +97,12 @@ public sealed class MarkingManagerTests Assert.That(hairMarkings[0].MarkingId, Is.EqualTo("HumanHairLongBedhead2")); Assert.That(hairMarkings[0].MarkingColors[0], Is.EqualTo(Color.Red)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task LimitsFilling() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -118,14 +117,12 @@ public sealed class MarkingManagerTests Assert.That(dict[HumanoidVisualLayers.Eyes], Has.Count.EqualTo(1)); Assert.That(dict[HumanoidVisualLayers.Eyes][0].MarkingId, Is.EqualTo("EyesMarking")); }); - - await pair.CleanReturnAsync(); } [Test] public async Task LimitsTruncations() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -146,14 +143,12 @@ public sealed class MarkingManagerTests Assert.That(dict[HumanoidVisualLayers.Eyes], Has.Count.EqualTo(1)); Assert.That(dict[HumanoidVisualLayers.Eyes][0].MarkingId, Is.EqualTo("MenOnlyMarking")); }); - - await pair.CleanReturnAsync(); } [Test] public async Task EnsureValidGroupAndSex() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -191,14 +186,12 @@ public sealed class MarkingManagerTests Assert.That(testingMenMarkings[HumanoidVisualLayers.Eyes][1].MarkingId, Is.EqualTo("TestingOnlyMarking")); Assert.That(testingMenMarkings[HumanoidVisualLayers.Eyes][2].MarkingId, Is.EqualTo("TestingMenOnlyMarking")); }); - - await pair.CleanReturnAsync(); } [Test] public async Task EnsureValidColors() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -232,7 +225,5 @@ public sealed class MarkingManagerTests Assert.That(eyeMarkings[1].MarkingColors[0], Is.EqualTo(Color.Red)); Assert.That(eyeMarkings[3].MarkingColors[0], Is.EqualTo(Color.Green)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Markings/MarkingsViewModelTests.cs b/Content.IntegrationTests/Tests/Markings/MarkingsViewModelTests.cs index f69d3356c2..026d2ecf3b 100644 --- a/Content.IntegrationTests/Tests/Markings/MarkingsViewModelTests.cs +++ b/Content.IntegrationTests/Tests/Markings/MarkingsViewModelTests.cs @@ -59,7 +59,7 @@ public sealed class MarkingsViewModelTests [SetUp] public async Task SetUp() { - Pair = await PoolManager.GetServerClient(); + Pair = await PoolManager.GetServerClient(testContext: new NUnitTestContextWrap(TestContext.CurrentContext, TestContext.Out)); await Client.WaitPost(() => { Model = new MarkingsViewModel(); diff --git a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs index 3c6c372b75..460146357c 100644 --- a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs +++ b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.Cargo.Systems; using Content.Server.Construction.Completions; using Content.Server.Construction.Components; @@ -26,7 +27,7 @@ namespace Content.IntegrationTests.Tests; /// create them. /// [TestFixture] -public sealed class MaterialArbitrageTest +public sealed class MaterialArbitrageTest : GameTest { // These sets are for selectively excluding recipes from arbitrage. // You should NOT be adding to these. They exist here for downstreams and potential future issues. @@ -36,7 +37,7 @@ public sealed class MaterialArbitrageTest [Test] public async Task NoMaterialArbitrage() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -439,7 +440,6 @@ public sealed class MaterialArbitrageTest }); await server.WaitPost(() => mapSystem.DeleteMap(testMap.MapId)); - await pair.CleanReturnAsync(); async Task GetSpawnedPrice(Dictionary ents) { diff --git a/Content.IntegrationTests/Tests/Materials/MaterialTests.cs b/Content.IntegrationTests/Tests/Materials/MaterialTests.cs index a177869e7f..43c7dab2b0 100644 --- a/Content.IntegrationTests/Tests/Materials/MaterialTests.cs +++ b/Content.IntegrationTests/Tests/Materials/MaterialTests.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.Stack; using Content.Shared.Stacks; using Content.Shared.Materials; @@ -14,12 +15,12 @@ namespace Content.IntegrationTests.Tests.Materials [TestFixture] [TestOf(typeof(StackSystem))] [TestOf(typeof(MaterialPrototype))] - public sealed class MaterialPrototypeSpawnsStackMaterialTest + public sealed class MaterialPrototypeSpawnsStackMaterialTest : GameTest { [Test] public async Task MaterialPrototypeSpawnsStackMaterial() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -60,8 +61,6 @@ namespace Content.IntegrationTests.Tests.Materials mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs index 32fcf9c1ad..7369fece35 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; using Content.Shared.Ghost; @@ -12,7 +13,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed class GhostRoleTests +public sealed class GhostRoleTests : GameTest { private const string GhostRoleProtoId = "GhostRoleTestEntity"; private const string TestMobProtoId = "GhostRoleTestMob"; @@ -33,6 +34,13 @@ public sealed class GhostRoleTests - type: MobState # MobState is required for correct determination of if the player can return to body or not """; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + DummyTicker = false, + Connected = true + }; + /// /// This is a simple test that just checks if a player can take a ghost role and then regain control of their /// original entity without encountering errors. @@ -43,12 +51,7 @@ public sealed class GhostRoleTests { var ghostCommand = adminGhost ? "aghost" : "ghost"; - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true - }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -210,7 +213,6 @@ public sealed class GhostRoleTests if (!adminGhost) { // End of the normal player ghost role test - await pair.CleanReturnAsync(); return; } @@ -242,7 +244,5 @@ public sealed class GhostRoleTests // Check that there is are no lingereing ghosts Assert.That(entMan.Count(), Is.Zero); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/GhostTests.cs b/Content.IntegrationTests/Tests/Minds/GhostTests.cs index 3a860267e5..26991c08ad 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostTests.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.IntegrationTests.Pair; using Content.Shared.Ghost; using Content.Shared.Mind; @@ -12,7 +13,7 @@ using Robust.UnitTesting; namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed class GhostTests +public sealed class GhostTests : GameTest { private struct GhostTestData { @@ -45,17 +46,20 @@ public sealed class GhostTests } } + // Client is needed to create a session for the ghost system. Creating a dummy session was too difficult. + public override PoolSettings PoolSettings => new() + { + DummyTicker = false, + Connected = true, + Dirty = true + }; + private async Task SetupData() { var data = new GhostTestData { - // Client is needed to create a session for the ghost system. Creating a dummy session was too difficult. - Pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }) + // ..Just use the gametest pair, please. + Pair = Pair, }; data.SEntMan = data.Pair.Server.ResolveDependency(); @@ -126,8 +130,6 @@ public sealed class GhostTests // Ensure the position is the same var ghostPosition = data.SEntMan.GetComponent(ghost).Coordinates; Assert.That(ghostPosition, Is.EqualTo(oldPosition)); - - await data.Pair.CleanReturnAsync(); } /// @@ -154,8 +156,6 @@ public sealed class GhostTests // Ensure the position is the same var ghostPosition = data.SEntMan.GetComponent(ghost).Coordinates; Assert.That(ghostPosition, Is.EqualTo(oldPosition)); - - await data.Pair.CleanReturnAsync(); } [Test] @@ -168,10 +168,6 @@ public sealed class GhostTests // Delete the grid await data.Server.WaitPost(() => data.SEntMan.DeleteEntity(data.MapData.Grid.Owner)); }); - - await data.Pair.RunTicksSync(5); - - await data.Pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs b/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs index ad4ddc2612..c207e7b489 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTest.DeleteAllThenGhost.cs @@ -11,13 +11,7 @@ public sealed partial class MindTests [Test] public async Task DeleteAllThenGhost() { - var settings = new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true - }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; // Client is connected with a valid entity & mind Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity)); @@ -56,7 +50,5 @@ public sealed partial class MindTests Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind)); var xform = pair.Client.Transform(pair.Client.AttachedEntity!.Value); Assert.That(xform.MapID, Is.EqualTo(mapId)); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs index 513049bcad..cf87b3ea76 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs @@ -23,7 +23,7 @@ public sealed partial class MindTests [Test] public async Task TestDeleteVisiting() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var server = pair.Server; var entMan = server.ResolveDependency(); @@ -67,15 +67,13 @@ public sealed partial class MindTests // This used to throw so make sure it doesn't. await server.WaitPost(() => entMan.DeleteEntity(mind.OwnedEntity!.Value)); await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } // this is a variant of TestGhostOnDelete that just deletes the whole map. [Test] public async Task TestGhostOnDeleteMap() { - await using var pair = await SetupPair(dirty: true); + var pair = await SetupPair(dirty: true); var server = pair.Server; var testMap = await pair.CreateTestMap(); var testMap2 = await pair.CreateTestMap(); @@ -117,8 +115,6 @@ public sealed partial class MindTests Assert.That(transform.MapID, Is.Not.EqualTo(testMap.MapId)); #pragma warning restore NUnit2045 }); - - await pair.CleanReturnAsync(); } /// @@ -130,7 +126,7 @@ public sealed partial class MindTests public async Task TestGhostOnDelete() { // Client is needed to spawn session - await using var pair = await SetupPair(dirty: true); + var pair = await SetupPair(dirty: true); var server = pair.Server; var entMan = server.ResolveDependency(); @@ -145,8 +141,6 @@ public sealed partial class MindTests await pair.RunTicksSync(5); Assert.That(entMan.HasComponent(player.AttachedEntity), "Player did not become a ghost"); - - await pair.CleanReturnAsync(); } /// @@ -162,7 +156,7 @@ public sealed partial class MindTests public async Task TestOriginalDeletedWhileGhostingKeepsGhost() { // Client is needed to spawn session - await using var pair = await SetupPair(); + var pair = await SetupPair(); var server = pair.Server; var entMan = server.ResolveDependency(); @@ -207,8 +201,6 @@ public sealed partial class MindTests Assert.That(mind.Comp.VisitingEntity, Is.Null); Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(ghost)); }); - - await pair.CleanReturnAsync(); } /// @@ -220,7 +212,7 @@ public sealed partial class MindTests [Test] public async Task TestGhostToAghost() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var server = pair.Server; var entMan = server.ResolveDependency(); var playerMan = server.ResolveDependency(); @@ -250,8 +242,6 @@ public sealed partial class MindTests var mind = entMan.GetComponent(mindId!.Value); Assert.That(mind.VisitingEntity, Is.Null); - - await pair.CleanReturnAsync(); } /// @@ -264,7 +254,7 @@ public sealed partial class MindTests public async Task TestGhostDeletedSpawnsNewGhost() { // Client is needed to spawn session - await using var pair = await SetupPair(); + var pair = await SetupPair(); var server = pair.Server; var entMan = server.ResolveDependency(); @@ -308,7 +298,5 @@ public sealed partial class MindTests Assert.That(entMan.HasComponent(player.AttachedEntity!.Value)); #pragma warning restore NUnit2045 }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs index cebbed8ee8..e22f6c1e4d 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs @@ -18,6 +18,14 @@ namespace Content.IntegrationTests.Tests.Minds; // This partial class contains misc helper functions for other tests. public sealed partial class MindTests { + // TODO GAMETEST: Rewrite this test to use improved GameTest pair management when I have an API for that figured out. + public override PoolSettings PoolSettings => new() + { + DummyTicker = false, + Connected = true, + Dirty = true, + }; + /// /// Gets a server-client pair and ensures that the client is attached to a simple mind test entity. /// @@ -26,15 +34,9 @@ public sealed partial class MindTests /// the player's mind's current entity, likely because some previous test directly changed the players attached /// entity. /// - private static async Task SetupPair(bool dirty = false) + private async Task SetupPair(bool dirty = false) { - var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = dirty - }); - + var pair = Pair; var entMan = pair.Server.ResolveDependency(); var playerMan = pair.Server.ResolveDependency(); var mindSys = entMan.System(); diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs b/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs index db87797553..fbe79fe044 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs @@ -17,7 +17,7 @@ public sealed partial class MindTests [Test] public async Task TestGhostsCanReconnect() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var entMan = pair.Server.ResolveDependency(); var mind = GetMind(pair); @@ -32,8 +32,6 @@ public sealed partial class MindTests Assert.That(entMan.HasComponent(mind.Comp.OwnedEntity)); Assert.That(mind.Comp.VisitingEntity, Is.Null); }); - - await pair.CleanReturnAsync(); } // This test will do the following: @@ -44,7 +42,7 @@ public sealed partial class MindTests [Test] public async Task TestDeletedCanReconnect() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var entMan = pair.Server.ResolveDependency(); var mind = GetMind(pair); @@ -82,8 +80,6 @@ public sealed partial class MindTests Assert.That(mind.Comp.OwnedEntity, Is.Not.EqualTo(entity)); Assert.That(entMan.HasComponent(mind.Comp.OwnedEntity)); }); - - await pair.CleanReturnAsync(); } // This test will do the following: @@ -94,7 +90,7 @@ public sealed partial class MindTests [Test] public async Task TestVisitingGhostReconnect() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var entMan = pair.Server.ResolveDependency(); var mind = GetMind(pair); @@ -110,8 +106,6 @@ public sealed partial class MindTests Assert.That(entMan.Deleted(original), Is.False); Assert.That(entMan.Deleted(ghost)); }); - - await pair.CleanReturnAsync(); } // This test will do the following: @@ -122,7 +116,7 @@ public sealed partial class MindTests [Test] public async Task TestVisitingReconnect() { - await using var pair = await SetupPair(true); + var pair = await SetupPair(true); var entMan = pair.Server.ResolveDependency(); var mindSys = entMan.System(); var mind = GetMind(pair); @@ -150,8 +144,6 @@ public sealed partial class MindTests Assert.That(entMan.Deleted(visiting), Is.False); Assert.That(mind.Comp.CurrentEntity, Is.EqualTo(visiting)); }); - - await pair.CleanReturnAsync(); } // This test will do the following @@ -162,7 +154,7 @@ public sealed partial class MindTests [Test] public async Task TestReconnect() { - await using var pair = await SetupPair(); + var pair = await SetupPair(); var mind = GetMind(pair); Assert.That(mind.Comp.VisitingEntity, Is.Null); @@ -178,7 +170,5 @@ public sealed partial class MindTests Assert.That(newMind.Comp.VisitingEntity, Is.Null); Assert.That(newMind.Comp.OwnedEntity, Is.EqualTo(entity)); Assert.That(newMind.Id, Is.EqualTo(mind.Id)); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.cs b/Content.IntegrationTests/Tests/Minds/MindTests.cs index 35069339ba..08cfbaed5e 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; using Content.Server.Mind; @@ -23,7 +24,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed partial class MindTests +public sealed partial class MindTests : GameTest { private static readonly ProtoId BluntDamageType = "Blunt"; @@ -56,7 +57,7 @@ public sealed partial class MindTests [Test] public async Task TestCreateAndTransferMindToNewEntity() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -75,14 +76,12 @@ public sealed partial class MindTests mindSystem.TransferTo(mind, entity, mind: mind); Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind.Owner)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestReplaceMind() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -107,14 +106,12 @@ public sealed partial class MindTests Assert.That(mind.OwnedEntity, Is.Not.EqualTo(entity)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestEntityDeadWhenGibbed() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -160,14 +157,12 @@ public sealed partial class MindTests var mind = entMan.GetComponent(mindId); Assert.That(mindSystem.IsCharacterDeadPhysically(mind)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestMindTransfersToOtherEntity() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -194,18 +189,12 @@ public sealed partial class MindTests Assert.That(mindSystem.GetMind(targetEntity), Is.EqualTo(mind)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestOwningPlayerCanBeChanged() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Connected = true, - DummyTicker = false - }); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -253,14 +242,12 @@ public sealed partial class MindTests }); await pair.RunTicksSync(5); - - await pair.CleanReturnAsync(); } [Test] public async Task TestAddRemoveHasRoles() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -323,15 +310,13 @@ public sealed partial class MindTests Assert.That(roleSystem.MindHasRole(mindId), Is.False); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestPlayerCanGhost() { // Client is needed to spawn session - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false }); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -399,19 +384,12 @@ public sealed partial class MindTests Assert.That(mId, Is.Not.EqualTo(mindId)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestGhostDoesNotInfiniteLoop() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -482,7 +460,5 @@ public sealed partial class MindTests Assert.That(player.AttachedEntity, Is.Not.Null); Assert.That(player.AttachedEntity!.Value, Is.EqualTo(ghost)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Minds/RoleTests.cs b/Content.IntegrationTests/Tests/Minds/RoleTests.cs index f0a7268a3d..3204dc1c2f 100644 --- a/Content.IntegrationTests/Tests/Minds/RoleTests.cs +++ b/Content.IntegrationTests/Tests/Minds/RoleTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Roles.Components; using Robust.Shared.GameObjects; using Robust.Shared.Reflection; @@ -6,7 +7,7 @@ using Robust.Shared.Reflection; namespace Content.IntegrationTests.Tests.Minds; [TestFixture] -public sealed class RoleTests +public sealed class RoleTests : GameTest { /// /// Check that any prototype with a is properly configured @@ -14,7 +15,7 @@ public sealed class RoleTests [Test] public async Task ValidateRolePrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var jobComp = pair.Server.ResolveDependency().GetComponentName(); @@ -35,7 +36,6 @@ public sealed class RoleTests } }); - await pair.CleanReturnAsync(); } /// @@ -45,7 +45,7 @@ public sealed class RoleTests [Test] public async Task ValidateJobPrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var mindCompId = pair.Server.ResolveDependency().GetComponentName(); @@ -57,8 +57,6 @@ public sealed class RoleTests Assert.That(((MindRoleComponent)mindComp).JobPrototype, Is.Not.Null); } }); - - await pair.CleanReturnAsync(); } /// @@ -68,7 +66,7 @@ public sealed class RoleTests [Test] public async Task ValidateRolesHaveMindRoleComp() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var refMan = pair.Server.ResolveDependency(); var mindCompId = pair.Server.ResolveDependency().GetComponentName(); @@ -87,7 +85,5 @@ public sealed class RoleTests } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/NPC/NPCTest.cs b/Content.IntegrationTests/Tests/NPC/NPCTest.cs index 064fd6c5bf..1568f360e7 100644 --- a/Content.IntegrationTests/Tests/NPC/NPCTest.cs +++ b/Content.IntegrationTests/Tests/NPC/NPCTest.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.NPC.HTN; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -7,12 +8,12 @@ using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests.NPC; [TestFixture] -public sealed class NPCTest +public sealed class NPCTest : GameTest { [Test] public async Task CompoundRecursion() { - var pool = await PoolManager.GetServerClient(); + var pool = Pair; var server = pool.Server; await server.WaitIdleAsync(); @@ -30,8 +31,6 @@ public sealed class NPCTest counts.Clear(); } }); - - await pool.CleanReturnAsync(); } private static void Count(HTNCompoundPrototype compound, Dictionary counts, HTNSystem htnSystem, IPrototypeManager protoManager) diff --git a/Content.IntegrationTests/Tests/Networking/NetworkIdsMatchTest.cs b/Content.IntegrationTests/Tests/Networking/NetworkIdsMatchTest.cs index c72be944fd..7b4f048939 100644 --- a/Content.IntegrationTests/Tests/Networking/NetworkIdsMatchTest.cs +++ b/Content.IntegrationTests/Tests/Networking/NetworkIdsMatchTest.cs @@ -1,14 +1,15 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Networking { [TestFixture] - public sealed class NetworkIdsMatchTest + public sealed class NetworkIdsMatchTest : GameTest { [Test] public async Task TestConnect() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -38,7 +39,6 @@ namespace Content.IntegrationTests.Tests.Networking Assert.That(clientNetComps[netId].Name, Is.EqualTo(serverNetComps[netId].Name)); } }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs b/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs index b395569848..786e14ae42 100644 --- a/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs +++ b/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; @@ -5,16 +6,17 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Networking; [TestFixture] -public sealed class PvsCommandTest +public sealed class PvsCommandTest : GameTest { private static readonly EntProtoId TestEnt = "MobHuman"; + public override PoolSettings PoolSettings => new() { Connected = true, DummyTicker = false }; + [Test] public async Task TestPvsCommands() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false }); + var pair = Pair; var (server, client) = pair; - await pair.RunTicksSync(5); // Spawn a complex entity. EntityUid entity = default; @@ -46,6 +48,5 @@ public sealed class PvsCommandTest Assert.That(meta.LastStateApplied, Is.GreaterThan(lastApplied)); await server.WaitPost(() => server.EntMan.DeleteEntity(entity)); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Networking/ReconnectTest.cs b/Content.IntegrationTests/Tests/Networking/ReconnectTest.cs index 4539ca81a5..26e76376ce 100644 --- a/Content.IntegrationTests/Tests/Networking/ReconnectTest.cs +++ b/Content.IntegrationTests/Tests/Networking/ReconnectTest.cs @@ -1,15 +1,16 @@ +using Content.IntegrationTests.Fixtures; using Robust.Client.Console; using Robust.Shared.Network; namespace Content.IntegrationTests.Tests.Networking { [TestFixture] - public sealed class ReconnectTest + public sealed class ReconnectTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -32,7 +33,6 @@ namespace Content.IntegrationTests.Tests.Networking await pair.RunTicksSync(10); await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs index 3e49499845..15c7373b63 100644 --- a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs +++ b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.Generic; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Robust.Client.GameStates; using Robust.Client.Timing; using Robust.Shared; @@ -27,12 +28,12 @@ namespace Content.IntegrationTests.Tests.Networking // the tick where the server *should* have, but did not, acknowledge the state change. // Finally, we run two events inside the prediction area to ensure reconciling does for incremental stuff. [TestFixture] - public sealed class SimplePredictReconcileTest + public sealed class SimplePredictReconcileTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); + var pair = Pair; var server = pair.Server; var client = pair.Client; @@ -386,7 +387,6 @@ namespace Content.IntegrationTests.Tests.Networking } cfg.SetCVar(CVars.NetLogging, log); - await pair.CleanReturnAsync(); } public sealed class PredictionTestEntitySystem : EntitySystem diff --git a/Content.IntegrationTests/Tests/Physics/AnchorPrototypeTest.cs b/Content.IntegrationTests/Tests/Physics/AnchorPrototypeTest.cs index a65e7d1fd6..f65bd324a0 100644 --- a/Content.IntegrationTests/Tests/Physics/AnchorPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Physics/AnchorPrototypeTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.GameObjects; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; @@ -6,7 +7,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Physics; [TestFixture] -public sealed class AnchorPrototypeTest +public sealed class AnchorPrototypeTest : GameTest { /// /// Asserts that entityprototypes marked as anchored are also static physics bodies. @@ -14,7 +15,7 @@ public sealed class AnchorPrototypeTest [Test] public async Task TestStaticAnchorPrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoManager = pair.Server.ResolveDependency(); @@ -37,7 +38,5 @@ public sealed class AnchorPrototypeTest Assert.That(physics.BodyType, Is.EqualTo(BodyType.Static), $"Found entity prototype {ent} marked as anchored but not static for physics."); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs index 6147fb6e17..a02179efc6 100644 --- a/Content.IntegrationTests/Tests/PostMapInitTest.cs +++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Content.IntegrationTests.Utility; using YamlDotNet.RepresentationModel; using Content.Server.Administration.Systems; @@ -28,8 +30,14 @@ using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class PostMapInitTest + public sealed class PostMapInitTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings() + { + Connected = true, + Dirty = true, + }; + private const bool SkipTestMaps = true; private const string TestMapsPath = "/Maps/Test/"; @@ -93,16 +101,16 @@ namespace Content.IntegrationTests.Tests /// Asserts that specific files have been saved as grids and not maps. /// [Test, TestCaseSource(nameof(Grids))] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task GridsLoadableTest(string mapFile) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); var mapSystem = entManager.System(); var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); var path = new ResPath(mapFile); await server.WaitPost(() => @@ -119,9 +127,6 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(mapId); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -129,16 +134,16 @@ namespace Content.IntegrationTests.Tests /// [Test] [TestCaseSource(nameof(ShuttleMapFiles))] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task ShuttlesLoadableTest(ResPath path) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); var mapSystem = entManager.System(); var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { @@ -158,17 +163,13 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(mapId); }); }); - - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } [Test] [TestCaseSource(nameof(AllMapFiles))] public async Task NoSavedPostMapInitTest(ResPath map) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var resourceManager = server.ResolveDependency(); @@ -182,7 +183,6 @@ namespace Content.IntegrationTests.Tests // ReSharper disable once RedundantLogicalConditionalExpressionOperand if (SkipTestMaps && rootedPath.ToString().StartsWith(TestMapsPath, StringComparison.Ordinal)) { - await pair.CleanReturnAsync(); return; // We just pass immediately. } @@ -239,8 +239,6 @@ namespace Content.IntegrationTests.Tests await server.WaitPost(() => mapSys.InitializeMap(id)); Assert.That(loader.TrySaveMap(id, path)); Assert.That(IsPreInit(path, loader, deps, ev.RenamedPrototypes, ev.DeletedPrototypes), Is.False); - - await pair.CleanReturnAsync(); } private bool IsWhitelistedForMap(EntProtoId protoId, ResPath map) @@ -325,12 +323,10 @@ namespace Content.IntegrationTests.Tests } [Test, TestCaseSource(nameof(GameMaps))] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task GameMapsLoadableTest(string mapProto) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true // Stations spawn a bunch of nullspace entities and maps like centcomm. - }); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -341,7 +337,6 @@ namespace Content.IntegrationTests.Tests var ticker = entManager.EntitySysManager.GetEntitySystem(); var shuttleSystem = entManager.EntitySysManager.GetEntitySystem(); var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { @@ -440,9 +435,6 @@ namespace Content.IntegrationTests.Tests throw new Exception($"Failed to delete map {mapProto}", ex); } }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } @@ -471,37 +463,18 @@ namespace Content.IntegrationTests.Tests return resultCount; } - [Test] - public async Task AllMapsTested() - { - await using var pair = await PoolManager.GetServerClient(); - var server = pair.Server; - var protoMan = server.ResolveDependency(); - - var gameMaps = protoMan.EnumeratePrototypes() - .Where(x => !pair.IsTestPrototype(x)) - .Select(x => x.ID) - .ToHashSet(); - - Assert.That(gameMaps.Remove(PoolManager.TestMap)); - - Assert.That(gameMaps, Is.EquivalentTo(GameMaps.ToHashSet()), "Game map prototype missing from test cases."); - - await pair.CleanReturnAsync(); - } - [Test] [TestCaseSource(nameof(AllMapFiles))] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task NonGameMapsLoadableTest(ResPath mapPath) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapLoader = server.ResolveDependency().GetEntitySystem(); var resourceManager = server.ResolveDependency(); var protoManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); var gameMaps = protoManager.EnumeratePrototypes().Select(o => o.MapPath).ToHashSet(); @@ -510,7 +483,6 @@ namespace Content.IntegrationTests.Tests { // TODO: You might be able to save like, 1-2 seconds of test time if you eliminate these before // actually needing a pair. - await pair.CleanReturnAsync(); return; } @@ -518,7 +490,6 @@ namespace Content.IntegrationTests.Tests if (SkipTestMaps && rootedPath.ToString().StartsWith(TestMapsPath, StringComparison.Ordinal)) { - await pair.CleanReturnAsync(); return; } @@ -561,9 +532,6 @@ namespace Content.IntegrationTests.Tests } }); }); - - await server.WaitRunTicks(1); - await pair.CleanReturnAsync(); } /// diff --git a/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs b/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs index 288e976e9b..e991801330 100644 --- a/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerStatePrototypeTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Power.Components; using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; @@ -8,7 +9,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Power; [TestFixture, TestOf(typeof(SharedPowerStateSystem))] -public sealed class PowerStatePrototypeTest +public sealed class PowerStatePrototypeTest : GameTest { /// /// Asserts that the 's load is the same @@ -18,7 +19,7 @@ public sealed class PowerStatePrototypeTest [Test] public async Task AssertApcPowerMatchesPowerState() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ResolveDependency(); @@ -53,7 +54,5 @@ public sealed class PowerStatePrototypeTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Power/PowerStateTest.cs b/Content.IntegrationTests/Tests/Power/PowerStateTest.cs index edec6f3d21..a265de532f 100644 --- a/Content.IntegrationTests/Tests/Power/PowerStateTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerStateTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Coordinates; using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; @@ -8,7 +9,7 @@ using Robust.Shared.Maths; namespace Content.IntegrationTests.Tests.Power; [TestFixture] -public sealed class PowerStateTest +public sealed class PowerStateTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -31,7 +32,7 @@ public sealed class PowerStateTest [Test] public async Task SetWorkingState_IdleToWorking_UpdatesLoad() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -65,8 +66,6 @@ public sealed class PowerStateTest Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -75,7 +74,7 @@ public sealed class PowerStateTest [Test] public async Task SetWorkingState_WorkingToIdle_UpdatesLoad() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -118,8 +117,6 @@ public sealed class PowerStateTest Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -128,7 +125,7 @@ public sealed class PowerStateTest [Test] public async Task SetWorkingState_AlreadyInState_NoChange() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -179,8 +176,6 @@ public sealed class PowerStateTest Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Power/PowerTest.cs b/Content.IntegrationTests/Tests/Power/PowerTest.cs index a28f646ef8..db6c0b7c89 100644 --- a/Content.IntegrationTests/Tests/Power/PowerTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Server.NodeContainer.EntitySystems; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -14,7 +15,7 @@ using Robust.Shared.Timing; namespace Content.IntegrationTests.Tests.Power { [TestFixture] - public sealed class PowerTest + public sealed class PowerTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -167,7 +168,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestSimpleSurplus() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -218,8 +219,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(loadPower * 2).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } @@ -229,7 +228,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestSimpleDeficit() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -280,14 +279,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestSupplyRamp() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -368,14 +365,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(consumer.ReceivedPower, Is.EqualTo(400).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestBatteryRamp() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -472,8 +467,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(currentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] @@ -481,7 +474,7 @@ namespace Content.IntegrationTests.Tests.Power { // checks that batteries and supplies properly ramp down if the load is disconnected/disabled. - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -571,14 +564,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(consumer.ReceivedPower, Is.EqualTo(0).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestSimpleBatteryChargeDeficit() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var gameTiming = server.ResolveDependency(); @@ -631,14 +622,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(500).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestFullBattery() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -713,14 +702,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(currentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestFullBatteryEfficiencyPassThrough() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -795,14 +782,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(currentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestFullBatteryEfficiencyDemandPassThrough() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -888,8 +873,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -899,7 +882,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestSupplyPrioritized() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -988,8 +971,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(netBattery2.SupplyRampPosition, Is.EqualTo(500).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -998,7 +979,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestBatteriesProportional() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1079,14 +1060,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(supplier.CurrentSupply, Is.EqualTo(supplier.MaxSupply).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestBatteryEngineCut() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1161,8 +1140,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(netBattery.CurrentSupply, Is.GreaterThan(0)); }); }); - - await pair.CleanReturnAsync(); } /// @@ -1171,7 +1148,7 @@ namespace Content.IntegrationTests.Tests.Power [Test] public async Task TestTerminalNodeGroups() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1230,14 +1207,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(leftNode.NodeGroup, Is.Not.EqualTo(rightNode.NodeGroup)); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task ApcChargingTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1290,14 +1265,12 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(currentCharge, Is.GreaterThan(0)); //apc battery should have gained charge }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task ApcNetTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); @@ -1355,8 +1328,6 @@ namespace Content.IntegrationTests.Tests.Power Assert.That(apcNetBattery.CurrentSupply, Is.EqualTo(1).Within(0.1)); }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Power/StationPowerTests.cs b/Content.IntegrationTests/Tests/Power/StationPowerTests.cs index 9d79abf480..763cec328e 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 @@ using Robust.Shared.EntitySerialization; namespace Content.IntegrationTests.Tests.Power; -public sealed class StationPowerTests +public sealed class StationPowerTests : GameTest { /// /// How long the station should be able to survive on stored power if nothing is changed from round start. @@ -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 @@ public sealed class StationPowerTests Assert.That(totalStartingCharge, Is.GreaterThanOrEqualTo(requiredStoredPower), $"Needs at least {requiredStoredPower - totalStartingCharge} more stored power!"); }); - - await pair.CleanReturnAsync(); } [Test, TestCaseSource(nameof(GameMaps))] public async Task TestApcLoad(string mapProtoId) { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - }); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; @@ -145,7 +143,5 @@ public sealed class StationPowerTests } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs b/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs index 267b3637e0..62c9f1a460 100644 --- a/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs +++ b/Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.Station.Systems; using Content.Shared.Inventory; using Content.Shared.Preferences; @@ -9,7 +10,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Preferences; [TestFixture] -public sealed class LoadoutTests +public sealed class LoadoutTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -42,16 +43,18 @@ public sealed class LoadoutTests ["jumpsuit"] = "ClothingUniformJumpsuitColorGrey" }; + public override PoolSettings PoolSettings => new() + { + Dirty = true, + }; + /// /// Checks that an empty loadout still spawns with default gear and not naked. /// [Test] public async Task TestEmptyLoadout() { - var pair = await PoolManager.GetServerClient(new PoolSettings() - { - Dirty = true, - }); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -87,7 +90,5 @@ public sealed class LoadoutTests entManager.DeleteEntity(tester); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs index 6208804e3a..ad93e083fb 100644 --- a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs +++ b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading; +using Content.IntegrationTests.Fixtures; using Content.Server.Database; using Content.Server.Preferences.Managers; using Content.Shared.Body; @@ -20,7 +21,7 @@ using Robust.UnitTesting; namespace Content.IntegrationTests.Tests.Preferences { [TestFixture] - public sealed class ServerDbSqliteTests + public sealed class ServerDbSqliteTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -69,12 +70,10 @@ namespace Content.IntegrationTests.Tests.Preferences [Test] public async Task TestUserDoesNotExist() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var db = GetDb(pair.Server); // Database should be empty so a new GUID should do it. Assert.That(await db.GetPlayerPreferencesAsync(NewUserId()), Is.Null); - - await pair.CleanReturnAsync(); } [Test] @@ -113,7 +112,7 @@ namespace Content.IntegrationTests.Tests.Preferences [Test] public async Task TestInitPrefs() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var db = GetDb(pair.Server); var preferences = (ServerPreferencesManager)pair.Server.ResolveDependency(); var username = new NetUserId(new Guid("640bd619-fc8d-4fe2-bf3c-4a5fb17d6ddd")); @@ -123,13 +122,12 @@ namespace Content.IntegrationTests.Tests.Preferences var prefs = await db.GetPlayerPreferencesAsync(username); var profile = preferences.ConvertProfiles(prefs!.Profiles.Find(p => p.Slot == slot)); Assert.That(profile.MemberwiseEquals(originalProfile)); - await pair.CleanReturnAsync(); } [Test] public async Task TestDeleteCharacter() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var db = GetDb(server); var username = new NetUserId(new Guid("640bd619-fc8d-4fe2-bf3c-4a5fb17d6ddd")); @@ -139,18 +137,16 @@ namespace Content.IntegrationTests.Tests.Preferences await db.SaveCharacterSlotAsync(username, null, 1); var prefs = await db.GetPlayerPreferencesAsync(username); Assert.That(prefs!.Profiles, Has.Count.EqualTo(1)); - await pair.CleanReturnAsync(); } [Test] public async Task TestNoPendingDatabaseChanges() { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var db = GetDb(server); Assert.That(async () => await db.HasPendingModelChanges(), Is.False, "The database has pending model changes. Add a new migration to apply them. See https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations"); - await pair.CleanReturnAsync(); } private static NetUserId NewUserId() @@ -166,7 +162,7 @@ namespace Content.IntegrationTests.Tests.Preferences [TestCaseSource(nameof(_trueFalse))] public async Task InvalidSpeciesConversion(bool legacy) { - var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var db = GetDb(pair.Server); var preferences = (ServerPreferencesManager)pair.Server.ResolveDependency(); @@ -198,8 +194,6 @@ namespace Content.IntegrationTests.Tests.Preferences Assert.That(converted.Characters[0].Species, Is.Not.EqualTo(InvalidSpecies)); Assert.That(converted.Characters[0].Species, Is.EqualTo(HumanoidCharacterProfile.DefaultSpecies)); }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Procedural/DungeonTests.cs b/Content.IntegrationTests/Tests/Procedural/DungeonTests.cs index 6bede9660a..d3afcc7253 100644 --- a/Content.IntegrationTests/Tests/Procedural/DungeonTests.cs +++ b/Content.IntegrationTests/Tests/Procedural/DungeonTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.Procedural; using Content.Shared.Procedural; using Robust.Shared.Maths; @@ -7,12 +8,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Procedural; [TestOf(typeof(DungeonSystem))] -public sealed class DungeonTests +public sealed class DungeonTests : GameTest { [Test] public async Task TestDungeonRoomPackBounds() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoManager = pair.Server.ResolveDependency(); await pair.Server.WaitAssertion(() => @@ -55,14 +56,12 @@ public sealed class DungeonTests } } }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestDungeonPresets() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoManager = pair.Server.ResolveDependency(); await pair.Server.WaitAssertion(() => @@ -92,7 +91,5 @@ public sealed class DungeonTests } } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index 9ff8ca2900..5977e7deee 100644 --- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Coordinates; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -27,12 +28,12 @@ namespace Content.IntegrationTests.Tests; /// spawn it into a new empty map and seeing what the map yml looks like. /// [TestFixture] -public sealed class PrototypeSaveTest +public sealed class PrototypeSaveTest : GameTest { [Test] public async Task UninitializedSaveTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entityMan = server.ResolveDependency(); @@ -156,7 +157,6 @@ public sealed class PrototypeSaveTest } }); }); - await pair.CleanReturnAsync(); } public sealed class TestEntityUidContext : ISerializationContext, diff --git a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs index 440d9e636e..3b4bed882d 100644 --- a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs +++ b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeTests.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Markdown; @@ -8,7 +9,7 @@ using Robust.UnitTesting; namespace Content.IntegrationTests.Tests.PrototypeTests; -public sealed class PrototypeTests +public sealed class PrototypeTests : GameTest { /// /// This test writes all known server prototypes as yaml files, then validates that the result is valid yaml. @@ -17,10 +18,9 @@ public sealed class PrototypeTests [Test] public async Task TestAllServerPrototypesAreSerializable() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var context = new PrototypeSaveTest.TestEntityUidContext(); await SaveThenValidatePrototype(pair.Server, "server", context); - await pair.CleanReturnAsync(); } /// @@ -30,10 +30,9 @@ public sealed class PrototypeTests [Test] public async Task TestAllClientPrototypesAreSerializable() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var context = new PrototypeSaveTest.TestEntityUidContext(); await SaveThenValidatePrototype(pair.Client, "client", context); - await pair.CleanReturnAsync(); } public async Task SaveThenValidatePrototype(RobustIntegrationTest.IntegrationInstance instance, string instanceId, @@ -69,10 +68,9 @@ public sealed class PrototypeTests [Test] public async Task ServerPrototypeSaveLoadSaveTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var context = new PrototypeSaveTest.TestEntityUidContext(); await SaveLoadSavePrototype(pair.Server, context); - await pair.CleanReturnAsync(); } /// @@ -81,10 +79,9 @@ public sealed class PrototypeTests [Test] public async Task ClientPrototypeSaveLoadSaveTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var context = new PrototypeSaveTest.TestEntityUidContext(); await SaveLoadSavePrototype(pair.Client, context); - await pair.CleanReturnAsync(); } private async Task SaveLoadSavePrototype( diff --git a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs index c641cd9bd1..ac7bdcd24d 100644 --- a/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeTests/PrototypeUploadTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Tag; using Robust.Client.Upload.Commands; using Robust.Shared.GameObjects; @@ -6,7 +7,7 @@ using Robust.Shared.Upload; namespace Content.IntegrationTests.Tests.PrototypeTests; -public sealed class PrototypeUploadTest +public sealed class PrototypeUploadTest : GameTest { public const string IdA = "UploadTestPrototype"; public const string IdB = $"{IdA}NoParent"; @@ -36,7 +37,7 @@ public sealed class PrototypeUploadTest [TestOf(typeof(LoadPrototypeCommand))] public async Task TestFileUpload() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings {Connected = true}); + var pair = Pair; var sCompFact = pair.Server.ResolveDependency(); var cCompFact = pair.Client.ResolveDependency(); @@ -79,7 +80,5 @@ public sealed class PrototypeUploadTest Assert.That(cProtoB!.TryGetComponent(out _, cCompFact), Is.False); Assert.That(cProtoD!.TryGetComponent(out _, cCompFact), Is.True); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Puller/PullerTest.cs b/Content.IntegrationTests/Tests/Puller/PullerTest.cs index a4fde86dbf..541417354a 100644 --- a/Content.IntegrationTests/Tests/Puller/PullerTest.cs +++ b/Content.IntegrationTests/Tests/Puller/PullerTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Hands.Components; using Content.Shared.Movement.Pulling.Components; using Content.Shared.Prototypes; @@ -9,7 +10,7 @@ namespace Content.IntegrationTests.Tests.Puller; #nullable enable [TestFixture] -public sealed class PullerTest +public sealed class PullerTest : GameTest { /// /// Checks that needsHands on PullerComponent is not set on mobs that don't even have hands. @@ -17,7 +18,7 @@ public sealed class PullerTest [Test] public async Task PullerSanityTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var compFactory = server.ResolveDependency(); @@ -39,7 +40,5 @@ public sealed class PullerTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Replays/ReplayTests.cs b/Content.IntegrationTests/Tests/Replays/ReplayTests.cs index dcff6c3b45..4bd5f62e78 100644 --- a/Content.IntegrationTests/Tests/Replays/ReplayTests.cs +++ b/Content.IntegrationTests/Tests/Replays/ReplayTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Shared.CCVar; using Robust.Shared; @@ -6,20 +7,21 @@ using Robust.Shared.Replays; namespace Content.IntegrationTests.Tests.Replays; [TestFixture] -public sealed class ReplayTests +public sealed class ReplayTests : GameTest { + public override PoolSettings PoolSettings => new() + { + DummyTicker = false, + Dirty = true + }; + /// /// Simple test that just makes sure that automatic replay recording on round restarts works without any issues. /// [Test] public async Task AutoRecordReplayTest() { - var settings = new PoolSettings - { - DummyTicker = false, - Dirty = true - }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var server = pair.Server; Assert.That(server.CfgMan.GetCVar(CVars.ReplayServerRecordingEnabled), Is.False); @@ -54,7 +56,5 @@ public sealed class ReplayTests await server.WaitPost(() => ticker.RestartRound()); await pair.RunTicksSync(25); Assert.That(recordMan.IsRecording, Is.False); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ResearchTest.cs b/Content.IntegrationTests/Tests/ResearchTest.cs index 4661a1ea9e..c95d68d9e2 100644 --- a/Content.IntegrationTests/Tests/ResearchTest.cs +++ b/Content.IntegrationTests/Tests/ResearchTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Lathe; using Content.Shared.Research.Prototypes; using Robust.Shared.GameObjects; @@ -8,12 +9,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class ResearchTest +public sealed class ResearchTest : GameTest { [Test] public async Task DisciplineValidTierPrerequesitesTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoManager = server.ResolveDependency(); @@ -42,14 +43,12 @@ public sealed class ResearchTest } }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task AllTechPrintableTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -99,7 +98,5 @@ public sealed class ResearchTest } }); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs b/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs index 40457f5488..13bb3cfd74 100644 --- a/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs +++ b/Content.IntegrationTests/Tests/ResettingEntitySystemTests.cs @@ -1,4 +1,5 @@ -using Content.Server.GameTicking; +using Content.IntegrationTests.Fixtures; +using Content.Server.GameTicking; using Content.Shared.GameTicking; using Robust.Shared.GameObjects; using Robust.Shared.Reflection; @@ -7,7 +8,7 @@ namespace Content.IntegrationTests.Tests { [TestFixture] [TestOf(typeof(RoundRestartCleanupEvent))] - public sealed class ResettingEntitySystemTests + public sealed class ResettingEntitySystemTests : GameTest { public sealed class TestRoundRestartCleanupEvent : EntitySystem { @@ -26,15 +27,17 @@ namespace Content.IntegrationTests.Tests } } + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }; + [Test] public async Task ResettingEntitySystemResetTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }); + var pair = Pair; var server = pair.Server; var entitySystemManager = server.ResolveDependency(); @@ -52,7 +55,6 @@ namespace Content.IntegrationTests.Tests Assert.That(system.HasBeenReset); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Respirator/LungTest.cs b/Content.IntegrationTests/Tests/Respirator/LungTest.cs index ae6b50ff0f..097fe5e3a0 100644 --- a/Content.IntegrationTests/Tests/Respirator/LungTest.cs +++ b/Content.IntegrationTests/Tests/Respirator/LungTest.cs @@ -6,6 +6,7 @@ using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Map; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Shared.Atmos.Components; using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Utility; @@ -14,7 +15,7 @@ namespace Content.IntegrationTests.Tests.Respirator; [TestFixture] [TestOf(typeof(LungSystem))] -public sealed class LungTest +public sealed class LungTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -54,7 +55,7 @@ public sealed class LungTest public async Task AirConsistencyTest() { // --- Setup - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -123,14 +124,12 @@ public sealed class LungTest "Did not exhale as much gas as was inhaled" ); } - - await pair.CleanReturnAsync(); } [Test] public async Task NoSuffocationTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); @@ -183,7 +182,5 @@ public sealed class LungTest $"Entity {entityManager.GetComponent(human).EntityName} is suffocating on tick {tick}"); }); } - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/RestartRoundTest.cs b/Content.IntegrationTests/Tests/RestartRoundTest.cs index 69c9a7dedf..1ed43b0951 100644 --- a/Content.IntegrationTests/Tests/RestartRoundTest.cs +++ b/Content.IntegrationTests/Tests/RestartRoundTest.cs @@ -1,20 +1,23 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class RestartRoundTest + public sealed class RestartRoundTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }; + [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }); + var pair = Pair; var server = pair.Server; var sysManager = server.ResolveDependency(); @@ -23,8 +26,7 @@ namespace Content.IntegrationTests.Tests sysManager.GetEntitySystem().RestartRound(); }); - await pair.RunTicksSync(10); - await pair.CleanReturnAsync(); + await pair.RunUntilSynced(); } } } diff --git a/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs b/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs index de89f16be7..e4f80eebb7 100644 --- a/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs +++ b/Content.IntegrationTests/Tests/Roles/StartingGearStorageTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Roles; using Content.Server.Storage.EntitySystems; using Robust.Shared.GameObjects; @@ -7,16 +8,17 @@ using Robust.Shared.Collections; namespace Content.IntegrationTests.Tests.Roles; [TestFixture] -public sealed class StartingGearPrototypeStorageTest +public sealed class StartingGearPrototypeStorageTest : GameTest { + public override PoolSettings PoolSettings => new() { Connected = true, Dirty = true }; + /// /// Checks that a storage fill on a StartingGearPrototype will properly fill /// [Test] public async Task TestStartingGearStorage() { - var settings = new PoolSettings { Connected = true, Dirty = true }; - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; var server = pair.Server; var mapSystem = server.System(); var storageSystem = server.System(); @@ -65,7 +67,5 @@ public sealed class StartingGearPrototypeStorageTest mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Round/JobTest.cs b/Content.IntegrationTests/Tests/Round/JobTest.cs index 215890791d..927f34a9ef 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 @@ using Robust.Shared.Prototypes; 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 @@ public sealed class JobTest [Test] public async Task StartRoundTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); var ticker = pair.Server.System(); @@ -95,7 +98,6 @@ public sealed class JobTest AssertJob(pair, Passenger); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -104,12 +106,7 @@ public sealed class JobTest [Test] public async Task JobPreferenceTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); var ticker = pair.Server.System(); @@ -133,7 +130,6 @@ public sealed class JobTest AssertJob(pair, Passenger); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -143,12 +139,7 @@ public sealed class JobTest [Test] public async Task JobWeightTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); var ticker = pair.Server.System(); @@ -169,7 +160,6 @@ public sealed class JobTest AssertJob(pair, Captain); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } /// @@ -178,12 +168,7 @@ public sealed class JobTest [Test] public async Task JobPriorityTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - InLobby = true - }); + var pair = Pair; pair.Server.CfgMan.SetCVar(CCVars.GameMap, _map); var ticker = pair.Server.System(); @@ -217,6 +202,5 @@ public sealed class JobTest }); await pair.Server.WaitPost(() => ticker.RestartRound()); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/RoundEndTest.cs b/Content.IntegrationTests/Tests/RoundEndTest.cs index 5de6de381d..16c460c3da 100644 --- a/Content.IntegrationTests/Tests/RoundEndTest.cs +++ b/Content.IntegrationTests/Tests/RoundEndTest.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.RoundEnd; using Content.Shared.CCVar; @@ -7,7 +8,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class RoundEndTest + public sealed class RoundEndTest : GameTest { private sealed class RoundEndTestSystem : EntitySystem { @@ -25,15 +26,18 @@ namespace Content.IntegrationTests.Tests } } + + public override PoolSettings PoolSettings => new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }; + [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - DummyTicker = false, - Connected = true, - Dirty = true - }); + var pair = Pair; var server = pair.Server; @@ -151,7 +155,6 @@ namespace Content.IntegrationTests.Tests roundEndSystem.DefaultCountdownDuration = TimeSpan.FromMinutes(4); ticker.RestartRound(); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/SalvageTest.cs b/Content.IntegrationTests/Tests/SalvageTest.cs index 0059db6292..68bdf4726c 100644 --- a/Content.IntegrationTests/Tests/SalvageTest.cs +++ b/Content.IntegrationTests/Tests/SalvageTest.cs @@ -1,4 +1,6 @@ -using Content.Shared.CCVar; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.Shared.CCVar; using Content.Shared.Salvage; using Robust.Shared.Configuration; using Robust.Shared.EntitySerialization.Systems; @@ -8,15 +10,16 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class SalvageTest +public sealed class SalvageTest : GameTest { /// /// Asserts that all salvage maps have been saved as grids and are loadable. /// [Test] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task AllSalvageMapsLoadableTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -24,7 +27,6 @@ public sealed class SalvageTest var prototypeManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); var mapSystem = entManager.System(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { @@ -50,8 +52,6 @@ public sealed class SalvageTest } } }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); + await RunUntilSynced(); } } diff --git a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs index eb3dc14720..914a1e21c2 100644 --- a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs @@ -1,4 +1,6 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; using Content.Shared.CCVar; using Robust.Server.GameObjects; using Robust.Shared.Configuration; @@ -12,14 +14,15 @@ using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class SaveLoadMapTest + public sealed class SaveLoadMapTest : GameTest { [Test] + [EnsureCVar(Side.Server, typeof(CCVars), nameof(CCVars.GridFill), false)] public async Task SaveLoadMultiGridMap() { var mapPath = new ResPath("/Maps/Test/TestMap.yml"); - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapManager = server.ResolveDependency(); var sEntities = server.ResolveDependency(); @@ -27,8 +30,6 @@ namespace Content.IntegrationTests.Tests var mapSystem = sEntities.System(); var xformSystem = sEntities.EntitySysManager.GetEntitySystem(); var resManager = server.ResolveDependency(); - var cfg = server.ResolveDependency(); - Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitAssertion(() => { @@ -94,8 +95,6 @@ namespace Content.IntegrationTests.Tests }); } }); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs index b41aa0bf2f..9339d61270 100644 --- a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs @@ -1,5 +1,6 @@ using System.IO; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.CCVar; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; @@ -16,12 +17,12 @@ namespace Content.IntegrationTests.Tests /// Tests that a grid's yaml does not change when saved consecutively. /// [TestFixture] - public sealed class SaveLoadSaveTest + public sealed class SaveLoadSaveTest : GameTest { [Test] public async Task CreateSaveLoadSaveGrid() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); @@ -85,10 +86,9 @@ namespace Content.IntegrationTests.Tests } }); testSystem.Enabled = false; - await pair.CleanReturnAsync(); } - private const string TestMap = "Maps/bagel.yml"; + private new const string TestMap = "Maps/bagel.yml"; /// /// Loads the default map, runs it for 5 ticks, then assert that it did not change. @@ -96,7 +96,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task LoadSaveTicksSaveBagel() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapLoader = server.ResolveDependency().GetEntitySystem(); var mapSys = server.System(); @@ -167,7 +167,6 @@ namespace Content.IntegrationTests.Tests testSystem.Enabled = false; await server.WaitPost(() => mapSys.DeleteMap(mapId)); - await pair.CleanReturnAsync(); } /// @@ -183,7 +182,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task LoadTickLoadBagel() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var mapLoader = server.System(); @@ -241,7 +240,6 @@ namespace Content.IntegrationTests.Tests testSystem.Enabled = false; await server.WaitPost(() => mapSys.DeleteMap(mapId1)); await server.WaitPost(() => mapSys.DeleteMap(mapId2)); - await pair.CleanReturnAsync(); } /// diff --git a/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs b/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs index 339420362c..daf14e42a0 100644 --- a/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs +++ b/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Robust.Shared.Reflection; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Manager.Attributes; @@ -8,7 +9,7 @@ using Robust.Shared.Serialization.Markdown.Value; namespace Content.IntegrationTests.Tests.Serialization; [TestFixture] -public sealed partial class SerializationTest +public sealed partial class SerializationTest : GameTest { /// /// Check that serializing generic enums works as intended. This should really be in engine, but engine @@ -17,7 +18,7 @@ public sealed partial class SerializationTest [Test] public async Task SerializeGenericEnums() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var seriMan = server.ResolveDependency(); var refMan = server.ResolveDependency(); @@ -67,8 +68,6 @@ public sealed partial class SerializationTest Assert.That(seriMan.ValidateNode(genericNode).GetErrors().Any(), Is.True); Assert.That(seriMan.ValidateNode(typedNode).GetErrors().Any(), Is.True); Assert.That(seriMan.ValidateNode(typedNode).GetErrors().Any(), Is.False); - - await pair.CleanReturnAsync(); } private enum TestEnum : byte { Aa, Bb, Cc, Dd } diff --git a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs index ab82a3d2f9..927f973472 100644 --- a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs +++ b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Shuttles.Systems; using Content.Tests; using Robust.Server.GameObjects; @@ -13,7 +14,7 @@ using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests.Shuttle; -public sealed class DockTest : ContentUnitTest +public sealed class DockTest : GameTest { private static IEnumerable TestSource() { @@ -26,7 +27,7 @@ public sealed class DockTest : ContentUnitTest [TestCaseSource(nameof(TestSource))] public async Task TestDockingConfig(Vector2 dock1Pos, Vector2 dock2Pos, Angle dock1Angle, Angle dock2Angle, bool result) { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var map = await pair.CreateTestMap(); @@ -83,14 +84,12 @@ public sealed class DockTest : ContentUnitTest Assert.That(result, Is.EqualTo(config != null)); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestPlanetDock() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var map = await pair.CreateTestMap(); @@ -126,7 +125,5 @@ public sealed class DockTest : ContentUnitTest var dockingConfig = dockingSystem.GetDockingConfig(shuttle, map.MapUid); Assert.That(dockingConfig, Is.Not.EqualTo(null)); }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/ShuttleTest.cs b/Content.IntegrationTests/Tests/ShuttleTest.cs index da5b82d91e..1c447db871 100644 --- a/Content.IntegrationTests/Tests/ShuttleTest.cs +++ b/Content.IntegrationTests/Tests/ShuttleTest.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.IntegrationTests.Fixtures; using Content.Server.Shuttles.Components; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -9,12 +10,12 @@ using Robust.Shared.Physics.Systems; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class ShuttleTest + public sealed class ShuttleTest : GameTest { [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -50,7 +51,6 @@ namespace Content.IntegrationTests.Tests { Assert.That(entManager.GetComponent(map.Grid).LocalPosition, Is.Not.EqualTo(Vector2.Zero)); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs b/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs index da7e1e8e9b..ca667f2e7a 100644 --- a/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs +++ b/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Shared.Item; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; @@ -22,7 +23,7 @@ namespace Content.IntegrationTests.Tests.Sprite; /// /// [TestFixture] -public sealed class PrototypeSaveTest +public sealed class PrototypeSaveTest : GameTest { private static readonly HashSet Ignored = new() { @@ -34,8 +35,7 @@ public sealed class PrototypeSaveTest [Test] public async Task AllItemsHaveSpritesTest() { - var settings = new PoolSettings() { Connected = true }; // client needs to be in-game - await using var pair = await PoolManager.GetServerClient(settings); + var pair = Pair; List badPrototypes = []; await pair.Client.WaitPost(() => @@ -58,7 +58,5 @@ public sealed class PrototypeSaveTest Assert.Fail($"Item prototype has no sprite: {proto.ID}. It should probably either be marked as abstract, not be an item, or have a valid sprite"); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/StartTest.cs b/Content.IntegrationTests/Tests/StartTest.cs index e2bf5e8ff1..223f4676b0 100644 --- a/Content.IntegrationTests/Tests/StartTest.cs +++ b/Content.IntegrationTests/Tests/StartTest.cs @@ -1,9 +1,10 @@ +using Content.IntegrationTests.Fixtures; using Robust.Shared.Exceptions; namespace Content.IntegrationTests.Tests { [TestFixture] - public sealed class StartTest + public sealed class StartTest : GameTest { /// /// Test that the server, and client start, and stop. @@ -11,7 +12,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task TestClientStart() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var client = pair.Client; Assert.That(client.IsAlive); await client.WaitRunTicks(5); @@ -27,8 +28,6 @@ namespace Content.IntegrationTests.Tests Assert.That(sRuntimeLog.ExceptionCount, Is.EqualTo(0), "No exceptions must be logged on server."); await server.WaitIdleAsync(); Assert.That(server.IsAlive); - - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs b/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs index 02552669f7..8da97cbab7 100644 --- a/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs +++ b/Content.IntegrationTests/Tests/Station/EvacShuttleTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.GameTicking; using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Systems; @@ -12,15 +13,21 @@ namespace Content.IntegrationTests.Tests.Station; [TestFixture] [TestOf(typeof(EmergencyShuttleSystem))] -public sealed class EvacShuttleTest +public sealed class EvacShuttleTest : GameTest { + public override PoolSettings PoolSettings => new PoolSettings() + { + DummyTicker = true, + Dirty = true, + }; + /// /// Ensure that the emergency shuttle can be called, and that it will travel to centcomm /// [Test] public async Task EmergencyEvacTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { DummyTicker = true, Dirty = true }); + var pair = Pair; var server = pair.Server; var entMan = server.EntMan; var ticker = server.System(); @@ -122,6 +129,5 @@ public sealed class EvacShuttleTest server.CfgMan.SetCVar(CCVars.EmergencyShuttleDockTime, dockTime); pair.Server.CfgMan.SetCVar(CCVars.EmergencyShuttleEnabled, false); pair.Server.CfgMan.SetCVar(CCVars.GameMap, gameMap); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Station/JobTests.cs b/Content.IntegrationTests/Tests/Station/JobTests.cs index 5172049a99..0961db1c67 100644 --- a/Content.IntegrationTests/Tests/Station/JobTests.cs +++ b/Content.IntegrationTests/Tests/Station/JobTests.cs @@ -2,12 +2,13 @@ using Content.Shared.Roles; using Content.Shared.Roles.Jobs; using Robust.Shared.Prototypes; using System.Linq; +using Content.IntegrationTests.Fixtures; namespace Content.IntegrationTests.Tests.Station; [TestFixture] [TestOf(typeof(SharedJobSystem))] -public sealed class JobTest +public sealed class JobTest : GameTest { /// /// Ensures that every job belongs to at most 1 primary department. @@ -16,7 +17,7 @@ public sealed class JobTest [Test] public async Task PrimaryDepartmentsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -43,6 +44,5 @@ public sealed class JobTest } } }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs index 4abd32bda0..c0f0b19b19 100644 --- a/Content.IntegrationTests/Tests/Station/StationJobsTest.cs +++ b/Content.IntegrationTests/Tests/Station/StationJobsTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Server.Station.Components; using Content.Server.Station.Systems; using Content.Shared.Maps; @@ -15,7 +16,7 @@ namespace Content.IntegrationTests.Tests.Station; [TestFixture] [TestOf(typeof(StationJobsSystem))] -public sealed class StationJobsTest +public sealed class StationJobsTest : GameTest { private const string StationMapId = "FooStation"; @@ -85,7 +86,7 @@ public sealed class StationJobsTest [Test] public async Task AssignJobsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -153,13 +154,12 @@ public sealed class StationJobsTest Assert.That(assigned.Values.Select(x => x.Item1).ToList(), Does.Contain("TCaptain")); }); }); - await pair.CleanReturnAsync(); } [Test] public async Task AdjustJobsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -203,13 +203,12 @@ public sealed class StationJobsTest Assert.That(stationJobs.IsJobUnlimited(station, "TChaplain"), "Could not make TChaplain unlimited."); }); }); - await pair.CleanReturnAsync(); } [Test] public async Task InvalidRoundstartJobsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var prototypeManager = server.ResolveDependency(); @@ -247,7 +246,6 @@ public sealed class StationJobsTest } }); }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs b/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs index f80cc089de..6b100f89fd 100644 --- a/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs +++ b/Content.IntegrationTests/Tests/Storage/EntityStorageTests.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Server.Storage.EntitySystems; using Content.Shared.Damage; using Content.Shared.Damage.Systems; @@ -7,7 +8,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Storage; [TestFixture] -public sealed class EntityStorageTests +public sealed class EntityStorageTests : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -31,7 +32,7 @@ public sealed class EntityStorageTests [Test] public async Task TestContainerDestruction() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var map = await pair.CreateTestMap(); @@ -76,7 +77,5 @@ public sealed class EntityStorageTests await server.WaitRunTicks(5); Assert.That(server.EntMan.Deleted(box)); Assert.That(server.EntMan.Deleted(crowbar), Is.False); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Storage/StorageTest.cs b/Content.IntegrationTests/Tests/Storage/StorageTest.cs index 95d94906bb..c0ef6a3691 100644 --- a/Content.IntegrationTests/Tests/Storage/StorageTest.cs +++ b/Content.IntegrationTests/Tests/Storage/StorageTest.cs @@ -1,6 +1,7 @@ #nullable enable using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Containers; using Content.Shared.Item; using Content.Shared.Prototypes; @@ -12,7 +13,7 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Storage; -public sealed class StorageTest +public sealed class StorageTest : GameTest { /// /// Can an item store more than itself weighs. @@ -21,7 +22,7 @@ public sealed class StorageTest [Test] public async Task StorageSizeArbitrageTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoManager = server.ResolveDependency(); @@ -44,13 +45,12 @@ public sealed class StorageTest $"Found storage arbitrage on {proto.ID}"); } }); - await pair.CleanReturnAsync(); } [Test] public async Task TestStorageFillPrototypes() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoManager = server.ResolveDependency(); @@ -72,13 +72,12 @@ public sealed class StorageTest } }); }); - await pair.CleanReturnAsync(); } [Test] public async Task TestSufficientSpaceForFill() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -159,14 +158,12 @@ public sealed class StorageTest } } }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestSufficientSpaceForEntityStorageFill() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entMan = server.ResolveDependency(); @@ -194,7 +191,6 @@ public sealed class StorageTest $"{proto.ID} storage fill is too large."); }); } - await pair.CleanReturnAsync(); } private int GetEntrySize(EntitySpawnEntry entry, bool getCount, IPrototypeManager protoMan, SharedItemSystem itemSystem) @@ -242,7 +238,7 @@ public sealed class StorageTest [Test] public async Task NoMultipleContainerFillsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var compFact = pair.Server.ResolveDependency(); Assert.Multiple(() => @@ -258,6 +254,5 @@ public sealed class StorageTest Assert.That(!proto.HasComponent(compFact), $"Prototype {proto.ID} has both {nameof(ContainerFillComponent)} and {nameof(StorageFillComponent)}."); } }); - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/StoreTests.cs b/Content.IntegrationTests/Tests/StoreTests.cs index 39df0fc8cc..811bf40548 100644 --- a/Content.IntegrationTests/Tests/StoreTests.cs +++ b/Content.IntegrationTests/Tests/StoreTests.cs @@ -1,7 +1,6 @@ -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.Traitor.Uplink; using Content.Shared.FixedPoint; using Content.Shared.Inventory; @@ -10,13 +9,12 @@ using Content.Shared.Store; using Content.Shared.Store.Components; using Content.Shared.StoreDiscount.Components; using Robust.Shared.GameObjects; -using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class StoreTests +public sealed class StoreTests : GameTest { [TestPrototypes] @@ -32,10 +30,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(); @@ -168,7 +179,5 @@ public sealed class StoreTests } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Tag/TagTest.cs b/Content.IntegrationTests/Tests/Tag/TagTest.cs index e6cd2accaf..4632e3ea2e 100644 --- a/Content.IntegrationTests/Tests/Tag/TagTest.cs +++ b/Content.IntegrationTests/Tests/Tag/TagTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Shared.Tag; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -10,7 +11,7 @@ namespace Content.IntegrationTests.Tests.Tag { [TestFixture] [TestOf(typeof(TagComponent))] - public sealed class TagTest + public sealed class TagTest : GameTest { private const string TagEntityId = "TagTestDummy"; @@ -44,7 +45,7 @@ namespace Content.IntegrationTests.Tests.Tag [Test] public async Task TagComponentTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var sEntityManager = server.ResolveDependency(); @@ -295,7 +296,6 @@ namespace Content.IntegrationTests.Tests.Tag Assert.Throws(() => { tagSystem.AddTags(sTagEntity, new HashSet> { UnregisteredTag }); }); #endif }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Tiles/TileStackRecursionTest.cs b/Content.IntegrationTests/Tests/Tiles/TileStackRecursionTest.cs index 52c5b03265..d1c26e9bec 100644 --- a/Content.IntegrationTests/Tests/Tiles/TileStackRecursionTest.cs +++ b/Content.IntegrationTests/Tests/Tiles/TileStackRecursionTest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.CCVar; using Content.Shared.Maps; using Robust.Shared.Configuration; @@ -7,12 +8,12 @@ using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Tiles; -public sealed class TileStackRecursionTest +public sealed class TileStackRecursionTest : GameTest { [Test] public async Task TestBaseTurfRecursion() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var protoMan = pair.Server.ResolveDependency(); var cfg = pair.Server.ResolveDependency(); var maxTileHistoryLength = cfg.GetCVar(CCVars.TileStackLimit); @@ -40,7 +41,6 @@ public sealed class TileStackRecursionTest (possibleTurf, new ProtoId(ctdef.ID)))); } Bfs(nodes, edges, maxTileHistoryLength); - await pair.CleanReturnAsync(); } private void Bfs(List<(ProtoId, int)> nodes, List<(ProtoId, ProtoId)> edges, int depthLimit) diff --git a/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs b/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs index 5efa009ca7..2aea2986ec 100644 --- a/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs +++ b/Content.IntegrationTests/Tests/UserInterface/UiControlTest.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Client.LateJoin; +using Content.IntegrationTests.Fixtures; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.ContentPack; using Robust.Shared.IoC; @@ -8,7 +9,7 @@ using Robust.Shared.Reflection; namespace Content.IntegrationTests.Tests.UserInterface; [TestFixture] -public sealed class UiControlTest +public sealed class UiControlTest : GameTest { // You should not be adding to this. private Type[] _ignored = new Type[] @@ -22,10 +23,7 @@ public sealed class UiControlTest [Test] public async Task TestWindows() { - var pair = await PoolManager.GetServerClient(new PoolSettings() - { - Connected = true, - }); + var pair = Pair; var activator = pair.Client.ResolveDependency(); var refManager = pair.Client.ResolveDependency(); var loader = pair.Client.ResolveDependency(); @@ -50,7 +48,5 @@ public sealed class UiControlTest activator.CreateInstance(type, oneOff: true, inject: false); } }); - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs b/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs index d460fd354f..6ab5ca1691 100644 --- a/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs +++ b/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs @@ -1,4 +1,5 @@ #nullable enable +using Content.IntegrationTests.Fixtures; using Content.Shared.Physics; using Content.Shared.Spawning; using Robust.Shared.GameObjects; @@ -8,7 +9,7 @@ namespace Content.IntegrationTests.Tests.Utility { [TestFixture] [TestOf(typeof(EntitySystemExtensions))] - public sealed class EntitySystemExtensionsTest + public sealed class EntitySystemExtensionsTest : GameTest { private const string BlockerDummyId = "BlockerDummy"; @@ -32,7 +33,7 @@ namespace Content.IntegrationTests.Tests.Utility [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -95,7 +96,6 @@ namespace Content.IntegrationTests.Tests.Utility Assert.That(entity, Is.Not.Null); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs b/Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs index 19b25816fa..d7e1239603 100644 --- a/Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs +++ b/Content.IntegrationTests/Tests/Utility/EntityWhitelistTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Containers.ItemSlots; using Content.Shared.Whitelist; using Robust.Shared.GameObjects; @@ -7,7 +8,7 @@ namespace Content.IntegrationTests.Tests.Utility { [TestFixture] [TestOf(typeof(EntityWhitelist))] - public sealed class EntityWhitelistTest + public sealed class EntityWhitelistTest : GameTest { private const string InvalidComponent = "Sprite"; private const string ValidComponent = "Physics"; @@ -58,7 +59,7 @@ namespace Content.IntegrationTests.Tests.Utility [Test] public async Task Test() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -111,7 +112,6 @@ namespace Content.IntegrationTests.Tests.Utility Assert.That(sys.IsValid(whitelistSer, WhitelistTestInvalidTag), Is.False); }); }); - await pair.CleanReturnAsync(); } } } diff --git a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs index 7058cfab6a..dfaaae05ad 100644 --- a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs +++ b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using Content.IntegrationTests.Fixtures; using Content.Server.VendingMachines; using Content.Server.Wires; using Content.Shared.Cargo.Prototypes; @@ -21,7 +22,7 @@ namespace Content.IntegrationTests.Tests [TestFixture] [TestOf(typeof(VendingMachineRestockComponent))] [TestOf(typeof(VendingMachineSystem))] - public sealed class VendingMachineRestockTest : EntitySystem + public sealed class VendingMachineRestockTest : GameTest { private static readonly ProtoId TestDamageType = "Blunt"; @@ -111,7 +112,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task TestAllRestocksAreAvailableToBuy() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -194,14 +195,12 @@ namespace Content.IntegrationTests.Tests $"Some entities with {restockCompName} are unavailable for purchase: \n - {string.Join("\n - ", restockEntities)}"); }); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestCompleteRestockProcess() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -280,14 +279,12 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestRestockBreaksOpen() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -342,14 +339,12 @@ namespace Content.IntegrationTests.Tests mapSystem.DeleteMap(testMap.MapId); }); - - await pair.CleanReturnAsync(); } [Test] public async Task TestRestockInventoryBounds() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; await server.WaitIdleAsync(); @@ -388,10 +383,6 @@ namespace Content.IntegrationTests.Tests Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(3), "Machine's available inventory did not stay the same after a third restock."); }); - - await pair.CleanReturnAsync(); } } } - -#nullable disable diff --git a/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs b/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs index 920dc08818..d9801275b9 100644 --- a/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs +++ b/Content.IntegrationTests/Tests/Wires/WireLayoutTest.cs @@ -1,4 +1,5 @@ -using Content.Server.Doors; +using Content.IntegrationTests.Fixtures; +using Content.Server.Doors; using Content.Server.Power; using Content.Server.Wires; using Robust.Shared.GameObjects; @@ -10,7 +11,7 @@ namespace Content.IntegrationTests.Tests.Wires; [TestFixture] [Parallelizable(ParallelScope.All)] [TestOf(typeof(WiresSystem))] -public sealed class WireLayoutTest +public sealed class WireLayoutTest : GameTest { [TestPrototypes] public const string Prototypes = """ @@ -53,7 +54,7 @@ public sealed class WireLayoutTest [Test] public async Task TestLayoutInheritance() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var testMap = await pair.CreateTestMap(); @@ -89,8 +90,6 @@ public sealed class WireLayoutTest Assert.That(ent3.Comp.WiresList, Has.One.With.Property("Action").InstanceOf(), "1 door bolt wire"); }); }); - - await pair.CleanReturnAsync(); } private static Entity SpawnWithComp(IEntityManager entityManager, string prototype, MapCoordinates coords) diff --git a/Content.IntegrationTests/Tests/WizdenContentFreeze/WizdenContentFreeze.cs b/Content.IntegrationTests/Tests/WizdenContentFreeze/WizdenContentFreeze.cs index 891525a25b..801c6c49b6 100644 --- a/Content.IntegrationTests/Tests/WizdenContentFreeze/WizdenContentFreeze.cs +++ b/Content.IntegrationTests/Tests/WizdenContentFreeze/WizdenContentFreeze.cs @@ -1,3 +1,4 @@ +using Content.IntegrationTests.Fixtures; using Content.Shared.Kitchen; namespace Content.IntegrationTests.Tests.WizdenContentFreeze; @@ -5,7 +6,7 @@ namespace Content.IntegrationTests.Tests.WizdenContentFreeze; /// /// These tests are limited to adding a specific type of content, essentially freezing it. If you are a fork developer, you may want to disable these tests. /// -public sealed class WizdenContentFreeze +public sealed class WizdenContentFreeze : GameTest { /// /// This freeze prohibits the addition of new microwave recipes. @@ -18,7 +19,7 @@ public sealed class WizdenContentFreeze [Test] public async Task MicrowaveRecipesFreezeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var protoMan = server.ProtoMan; @@ -35,7 +36,5 @@ public sealed class WizdenContentFreeze { Assert.Fail($"Oh, you deleted the microwave recipes? YOU ARE SO COOL! Please lower the number of recipes in MicrowaveRecipesFreezeTest from {recipesLimit} to {recipesCount} so that future contributors cannot add new recipes back."); } - - await pair.CleanReturnAsync(); } } diff --git a/Content.IntegrationTests/Tests/XenoArtifactTest.cs b/Content.IntegrationTests/Tests/XenoArtifactTest.cs index ac4c58c52c..35b03470e9 100644 --- a/Content.IntegrationTests/Tests/XenoArtifactTest.cs +++ b/Content.IntegrationTests/Tests/XenoArtifactTest.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.IntegrationTests.Fixtures; using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.Components; using Robust.Shared.GameObjects; @@ -6,7 +7,7 @@ using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests; [TestFixture] -public sealed class XenoArtifactTest +public sealed class XenoArtifactTest : GameTest { [TestPrototypes] private const string Prototypes = @" @@ -90,7 +91,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactAddNodeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -133,9 +134,6 @@ public sealed class XenoArtifactTest Assert.That(artifactSystem.GetDirectPredecessorNodes(artifactEnt, node3!.Value), Has.Count.EqualTo(1)); Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node3!.Value), Has.Count.EqualTo(2)); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -144,7 +142,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactRemoveNodeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -182,9 +180,6 @@ public sealed class XenoArtifactTest Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node2!.Value), Is.Empty); Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -193,7 +188,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactResizeTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -245,9 +240,6 @@ public sealed class XenoArtifactTest Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node4!.Value), Is.Empty); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -256,7 +248,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactReplaceTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -304,9 +296,6 @@ public sealed class XenoArtifactTest Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } /// @@ -315,7 +304,7 @@ public sealed class XenoArtifactTest [Test] public async Task XenoArtifactBuildActiveNodesTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -367,15 +356,12 @@ public sealed class XenoArtifactTest Assert.That(artifactEnt.Comp.CachedActiveNodes, Has.Count.EqualTo(expectedActiveNodes.Length)); }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } [Test] public async Task XenoArtifactGenerateSegmentsTest() { - await using var pair = await PoolManager.GetServerClient(); + var pair = Pair; var server = pair.Server; var entManager = server.ResolveDependency(); @@ -412,8 +398,5 @@ public sealed class XenoArtifactTest Assert.That(grouped[2].Count(), Is.LessThanOrEqualTo(2)); // maintain same width or, if we used 3 nodes on previous layer - we only have 1 left! }); - await server.WaitRunTicks(1); - - await pair.CleanReturnAsync(); } } diff --git a/Content.MapRenderer/Program.cs b/Content.MapRenderer/Program.cs index 90f97a5786..668e04f63d 100644 --- a/Content.MapRenderer/Program.cs +++ b/Content.MapRenderer/Program.cs @@ -145,7 +145,7 @@ namespace Content.MapRenderer { Console.Write($"Following map files did not exist on disk directly, searching through prototypes: {string.Join(", ", lookupPrototypeFiles)}"); - await using var pair = await PoolManager.GetServerClient(); + await using var pair = await PoolManager.GetServerClient(testContext: testContext); var mapPrototypes = pair.Server .ResolveDependency() .EnumeratePrototypes() diff --git a/Content.Shared/Trigger/Systems/TriggerSystem.cs b/Content.Shared/Trigger/Systems/TriggerSystem.cs index 1e7261043f..3ba9e76186 100644 --- a/Content.Shared/Trigger/Systems/TriggerSystem.cs +++ b/Content.Shared/Trigger/Systems/TriggerSystem.cs @@ -90,6 +90,9 @@ public sealed partial class TriggerSystem : EntitySystem if (!Resolve(ent, ref ent.Comp)) return false; + if (Terminating(ent)) + return false; // Stop trying to resurrect a dead horse. + if (HasComp(ent)) return false; // already activated From 7733ed303114fe3c75b3ed0d723733e975365f15 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 2 Apr 2026 00:56:45 -0400 Subject: [PATCH 036/247] Fix automatic whitelist condition being reversed (#43433) --- Resources/Prototypes/wizardsDenWhitelists.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/wizardsDenWhitelists.yml b/Resources/Prototypes/wizardsDenWhitelists.yml index 51cbd1bc2f..244244a3c1 100644 --- a/Resources/Prototypes/wizardsDenWhitelists.yml +++ b/Resources/Prototypes/wizardsDenWhitelists.yml @@ -34,6 +34,6 @@ action: Allow - !type:ConditionPlaytime minimumPlaytime: 1200 # 20 hours to be whitelisted - action: Deny + action: Allow - !type:ConditionAlwaysMatch action: Deny From 0b8ff0ffa276b6c59c17d415f360c959739ba1be Mon Sep 17 00:00:00 2001 From: ThatGuyUSA Date: Thu, 2 Apr 2026 14:56:08 -0700 Subject: [PATCH 037/247] New Wizard robe and hat in-hand sprites (#43429) * save station * Revert "Merge branch 'wiz-guardian-deck'" This reverts commit 78fa318583b6c93110c47e3b9e23f7222747f89a, reversing changes made to 4d5dab1098bcfdbce14906d9c77dbc669e295760. * resprite! * i forgot to credit myself oops * you had it set to W for Wumbo, when it should have been set to M for Mini --- .../Head/Hats/redwizard.rsi/inhand-left.png | Bin 516 -> 305 bytes .../Head/Hats/redwizard.rsi/inhand-right.png | Bin 490 -> 299 bytes .../Clothing/Head/Hats/redwizard.rsi/meta.json | 2 +- .../Hats/violetwizard.rsi/equipped-HELMET.png | Bin 515 -> 511 bytes .../Head/Hats/violetwizard.rsi/inhand-left.png | Bin 462 -> 377 bytes .../Head/Hats/violetwizard.rsi/inhand-right.png | Bin 486 -> 365 bytes .../Head/Hats/violetwizard.rsi/meta.json | 2 +- .../Head/Hats/wizardhat.rsi/equipped-HELMET.png | Bin 605 -> 619 bytes .../Head/Hats/wizardhat.rsi/inhand-left.png | Bin 300 -> 234 bytes .../Head/Hats/wizardhat.rsi/inhand-right.png | Bin 297 -> 236 bytes .../Clothing/Head/Hats/wizardhat.rsi/meta.json | 2 +- .../Misc/redwizard.rsi/inhand-left.png | Bin 385 -> 423 bytes .../Misc/redwizard.rsi/inhand-right.png | Bin 412 -> 432 bytes .../Misc/wizard.rsi/inhand-left.png | Bin 356 -> 272 bytes .../Misc/wizard.rsi/inhand-right.png | Bin 354 -> 275 bytes 15 files changed, 3 insertions(+), 3 deletions(-) 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 dabd7cb3d368dbebe2e6293f3b83ca4f0e568a7e..bac0bd75d95143b9fe250d96e3d14e5f8bc12371 100644 GIT binary patch delta 279 zcmV+y0qFjO1hE2;BYyw^b5ch_0Itp)=>Px#>q$gGRCt{2+RYJyFc5~}Psf_4ut d008Xq23G)uJP-?DoL>L{002ovPDHLkV1gm=fhGU| delta 491 zcmVUNxCejs8iglxAE zC;Aci0(wr|y>SiT!x)rW2Q#5ksws@YLu+J?EdYFv*nCFD@E&6<6vhAmo?4>@aBauJ z6~IGl6vm(o+%-RV0LbtwLf(FswL38*Ye2LBH)}5vRACIRt#dviQsIbGwxSGC1Dz0l z;Bwtq-+_&H0e^WWhnx#lcYkh3l(E2LcY$5E!LX%DKj17- zfYc78h61E^AT<;qwF86@LI@#*5JCtcujXa%-`6s9@Uo;#_8p9IDgl=C z4$S3{M`JE-fVuO%9GlCL``S!6d<|%fX&%mTd*;o%&14roH)}6bHQ?|W;Pwzaogm%* z0ifS^FgGI(p8?j|?lB1U4G2s72M(VBz+X^9?KQExz$pX}_UIcp)i$^X6d<(&si6R= h9Y_rYNbNvsegF+_vR~2qKzslI002ovPDHLkV1gn{+CKmQ 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 af141d7eec53158f41695fc626dacb04bdf767f0..cd3e95bb9a3310115850dc2d497c67be586ad038 100644 GIT binary patch delta 273 zcmV+s0q*|l1FHg%BYyw^b5ch_0Itp)=>Px#hBYy$0Nklq{glLYU zhJ*=B1;4{3f$I9($9B>hy|qnat-Vb$1f4NMWQremENp-?W|k0dzy!>U=%U#CN}inp z-iXvP&%Ir1IDZ9VZMkFt_p!BM&_!cswfM?U zfz|JTEw%U>*?d3Xm4Z$2_LU(TP+CA~Xh3NJrJ(_(1(fCoV-365Gc(B&00000NkvXX Hu0mjf6b{w! diff --git a/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/meta.json index 4e1c6c10da..1d42deb94c 100644 --- a/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/meta.json +++ b/Resources/Textures/Clothing/Head/Hats/redwizard.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. In-hand by ThatGuyUSA (github).", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hats/violetwizard.rsi/equipped-HELMET.png index f457d22f2f5b837ab7ec9175c2940d8147d2bc97..ca99276fce6a2c47c04fbf0b12884a719e50fa95 100644 GIT binary patch delta 486 zcmZo>`OiE-xt@WsILO_JVcj{ImkbPyJ3U<-Ln`LHopmtquz`T9Ige<;LpN?sg?0;B zgDDfZUL5daU#Xy)^5VcV2gWTEq{X8Q*wZ9abx%%P`oZ|aecfq`YkvjKIkQGgXu<1U z7cTJs-u3qG_4B*WCVlapd1h^7=+y7OeEUvh?VNhGXtv6v?rRrs)tgNSS;W|)cKxWf zs>`xTLDgNC-%e@zHcPueujqyTPT@C?a^G4Ov!#BO+_3)kzs$=W9Vd=I_jGZ2_g;?e z`|BIGe#oKDuGn{JYf0N5azj5bynSxrO3-^Db;= zDrl{le7dz>@XZ}Fkxrc$O`!wzTOa(c>S2}RO8BB!(L3c%?B@UMe?%o1q8NCY9k>@H zGGs8C@G$5$v@tGVyU^BZeT^_ z-OAWScOOpr`!nW^WUC>+t9^$OyUG;HzdwU+DAe$IZ4_2g>d00f?{elF{r5}E+gLe{qc literal 515 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU_9mN;uumf=k2V6zK0zIT#t8f z?I>8>Dl&oDi+joht``iK8AX@0Y)OdMXb>xU?8N<0pm$5on~!~auT4I*Cpsnl`P=VN zc4tg#@1KsbySJ9Wo1m4dpiUA$GP5O;>*nAr8R+NLgE zAFt>YW~~WN$Y~5wc>4MEhF6{^%;#U1OFDI&$Dw=Qnz;oa9?m?*uvmTEaT5_hIf1G71Ee=rv2LC@9_5DAEB%>8atR* z+?}t#a{HCThrV+4e0{Eel)t_!e%IEy*V88i|F1b`eP~+ybCn)5_qG4_v~IVLKXjp| zsWsg#vB*i~?eA!&m&evN6`xEOS!Oe3CHq6E2?3u1woEwjE56mHLy>*yt(FrrTDC{{ qW?3F-V9|9DX1u^;BFI1jW4~E9E2%i!ti=d#Wzp$PzDq~US^ 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 759e6b3ed975b6eef8a641a0d5751858934f5c15..27ff39da48480b0dfb9ad054dd34a0e7cebd8f87 100644 GIT binary patch delta 351 zcmV-l0igcQ1Nj1wBYyw^b5ch_0Itp)=>Px$Gf6~2RCt{2+Obi?Fcik|&m0q^Nf$=Q z04a2Apkn}v3_zFKqNZa2iVTorgB%3|Faqh)WCc+qGCJd&mGd)c{=dqxeV?DKxOz_l z00000002yRS4Z#ach-RYq;-gKf7nTqKEK|Y`})x&3Y0000C@dL09h0BD--ADic002ovPDHLkV1nf`qpJV_ delta 437 zcmV;m0ZRV)0?q@FBYy#vNklRW)pjZwrvN; z>Z0k)-^VHs{=KPlvMdYT((5j2wntqw-F|n#es=&cu6Xl0nSW~sGPj!qAjOL*g-n?{ zM>x@s!1GhlTZCDF>ErX{-3fN__FR{@_bRG}Sps;;O8~(4ehwhzJ z02-2g4K4)((0^WlHU^-*0BsCFdjZ;39{>>%5fKp)5v@=3hml(w`tUNgk0A8nWRd?7 z`fKlj%i;ov={;~s{t>YI52p4WgdTz919&aF4!C|&_i^N44?uQ+-GpJYn<~aO zP$VCKyantg43phZVLl3>0Jd?<`#(}tU6j28FXk<Px$CrLy>RCt{2+OciIKorLD&rv3zp@R`T zfD0=dSQ#LN2cV<&QPX9B6byi}0ZNepG6HlotZ)?3Ij8t6h;zt?^#4`Z#!ufp$<=!j z00000007XdDjQpsjU7A3Xzuawe&$gA$IuJ!AFC`>tjKFg>VJKnBA2=EdFLR0mx{dB z@^zj`(#BYOJRKxSpI>k7|5Is(pW9_xFZNcFrjwbrI}^+r3}2OveSR|S^>+9D`}e(6 z4)1J?ZJTgB9Wjnz{h~r1hHldkk(|@Aebi&O!`-Dl}yN9*e)T;KtxF l3@S9%{5`Y<0ssIo)EQzLh07$jmKgv5002ovPDHLkV1gfHnZ*DA delta 461 zcmaFM^o)6eay{c(PZ!6KiaBp@?GHR`Aj0;+KSpiA!sJ$-2~5?@eT*7L49|IUuA9s| zp#0>PA9{q z-;~x`ef=}Vs`(kqvSqq+)fJcMCg|V0vL*HXdvk^lLg@?*pP#ZYRP3lPv8&iOXWwO= zw+CFdxBa|-wfp_*`*PMHl9O!0R;_zI-?K!ZU8`8_Lh=FA70Yx#ew^9O_TgXIn}++{ z`$G52UH0F!NnWw|!jt;y-EA9KkL~$co4vb|r%%v{q5h#Q+w~?!4{?QFIFb96Q9uw3 zF8<=#wza0WePeCK%VQ@e+`P}>`}S~qRvCl$KIep6XMQTj%iA6K_H)9)N6TUs7`!a| zn!T)vFX02H$EOI<%&RlMGdH}JWt?5fn8>L%m+70_b~k6nJKP)#=QB;d$M`KUe96qs yw$qAc`^^3-bYU{dvTaSW-L z^LCbF)@=ug*7;GKPx5wsJT_ZTN`SHXe8PoA+3_xx-bI3Q6&4ta@F+ge+jZ{PY=sG@ zO9bm&CHs#}GdU-J>-*ay9-sGD8Z$Tjcl{6$IyGOSGH}+r&wuZ#eX0{^Ff_gXy7c{x z<(f`u-J+K&%eGAr@R(aNFL7IhtfZ01ddAwx&s{SwRkB^aB48NYI;Z=JSk2b+7oXhS zdVaO^bC%6VU!C2YnN>4ey`ttu+!vK6u9=%&tJc|@c1+r`mY3n{<;9<8|K9EUWH(dX z#?VkjeJ|noTn2`dpZS)pEc~&{rpo+Yezb6mlArFmV_U1gJz_Of^a?VGU&p}E9((iJ z+!TlK9*M#mQ{CU6l3=Luk1I=l)j2_yy&?YW>5cv0qg_0?E?>#-d7&pW?d4?N7hIdv zw;ykr5Popp?(A(3qIL+JY3OfyUG;F7_*i*dFVdQ&MBb@0B@8Ts{jB1 literal 605 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU{djPaSW-L^LCbRR&#(zYrpHk zl1rVFeXKGZnHqBrIUc{+QJrc2h>Po{ivr6f$K!KTgjkr@H-B1lQM}e}>gD;r&-OgB zeg9eg(B6A#n;W$~XRUno?e*zf#deY_pPikcu=yufqDa5c5wTeHo}@F+R<}*qw}op* z*(a5=AreOeT=`_vG6bSaa($z7XZ!5)J@_ks^UqgF(V~`*jtT*d(tO_nRJbSN(ci@l z;w?|#JL@%E|H|ia$>(Q(HiJWfm4m3|x$@J;3jY;$|JFEAw>|ew@x-L437H=mPFY{% zb`sg%&(QE$ar3I%nhT`dx-H~49W$(wXRu+ZW&kRd{gQVfT!!IZ*ZSM{?DHP7Duzsq z?^vNHvtfFk>H?`3w@xnqsbX4@Evet z*}%L(kns&ejthe|gET=QtJ#ayB#Ie>pBC(5KKAb7Zi8zzO_LioR)5JYZM*h<(Rbww z%iZ&{lD<8CDY@!i?>fD>@~+tD!co=DdbU*zPyaOJ_V4R`Rnt`eXP&LW)$*DCFH98{ zO73>Q?^r5>H&Uc4mshA(3NCUj(GsCAj?iJADC1 u0(*io(+$QO9Smg*W=ajR4ZQ?~=2uOS4!t_LhXa^w7(8A5T-G@yGywolg9v8; 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 4a3bb5d4b85d1ee09e1f4e16a619e7a8b8eb3c61..8c45376672f245ce2384882f570058c8af075c14 100644 GIT binary patch delta 218 zcmZ3(^ontUL_G&H0|Ud{?Tv4M6k~CayA#8@b22Z19H9W85LY10te4O2G?mAFu4dRQ z_P_(CuixFS$kcif|(++YSP~dU)Rrvq^bhF=$l(}Mi zawe2(2qmqIn{}-{tiTzl=le)wP1=i8 zk@EdlC6ar78o!j&VQY!FP}zLrPxifL1{bkM=T$f!@Nj+peOckbjD#S*?ePpJ6yi!; QfDT~rboFyt=akR{0O3tpZ2$lO delta 285 zcmaFGxQ1zhL_HHT0|Uc#_L;swiZj3`#1%+0FeETAoY4Ih;s|6imIV0)GdMiEkp|>s zSA|5BxTF>*7iAWdWaj5FFjUM54l5`s{r)Am;N#aPTHd-^=gyoD-VkbV(fGk5o%24L zCmD)*dUseD2N_>B_EMPhXws2MAqv4OZ8mEuZ*=Lb?>1K5yh370cen9c4MTI$BW6#6 z!V10?GtTB_h*>PPRK9eL4ba{wPZ!4!i{9h}3Dy-2tTP`o99iR0<2Hd|GP6?yuZsce z0oRBl2Tp|@VvPuIVx6YcbD|}~aR%eHro_`45^EY*V;e+c8%{FDDlqu6Xtp}APFwUT hFo>fpnvGeEfkBmzeWuw4-DaRQ44$rjF6*2UngG?rWxxOc 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 4f669b4bb74606d02dcdced92a498a59a0231bd1..d4a9e495ffd67f571051dc46edd8093feac0e2d9 100644 GIT binary patch delta 220 zcmZ3<^oDVQL_G&H0|Ud{?Tv4M6k~CayA#8@b22Z19H9W85LY10te4O2G?mAFu4dRQ z_P_(CuixmD6=GU+PYw!mdWfPy;6!zSnQml&*R!a_(Hy;$7o;xm5H2 zwE1>B439~C77zF%JZ<-eRTXDuTkG3Z)fXtJ^PPI6->h&pZN`^<_N>dEFzx=raH1kW RtqJG^22WQ%mvv4FO#q+cRlEQI delta 282 zcmaFExRPmtL_HHT0|Uc#_L;swiZj3`#1%+0FeETAoY4Ih;s|6imIV0)GdMiEkp|>s zSA|5BxTF>*7iAWdWaj5FFjUM54l5`s{r)Am;N#aPTHd-^=gyoD-VkbV(fGk5o%24L zCmD)*dUseD2N_>B_EMPhXws2MAqv4OZ8mEuZ*=Lb?>1K5yh370cen9c4MTI$BW6#6 z!V10?GtTB_h*>PPRK9eL4ba|jPZ!4!i{9h}3D$@PW@ct?qxpgw2fB0)upRN?gQ2jCj;j&#BG&o3ixUEv8A}=&Oze`GrRIyqDx7BY^%mL1uuIG* enoWp-VUBPZN7#eS4Xi*r7(8A5T-G@yGywp&;a+k8 diff --git a/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/wizardhat.rsi/meta.json index 03229e284d..4bce2899c5 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/Misc/redwizard.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Misc/redwizard.rsi/inhand-left.png index eec3320f0f7a6316f76c6a89a04f396908ba2583..991445e5824fe2fcb19ffe1866770967bdf5cca6 100644 GIT binary patch delta 398 zcmZo*OY#?#$qy38T0(LchAD+|| z5f;p+bZeFu^vX4?bQWaxo?e#mY>KkNUY^259h&o(EIjYur4wBxTffJPP1*$xmdF>M z3o>XBIWuSTwvW%|ME2xL+po-epm-y3zZv(!SswM5f5@CUP`2@mwaz1zOBOd|&g{`w zdr*5dEX{TCk6V{b_I2N0XkBt|GxLc}ixf{=9R0TJ{P_rz1ZyN&Fobtkr)?|bDOVHf+4$@bB0303B2Pttn) z%oDsH7+-teFuh*v{jH{}m;Xsu$YrikLI5Lvv8JWA{=MndMkP1aul8hr{$dx$X6tgD zy|y(4@Ap@{xp_Xvn4xPoyT9k5*FSB(di;x%^6z{7RqyW^GiE1_2e!}tKD{q1<^1dQ qzXMlO4+Q)>%Csl`v3mg9C*~@rEqA6DGqExNfhW7GpUXO@geCy`3$}9r delta 359 zcmZ3^+{ipZxt`I()5S5QV$R!Jws}m30&Ne?1;Y!vLiRDn&iYbv@m`blTT2UO)|Hon zH|SQkdBk(&ICXwkfAGQf(y28|BilY^XyzCg!@%Z3k)|jC2ksyat=@2spf26s{VR(f zz217uWYq!=E=OCB1+h}U8C~~unyKDy7J2-V{YXXqywkCTp?{8+=Kc{lA8c=M z*M7pW#}&`UOYc8#?#p~&pTa-AoKISM(_aN^Pd}2cAGVi0H$CEK&L=*G3PwH#!+FP? zx@G1cJL*#Tcjo7AgLvnEtZql9&sfTw^~vP8v-tw?2ikY{Gs&~w-^v=h?XURS3xl;q;`~CE0r6z48Mtt8^XgoP?crWXgr@md{h!$g$w|TVn-BYyw^b5ch_0Itp)=>Px$YDq*vRCt{2+A&VUFc^m67t|}b1Cw`# zGE^*_0p%9HQ@BRAE=(PY7+AWrLo+8=K>8W#giuOF+LSird!)#+l~|vgKUr)L5fKp) z5fKp)5$QW~&QdUd6wI8nb7yM8oHKLIEM{+%zj`nPTQ0_CA%9d8oiuez%sI=!3~V0T z{I~hjcykVB-ft%Gz8Wj5;!=IdU%U3@zuq2j-ZKZtvduu2v5wF zaur7-%VQ~>B<+dJYHvLUo9)T}tpWgsz#72&P1`YpGJk9bO(&>+gyq$dA7H6T{+$)j zCs(@$CtyS1Er0fJfX5&1y+kvxdfC4N4;{)cA|fIpA|fIpA|i5W(zx4dA#`fjXxl+M zLn&BruIeur2>PUc(!x4$a%7*QJ^}OI z@SYGtdx^TBJULeGzxTER{)2-T;H4ss!#4q78^uFL_|bHL_|bH#?G9x91I`_ zGw1Bsnffs2%$zfe*?Z-09}L0rI9fuBV9r?yW?;)WmcPxP$A6nkF!R3t0q@&d(-o)c zOa9KaEC2as!FkUTpeX7>QLv8n6gcl$HIpm@U`5aTvt|{iQ!8UD4V?2&Ejs^t3AWgg z|62zDOn^0j_w|^V3utq%(zeN+>!BKSz*sNeR=R<}BlpzLE3$#QNcTb_F^i+d~)-Kt{5wnRbY z+@1&CcQrX$IJVu{{^zpfqf7lh|3e*~D>*D-`5u1$pHu9CC(dR-o%ebk)mX6Y)T~bT w{Wr;BZ&~|J%c(5Ni_|>UYx#5h%L}YvtSV$&-1jkk7SKryp00i_>zopr0PPrR>i_@% delta 341 zcmbQh^n_`GL_G^L0|P^Zd(K-RB@y5g;tHf492^)JPT1MmSy)&wFeCs)Zrr#LdiQ7q zki}RMeukTAW;zSx}OhpU1#ZF(){zps4iwm*9erU!Q1s>uQ}l zb3S-OsKG_!2aj~l`)HnIDC+6mVPPC(eA(DbVNU&{Nk=AyCD9yuACnPNpRw5e+w7ALB#WBRAGdV$mb#X(OLqLFl zhY=SSSHkpkBauMXpfz1hPB%nZG&LJVH8eF@CoEUc(beT$!r<}H$dNavNh(j9LxY1i zh?iBkmZj?fox|Yi>gTe~DWM4fnM8BE 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 e2542d772cd650319a602ef95fa8bf4bdcd16970..84f70a58a6537b3409e487ea7afc27bdbd26a249 100644 GIT binary patch delta 259 zcmaFFG?{6FL_G&H0|Ud{?Tv4M6k~CayA#8@b22Z19K`^i5LY10te4MZR4r^#$n7+h z$9=A5*ev$I1Mz*=dX}BLb=BnUcOLFMGa64Cm0+CeU6 zLk?DBn+YlZ|2Ln`%gi)u4b{^R8 BYApZ& delta 339 zcmV-Z0j&O$0^$OY7=Hu<00013M{Ml?00000787K0Y`&H~;_u z*x1-lgK%~L0004WQchCV=-0C=2JR&a84_w-Y6 z@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoPXky#FA7XQ>i?&swh7x zRf&r;C9|j)q>YO+ttc@!6~s2=QdV&Fa{(I+0Dcl15LR>@Q2+n{fJsC_R7i>Klrai` zFbqXoy+DJwOA78b(A~@Q3cbFyN^wY}gW!_4WcZJa&lpAd77O6`1pv-7Y%sC=xX+;q zM6_^%C+ZYZN=&S)9rF9(h=5|rl)gp&XO002ovPDHLkV1idZhEM Date: Thu, 2 Apr 2026 22:12:37 +0000 Subject: [PATCH 038/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 62b7a50860..cf60d97d83 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4029,3 +4022,10 @@ 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 From 81be6f25713f5770636368e0d1c6782922d5e7dd Mon Sep 17 00:00:00 2001 From: Booblesnoot42 <108703193+Booblesnoot42@users.noreply.github.com> Date: Fri, 3 Apr 2026 02:03:23 -0400 Subject: [PATCH 039/247] Force Vent Critters to Attack (#42399) * Create force attack component, system and add to spider * Add check for incapacitated enemies * minor cleanup * Fix networking stuff, hopefully * Add component to slimes * Move system and component to server, add localization * Fixed attack animation not playing for server-forced attacks, hopefully didn't break anything godo * Add feedback popup * pretty big optimization --------- Co-authored-by: SlamBamActionman Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../ForceAttack/ForceAttackComponent.cs | 35 ++++++++ .../ForceAttack/ForceAttackSystem.cs | 82 +++++++++++++++++++ .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 12 +-- .../components/force-attack-component.ftl | 1 + .../Prototypes/Entities/Mobs/NPCs/animals.yml | 1 + .../Entities/Mobs/NPCs/elemental.yml | 1 + .../Prototypes/Entities/Mobs/NPCs/slimes.yml | 3 + .../FeedbackPopup/feedbackpopups.yml | 13 +++ 8 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 Content.Server/ForceAttack/ForceAttackComponent.cs create mode 100644 Content.Server/ForceAttack/ForceAttackSystem.cs create mode 100644 Resources/Locale/en-US/components/force-attack-component.ftl diff --git a/Content.Server/ForceAttack/ForceAttackComponent.cs b/Content.Server/ForceAttack/ForceAttackComponent.cs new file mode 100644 index 0000000000..ce24554714 --- /dev/null +++ b/Content.Server/ForceAttack/ForceAttackComponent.cs @@ -0,0 +1,35 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.ForceAttack; + +/// +/// This is used to force a player-controlled mob to attack nearby enemies, preventing "friendly antag"ing. +/// +[RegisterComponent] +[AutoGenerateComponentPause] +public sealed partial class ForceAttackComponent : Component +{ + /// + /// The next time this component will attempt to force an attack. + /// + [AutoPausedField] + public TimeSpan NextAttack = TimeSpan.MaxValue; + + /// + /// Whether an enemy is in range. + /// + public bool InRange = false; + + /// + /// The time this component will wait before forcing an attack when an enemy is in range. + /// + [DataField] + public TimeSpan PassiveTime = TimeSpan.FromSeconds(5); + + /// + /// The message displayed on forced attack + /// + [DataField] + public LocId Message = "force-attack-component-message"; +} diff --git a/Content.Server/ForceAttack/ForceAttackSystem.cs b/Content.Server/ForceAttack/ForceAttackSystem.cs new file mode 100644 index 0000000000..33512b0e68 --- /dev/null +++ b/Content.Server/ForceAttack/ForceAttackSystem.cs @@ -0,0 +1,82 @@ +using System.Linq; +using Content.Shared.CombatMode; +using Content.Shared.Mobs.Systems; +using Content.Shared.NPC.Components; +using Content.Shared.NPC.Systems; +using Content.Shared.Popups; +using Content.Shared.Weapons.Melee; +using Content.Shared.Weapons.Melee.Events; +using Robust.Shared.Player; +using Robust.Shared.Timing; +using Robust.Shared.Utility; + +namespace Content.Server.ForceAttack; + +/// +/// This handles forcing a player-controlled mob to attack nearby enemies. +/// +public sealed class ForceAttackSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedMeleeWeaponSystem _melee = default!; + [Dependency] private readonly NpcFactionSystem _faction = default!; + [Dependency] private readonly SharedCombatModeSystem _mode = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly MobStateSystem _mob = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnMeleeAttack); + } + + private void OnMeleeAttack(Entity ent, ref MeleeAttackEvent args) + { + ent.Comp.NextAttack = _timing.CurTime + ent.Comp.PassiveTime; + } + + /// + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + + // Query includes ActorComponent to only get mobs currently controlled by players + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var forceComp, out var factionComp, out var modeComp, out _)) + { + // Check if we have a weapon + if (!_melee.TryGetWeapon(uid, out var weaponUid, out var weapon) || weapon.NextAttack > curTime) + continue; + + // Find a target in range that isn't critical or dead + if (!_faction.GetNearbyHostiles((uid, factionComp), weapon.Range) + .Where((potTarget) => !_mob.IsIncapacitated(potTarget)) + .TryFirstOrNull(out var target)) + { + forceComp.InRange = false; + continue; + } + + if (!forceComp.InRange) // Just entered range + { + forceComp.InRange = true; + forceComp.NextAttack = curTime + forceComp.PassiveTime; + continue; + } + + if (forceComp.NextAttack > curTime) + continue; + + // Force mob to enter combat mode (necessary for AttemptAttack to succeed). + _mode.SetInCombatMode(uid, true, modeComp); + + var popupMessage = Loc.GetString(forceComp.Message); + if (popupMessage.Length != 0) + _popup.PopupEntity(popupMessage, uid, uid); + + _melee.AttemptLightAttack(uid, weaponUid, weapon, target.Value, false); + } + } +} diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 6f4b078fe2..f827249dd3 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -344,12 +344,12 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(null, GetNetEntity(weaponUid), GetNetCoordinates(coordinates)), null); } - public bool AttemptLightAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target) + public bool AttemptLightAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target, bool predicted = true) { if (!TryComp(target, out TransformComponent? targetXform)) return false; - return AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(targetXform.Coordinates)), null); + return AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(targetXform.Coordinates)), null, predicted); } public bool AttemptDisarmAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target) @@ -364,7 +364,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem /// Called when a windup is finished and an attack is tried. /// /// True if attack successful - private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, AttackEvent attack, ICommonSession? session) + private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, AttackEvent attack, ICommonSession? session, bool predicted = true) { var curTime = Timing.CurTime; @@ -471,7 +471,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem throw new NotImplementedException(); } - DoLungeAnimation(user, weaponUid, weapon.Angle, TransformSystem.ToMapCoordinates(GetCoordinates(attack.Coordinates)), weapon.Range, animation); + DoLungeAnimation(user, weaponUid, weapon.Angle, TransformSystem.ToMapCoordinates(GetCoordinates(attack.Coordinates)), weapon.Range, animation, predicted); } var attackEv = new MeleeAttackEvent(weaponUid); @@ -954,7 +954,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem return true; } - private void DoLungeAnimation(EntityUid user, EntityUid weapon, Angle angle, MapCoordinates coordinates, float length, string? animation) + private void DoLungeAnimation(EntityUid user, EntityUid weapon, Angle angle, MapCoordinates coordinates, float length, string? animation, bool predicted = true) { // TODO: Assert that offset eyes are still okay. if (!TryComp(user, out TransformComponent? userXform)) @@ -975,7 +975,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem if (localPos.Length() > visualLength) localPos = localPos.Normalized() * visualLength; - DoLunge(user, weapon, angle, localPos, animation); + DoLunge(user, weapon, angle, localPos, animation, predicted); } public abstract void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true); diff --git a/Resources/Locale/en-US/components/force-attack-component.ftl b/Resources/Locale/en-US/components/force-attack-component.ftl new file mode 100644 index 0000000000..fc033e1edc --- /dev/null +++ b/Resources/Locale/en-US/components/force-attack-component.ftl @@ -0,0 +1 @@ +force-attack-component-message = Your anger overwhelms you! diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 1379095e4e..0c8e4beedb 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -2619,6 +2619,7 @@ raffle: settings: short - type: GhostTakeoverAvailable + - type: ForceAttack - type: entity name: tarantula diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml index b3f7749246..49f53654f2 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml @@ -386,6 +386,7 @@ solution: bloodstream - type: DrainableSolution solution: bloodstream + - type: ForceAttack - type: entity parent: MarkerBase diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml index 8d8b4f763e..ef1203e458 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml @@ -168,6 +168,7 @@ - MindRoleGhostRoleTeamAntagonist raffle: settings: short + - type: ForceAttack - type: entity name: green slime @@ -208,6 +209,7 @@ - MindRoleGhostRoleTeamAntagonist raffle: settings: short + - type: ForceAttack - type: entity name: yellow slime @@ -247,3 +249,4 @@ - MindRoleGhostRoleTeamAntagonist raffle: settings: short + - type: ForceAttack diff --git a/Resources/Prototypes/FeedbackPopup/feedbackpopups.yml b/Resources/Prototypes/FeedbackPopup/feedbackpopups.yml index d424a24244..a71276bffc 100644 --- a/Resources/Prototypes/FeedbackPopup/feedbackpopups.yml +++ b/Resources/Prototypes/FeedbackPopup/feedbackpopups.yml @@ -17,3 +17,16 @@ responseType: "General Feedback" responseLink: "https://forum.spacestation14.com/c/development/feedback/51" showRoundEnd: false + +- type: feedbackPopup + id: ForceVentCrittersToAttackFeedback + popupOrigin: wizden_master + title: "[bold]Played an antagonist vent critter role?[/bold]" + description: >- + If you've played or interacted with a player-controlled antagonist vent critter (spiders, slimes, clown spiders), please leave your feedback on how the new auto-attack feature went. + responseType: "Feedback Thread" + responseLink: "https://forum.spacestation14.com/t/force-vent-critters-to-attack-feedback/26861" + showRoundEnd: true + ruleWhitelist: + components: + - VentHordeRule From 9504c1e0bb66294937dfd5037fe5c7a08e420c68 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 3 Apr 2026 06:19:23 +0000 Subject: [PATCH 040/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index cf60d97d83..f6d5385c12 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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" @@ -4029,3 +4022,11 @@ 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 From 17ccf1dc70d7a931c798bf8987c343d65afb96ed Mon Sep 17 00:00:00 2001 From: alexalexmax <149889301+alexalexmax@users.noreply.github.com> Date: Fri, 3 Apr 2026 06:21:04 -0400 Subject: [PATCH 041/247] Hijack the Automated Trade Station objective (#42135) * predict TradeStationComponent * hijack beacon first pass * add Objective uplink category * add HijackTradeStationCondition, objective, and uplink entry * add sprite and cleanups * fix + make cooldown sane * use TimeSpans and update logic accordingly + private state-related fields * remove temporary reward * tiny loc changes * buncha changes * Change success message, beacon physics properties, formatting --------- Co-authored-by: seanpimble <149889301+seanpimble@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: SlamBamActionman --- .../Cargo/Components/TradeStationComponent.cs | 10 - .../Cargo/Systems/CargoSystem.Shuttle.cs | 3 +- Content.Server/Cargo/Systems/CargoSystem.cs | 1 + .../HijackTradeStationConditionComponent.cs | 11 + .../HijackTradeStationConditionSystem.cs | 35 ++ .../Cargo/Components/TradeStationComponent.cs | 17 + Content.Shared/Cargo/SharedCargoSystem.cs | 19 + .../EntitySystems/AnchorableSystem.cs | 23 +- .../ActiveHijackBeaconComponent.cs | 16 + .../HijackBeacon/HijackBeaconComponent.cs | 53 +++ .../HijackBeacon/HijackBeaconSystem.cs | 353 ++++++++++++++++++ .../en-US/hijack-beacon/hijack-beacon.ftl | 16 + Resources/Locale/en-US/store/categories.ftl | 1 + .../Locale/en-US/store/uplink-catalog.ftl | 4 + .../Prototypes/Catalog/uplink_catalog.yml | 17 + .../Entities/Objects/Tools/hijack_beacon.yml | 34 ++ .../Prototypes/Objectives/objectiveGroups.yml | 8 + Resources/Prototypes/Objectives/traitor.yml | 17 + Resources/Prototypes/Store/categories.yml | 5 + Resources/Prototypes/Store/presets.yml | 1 + .../hijack_beacon.rsi/extraction_point.png | Bin 0 -> 1389 bytes .../Objects/Tools/hijack_beacon.rsi/meta.json | 20 + 22 files changed, 648 insertions(+), 16 deletions(-) delete mode 100644 Content.Server/Cargo/Components/TradeStationComponent.cs create mode 100644 Content.Server/Objectives/Components/HijackTradeStationConditionComponent.cs create mode 100644 Content.Server/Objectives/Systems/HijackTradeStationConditionSystem.cs create mode 100644 Content.Shared/Cargo/Components/TradeStationComponent.cs create mode 100644 Content.Shared/HijackBeacon/ActiveHijackBeaconComponent.cs create mode 100644 Content.Shared/HijackBeacon/HijackBeaconComponent.cs create mode 100644 Content.Shared/HijackBeacon/HijackBeaconSystem.cs create mode 100644 Resources/Locale/en-US/hijack-beacon/hijack-beacon.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Tools/hijack_beacon.yml create mode 100644 Resources/Textures/Objects/Tools/hijack_beacon.rsi/extraction_point.png create mode 100644 Resources/Textures/Objects/Tools/hijack_beacon.rsi/meta.json diff --git a/Content.Server/Cargo/Components/TradeStationComponent.cs b/Content.Server/Cargo/Components/TradeStationComponent.cs deleted file mode 100644 index 0422470cc9..0000000000 --- a/Content.Server/Cargo/Components/TradeStationComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Content.Server.Cargo.Components; - -/// -/// Target for approved orders to spawn at. -/// -[RegisterComponent] -public sealed partial class TradeStationComponent : Component -{ - -} diff --git a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs index 351451123c..635eadea1d 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs @@ -6,6 +6,7 @@ using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Events; using Content.Shared.Cargo.Prototypes; using Content.Shared.CCVar; +using Content.Shared.HijackBeacon; using Robust.Shared.Audio; using Robust.Shared.Prototypes; @@ -14,7 +15,7 @@ namespace Content.Server.Cargo.Systems; public sealed partial class CargoSystem { /* - * Handles cargo shuttle / trade mechanics. + * Handles automated trade station / trade mechanics. */ private static readonly SoundPathSpecifier ApproveSound = new("/Audio/Effects/Cargo/ping.ogg"); diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index 9f3a4d5bf3..a35f1e7ea3 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Access.Systems; using Content.Shared.Administration.Logs; using Content.Server.Radio.EntitySystems; using Content.Shared.Cargo; +using Content.Shared.Cargo.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Mobs.Components; using Content.Shared.Paper; diff --git a/Content.Server/Objectives/Components/HijackTradeStationConditionComponent.cs b/Content.Server/Objectives/Components/HijackTradeStationConditionComponent.cs new file mode 100644 index 0000000000..416c422837 --- /dev/null +++ b/Content.Server/Objectives/Components/HijackTradeStationConditionComponent.cs @@ -0,0 +1,11 @@ +using Content.Server.Objectives.Systems; + +namespace Content.Server.Objectives.Components; + +/// +/// Objective condition that requires the player to hijack the trade station. +/// +[RegisterComponent, Access(typeof(HijackTradeStationConditionSystem))] +public sealed partial class HijackTradeStationConditionComponent : Component +{ +} diff --git a/Content.Server/Objectives/Systems/HijackTradeStationConditionSystem.cs b/Content.Server/Objectives/Systems/HijackTradeStationConditionSystem.cs new file mode 100644 index 0000000000..e5f88a28dd --- /dev/null +++ b/Content.Server/Objectives/Systems/HijackTradeStationConditionSystem.cs @@ -0,0 +1,35 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Cargo.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using NetCord; + +namespace Content.Server.Objectives.Systems; + +/// +/// Handles the Hijack Trade Station objective. +/// +public sealed class HijackTradeStationConditionSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetProgress); + } + + private void OnGetProgress(Entity ent, ref ObjectiveGetProgressEvent args) + { + var enumerator = EntityQueryEnumerator(); + args.Progress = 0f; + // If there's any hacked trade station, succeed. + while (enumerator.MoveNext(out var comp)) + { + if (!comp.Hacked) + continue; + + args.Progress = 1f; + return; + } + } +} diff --git a/Content.Shared/Cargo/Components/TradeStationComponent.cs b/Content.Shared/Cargo/Components/TradeStationComponent.cs new file mode 100644 index 0000000000..ec0cba5386 --- /dev/null +++ b/Content.Shared/Cargo/Components/TradeStationComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.HijackBeacon; +using Robust.Shared.GameStates; + +namespace Content.Shared.Cargo.Components; + +/// +/// Target for approved orders to spawn at. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class TradeStationComponent : Component +{ + /// + /// The Trade Station's current hijack state. Modified by HijackBeaconSystem. + /// + [DataField, AutoNetworkedField] + public bool Hacked = false; +} diff --git a/Content.Shared/Cargo/SharedCargoSystem.cs b/Content.Shared/Cargo/SharedCargoSystem.cs index 9d044d1850..291d3abbc3 100644 --- a/Content.Shared/Cargo/SharedCargoSystem.cs +++ b/Content.Shared/Cargo/SharedCargoSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Prototypes; +using Content.Shared.HijackBeacon; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -17,6 +18,7 @@ public abstract class SharedCargoSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnHijackSuccess); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -25,6 +27,23 @@ public abstract class SharedCargoSystem : EntitySystem Dirty(ent); } + private void OnHijackSuccess(ref HijackBeaconSuccessEvent args) + { + var stationQuery = EntityQueryEnumerator(); + while (stationQuery.MoveNext(out var uid, out var comp)) + { + foreach (var (account, cash) in comp.Accounts) + { + comp.Accounts[account] = cash - args.Fine; + args.Total += args.Fine; + } + + var ev = new BankBalanceUpdatedEvent(uid, comp.Accounts); + RaiseLocalEvent(uid, ref ev, true); + Dirty(uid, comp); + } + } + /// /// For a given station, retrieves the balance in a specific account. /// diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index 3985bd3051..b290aae404 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -235,12 +235,8 @@ public sealed partial class AnchorableSystem : EntitySystem // Log anchor attempt (server only) _adminLogger.Add(LogType.Anchor, LogImpact.Low, $"{ToPrettyString(userUid):user} is trying to anchor {ToPrettyString(uid):entity} to {transform.Coordinates:targetlocation}"); - if (TryComp(uid, out var anchorBody) && - !TileFree(transform.Coordinates, anchorBody)) - { - _popup.PopupClient(Loc.GetString("anchorable-occupied"), uid, userUid); + if (!CanAnchorAt(uid, transform.Coordinates, userUid)) return; - } if (AnyUnstackable(uid, transform.Coordinates)) { @@ -285,6 +281,23 @@ public sealed partial class AnchorableSystem : EntitySystem return !attempt.Cancelled; } + public bool CanAnchorAt(Entity entity, EntityUid? user = null) + { + return CanAnchorAt(entity, Transform(entity).Coordinates, user); + } + + public bool CanAnchorAt(Entity entity, EntityCoordinates coordinates, EntityUid? user = null) + { + if (!Resolve(entity, ref entity.Comp)) + return true; + + if (TileFree(coordinates, entity.Comp)) + return true; + + _popup.PopupClient(Loc.GetString("anchorable-occupied"), entity, user); + return false; + } + /// /// Returns true if no hard anchored entities exist on the coordinate tile that would collide with the provided physics body. /// diff --git a/Content.Shared/HijackBeacon/ActiveHijackBeaconComponent.cs b/Content.Shared/HijackBeacon/ActiveHijackBeaconComponent.cs new file mode 100644 index 0000000000..b6f1d60c79 --- /dev/null +++ b/Content.Shared/HijackBeacon/ActiveHijackBeaconComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.HijackBeacon; + +/// +/// This is used for tracking a that is currently activated or on cooldown. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ActiveHijackBeaconComponent : Component +{ + /// + /// Remaining time until the hijack is completed. + /// + [DataField, AutoNetworkedField, Access(typeof(HijackBeaconSystem))] + public TimeSpan CompletionTime = TimeSpan.Zero; +} diff --git a/Content.Shared/HijackBeacon/HijackBeaconComponent.cs b/Content.Shared/HijackBeacon/HijackBeaconComponent.cs new file mode 100644 index 0000000000..ed1d3ca74b --- /dev/null +++ b/Content.Shared/HijackBeacon/HijackBeaconComponent.cs @@ -0,0 +1,53 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; + +namespace Content.Shared.HijackBeacon; + +/// +/// Component for hijack beacons, meant to be planted on the ATS to drain station funds. +/// +/// +/// Status and timer fields are private so the state machine is preserved. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class HijackBeaconComponent : Component +{ + /// + /// Current state of the beacon. + /// + [DataField, AutoNetworkedField, Access(typeof(HijackBeaconSystem))] + public HijackBeaconStatus Status = HijackBeaconStatus.AwaitActivate; + + /// + /// How long it takes to deactivate the beacon. + /// + [DataField, AutoNetworkedField, Access(typeof(HijackBeaconSystem))] + public TimeSpan DeactivationLength = TimeSpan.FromSeconds(5); + + /// + /// Remaining time until the hijack is completed. + /// + [DataField, Access(typeof(HijackBeaconSystem))] + public TimeSpan RemainingTime = TimeSpan.FromSeconds(200); + + /// + /// Default amount of time before the beacon can be re-activated, if it is disarmed. + /// + [DataField, Access(typeof(HijackBeaconSystem))] + public TimeSpan Cooldown = TimeSpan.FromSeconds(20); + + /// + /// Remaining cooldown time before the beacon can be reactivated. + /// + [DataField, AutoNetworkedField, Access(typeof(HijackBeaconSystem))] + public TimeSpan CooldownTime = TimeSpan.Zero; + + /// + /// How much cash should be withdrawn from each department account? + /// + [DataField] + public int Fine = 5000; +} diff --git a/Content.Shared/HijackBeacon/HijackBeaconSystem.cs b/Content.Shared/HijackBeacon/HijackBeaconSystem.cs new file mode 100644 index 0000000000..d3e5810fba --- /dev/null +++ b/Content.Shared/HijackBeacon/HijackBeaconSystem.cs @@ -0,0 +1,353 @@ +using Content.Shared.Cargo.Components; +using Content.Shared.Chat; +using Content.Shared.Construction.Components; +using Content.Shared.Construction.EntitySystems; +using Content.Shared.Database; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Audio; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; + +namespace Content.Shared.HijackBeacon; + +public sealed class HijackBeaconSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly AnchorableSystem _anchor = default!; + [Dependency] private readonly SharedChatSystem _chat = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public readonly SoundSpecifier AnnounceSound = new SoundPathSpecifier("/Audio/Misc/notice1.ogg"); + public readonly SoundSpecifier DeactivateSound = new SoundPathSpecifier("/Audio/Misc/notice2.ogg"); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetAltVerbs); + SubscribeLocalEvent(OnUnanchorAttempt); + SubscribeLocalEvent(OnAnchorChanged); + SubscribeLocalEvent(OnDeactivateDoAfter); + SubscribeLocalEvent(OnExaminedEvent); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var active, out var comp)) + { + switch (comp.Status) + { + case HijackBeaconStatus.Armed: + if (_gameTiming.CurTime < active.CompletionTime) + return; + + HijackFinish((uid, comp)); + Dirty(uid, comp); + break; + case HijackBeaconStatus.Cooldown: + if (comp.CooldownTime < _gameTiming.CurTime) + { + comp.Status = HijackBeaconStatus.AwaitActivate; + RemCompDeferred(uid); + Dirty(uid, comp); + } + break; + } + } + } + + #region Event Subs + + /// + /// Deactivate beacon if it gets unanchored(via a bomb or something) + /// + private void OnAnchorChanged(Entity ent, ref AnchorStateChangedEvent args) + { + // Unanchoring the beacon deactivates it. This is to prevent people from bombing the tile the beacon is on and running away with it for a free activation. + if (!args.Anchored && ent.Comp.Status == HijackBeaconStatus.Armed) + DeactivateBeacon(ent); + } + + private void OnUnanchorAttempt(Entity entity, ref UnanchorAttemptEvent args) + { + if (entity.Comp.Status == HijackBeaconStatus.Armed) + args.Cancel(); + } + + /// + /// Get the activation and deactivation verbs. + /// + private void OnGetAltVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || args.Hands is null) + return; + + if (ent.Comp.Status == HijackBeaconStatus.AwaitActivate) + { + args.Verbs.Add(new() + { + Act = () => + { + ActivateBeacon(ent); + }, + Text = Loc.GetString("hijack-beacon-verb-activate-text"), + Message = Loc.GetString("hijack-beacon-verb-activate-message"), + Disabled = ent.Comp.Status != HijackBeaconStatus.AwaitActivate || !CanActivate(ent), + TextStyleClass = "InteractionVerb", + Impact = LogImpact.High, + }); + } + + if (ent.Comp.Status == HijackBeaconStatus.Armed) + { + var user = args.User; + + args.Verbs.Add(new() + { + Act = () => + { + DeactivateBeaconDoAfter(ent, user); + }, + Text = Loc.GetString("hijack-beacon-verb-deactivate-text"), + Message = Loc.GetString("hijack-beacon-verb-deactivate-message"), + TextStyleClass = "InteractionVerb", + Impact = LogImpact.High, + }); + } + } + + /// + /// When it's examined. + /// + private void OnExaminedEvent(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + switch (ent.Comp.Status) + { + case HijackBeaconStatus.AwaitActivate: + args.PushMarkup(Loc.GetString("hijack-beacon-examine-await-activate")); + break; + case HijackBeaconStatus.Armed: + args.PushMarkup(Loc.GetString("defusable-examine-live", + ("name", ent), + ("time", GetRemainingTime(ent.Owner)))); + break; + case HijackBeaconStatus.Cooldown: + args.PushMarkup(Loc.GetString("hijack-beacon-examine-await-cooldown")); + break; + case HijackBeaconStatus.HijackComplete: + args.PushMarkup(Loc.GetString("hijack-beacon-examine-await-hijack-complete")); + break; + } + } + + /// + /// What happens when you deactivate the beacon. + /// + private void OnDeactivateDoAfter(Entity ent, ref HijackBeaconDeactivateDoAfterEvent args) + { + if (args.Handled || args.Cancelled) + return; + + DeactivateBeacon(ent); + + args.Handled = true; + } + + #endregion + + /// + /// Arming the beacon. Should only occur if on the ATS. + /// + private void ActivateBeacon(Entity ent) + { + if (ent.Comp.Status != HijackBeaconStatus.AwaitActivate || !CanActivate(ent)) + return; + + // Activate and start countdown. + // Remaining time is adjusted by current time to simplify the update loop. + EnsureComp(ent, out var activeComp); + activeComp.CompletionTime = _gameTiming.CurTime + ent.Comp.RemainingTime; + ent.Comp.Status = HijackBeaconStatus.Armed; + + //global announcement + var sender = Loc.GetString("hijack-beacon-announcement-sender"); + var message = Loc.GetString("hijack-beacon-announcement-activated", ("time", GetRemainingTime((ent.Owner, activeComp)))); + _chat.DispatchGlobalAnnouncement(message, sender, true, AnnounceSound, Color.Yellow); + + //Anchor. Anchoring is tied to activation. + Anchor(ent); + + Dirty(ent); + } + + /// + /// Deactivates the beacon. + /// + private void DeactivateBeacon(Entity ent) + { + if (ent.Comp.Status != HijackBeaconStatus.Armed) + return; + + // Put beacon on cooldown + ent.Comp.CooldownTime = ent.Comp.Cooldown + _gameTiming.CurTime; + ent.Comp.Status = HijackBeaconStatus.Cooldown; + + //global announcement + var sender = Loc.GetString("hijack-beacon-announcement-sender"); + var message = Loc.GetString("hijack-beacon-announcement-deactivated"); + _chat.DispatchGlobalAnnouncement(message, sender, true, DeactivateSound, Color.Green); + + // Unanchor. we want anchoring to be tied to activation here so we just call this. + Unanchor(ent); + + Dirty(ent); + } + + /// + /// Complete the hijack. The beacon equivalent to a nuke detonation + /// + private void HijackFinish(Entity ent) + { + if (ent.Comp.Status != HijackBeaconStatus.Armed) + return; + + // Hijack is completed and can't be reattempted + ent.Comp.Status = HijackBeaconStatus.HijackComplete; + RemCompDeferred(ent); + + var beaconXForm = Transform(ent); + + // Ensure we are on the trade station still + if (!TryComp(beaconXForm.GridUid, out var station)) + { + Log.Warning($"Trade station hijack tried to succeed on non-trade station grid {beaconXForm.GridUid}!"); + DeactivateBeacon(ent); + return; + } + + if (station.Hacked) + { + Log.Warning("Hack succeeded on an already hacked trade station!"); + return; + } + + // Mark the station as hacked. + station.Hacked = true; + Dirty(beaconXForm.GridUid.Value, station); + + // Broadcast that the ATS has been hacked. + var ev = new HijackBeaconSuccessEvent(ent.Comp.Fine); + RaiseLocalEvent(ref ev); + + //global announcement + var sender = Loc.GetString("hijack-beacon-announcement-sender"); + var message = Loc.GetString("hijack-beacon-announcement-success", ("fine", ev.Total)); + _chat.DispatchGlobalAnnouncement(message, sender, true, AnnounceSound, Color.Red); + + // Unanchoring must occur after updating the status, or it will disarm the beacon + Unanchor(ent, beaconXForm); + + Dirty(ent); + } + + /// + /// Starts the deactivation doafter. + /// + private void DeactivateBeaconDoAfter(Entity beacon, EntityUid user) + { + var doAfter = new DoAfterArgs(EntityManager, user, beacon.Comp.DeactivationLength, new HijackBeaconDeactivateDoAfterEvent(), beacon) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = true, + }; + + // (try to) start doafter + _doAfter.TryStartDoAfter(doAfter); + } + + #region Helpers + + /// + /// Check if the beacon is on the Trade Station and if the Trade Station has not been hijacked already. + /// + private bool CanActivate(Entity ent) + { + return TryComp(Transform(ent).GridUid, out TradeStationComponent? tradeStation) && !tradeStation.Hacked && _anchor.CanAnchorAt(ent.Owner); + } + + /// + /// Anchoring helper + /// + private void Anchor(Entity ent, TransformComponent? beaconXForm = null) + { + beaconXForm ??= Transform(ent); + + if (beaconXForm.Anchored || beaconXForm.GridUid == null) + return; + + _transform.AnchorEntity(ent, beaconXForm); + _popup.PopupPredicted(Loc.GetString("hijack-beacon-popup-anchor"), ent, null); + } + + /// + /// Unanchoring helper + /// + private void Unanchor(Entity ent, TransformComponent? beaconXForm = null) + { + beaconXForm ??= Transform(ent); + + if (!beaconXForm.Anchored) + return; + + _transform.Unanchor(ent, beaconXForm); + _popup.PopupPredicted(Loc.GetString("hijack-beacon-popup-unanchor"), ent, null); + } + + /// + /// Turns time values into usable values for announcements/examine messages + /// + private int GetRemainingTime(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return 69420; // Mature error code + + return (int) (ent.Comp.CompletionTime - _gameTiming.CurTime).TotalSeconds; + } + + #endregion +} + +public enum HijackBeaconStatus : byte +{ + AwaitActivate, + Armed, + Cooldown, + HijackComplete +} + +/// +/// DoAfter event raised when the hijack beacon is deactivated. +/// +[Serializable, NetSerializable] +public sealed partial class HijackBeaconDeactivateDoAfterEvent : SimpleDoAfterEvent; + +/// +/// Event raised when the hijack beacon succeeds in hijacking the ATS. +/// +[ByRefEvent] +public record struct HijackBeaconSuccessEvent(int Fine) +{ + public int Total = 0; +}; diff --git a/Resources/Locale/en-US/hijack-beacon/hijack-beacon.ftl b/Resources/Locale/en-US/hijack-beacon/hijack-beacon.ftl new file mode 100644 index 0000000000..a98370e891 --- /dev/null +++ b/Resources/Locale/en-US/hijack-beacon/hijack-beacon.ftl @@ -0,0 +1,16 @@ +hijack-beacon-announcement-sender = Automated Trade Station +hijack-beacon-announcement-activated = Attention! An Attempted Breach of the Automated Trade Station's firewall has been detected! Estimated {$time} seconds until firewall breach! +hijack-beacon-announcement-deactivated = Firewall breach failed. Firewall integrity partially restored. Have a nice day! +hijack-beacon-announcement-success = Successfully disengaged Automated Trade Station firewall. {$fine} spessos has been transferred from station funds to [%ERROR%]. Your trade station warranty is now void. This incident has been reported. + +hijack-beacon-examine-await-activate = The beacon is [color=green]ready to activate[/color]. +hijack-beacon-examine-await-cooldown = The beacon is [color=red]on cooldown[/color]. +hijack-beacon-examine-await-hijack-complete = The beacon is [color=red]spent[/color]. + +hijack-beacon-popup-anchor = The beacon anchors itself into the ground! +hijack-beacon-popup-unanchor = The beacon unanchors itself from the ground. + +hijack-beacon-verb-activate-text = Activate +hijack-beacon-verb-activate-message = The beacon can only be armed on the Automated Trade Station, on an unoccupied tile. +hijack-beacon-verb-deactivate-text = Deactivate +hijack-beacon-verb-deactivate-message = The beacon isn't going to deactivate itself, you know. diff --git a/Resources/Locale/en-US/store/categories.ftl b/Resources/Locale/en-US/store/categories.ftl index 4469a576cd..9ecf4f91d2 100644 --- a/Resources/Locale/en-US/store/categories.ftl +++ b/Resources/Locale/en-US/store/categories.ftl @@ -12,6 +12,7 @@ store-category-allies = Allies store-category-job = Job store-category-wearables = Wearables store-category-pointless = Pointless +store-category-objective = Objective store-discounted-items = Discounts # Revenant diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 4f67fae1cd..685a4d83dd 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -520,3 +520,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/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index e232308d01..98c2206f6a 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -2268,3 +2268,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/Entities/Objects/Tools/hijack_beacon.yml b/Resources/Prototypes/Entities/Objects/Tools/hijack_beacon.yml new file mode 100644 index 0000000000..d15b845322 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Tools/hijack_beacon.yml @@ -0,0 +1,34 @@ +- type: entity + parent: [ BaseItem, BaseSyndicateContraband ] + id: HijackBeacon + name: hijack beacon + description: A device that bypasses the firewall on Nanotrasen-brand Automated Trade Stations. + components: + - type: Transform + anchored: false + noRot: true + - type: Physics + bodyType: Dynamic + - type: Anchorable + - type: Item + size: Normal + - type: HijackBeacon + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.3,-0.3,0.3,0.3" + density: 190 + mask: + - MachineMask + layer: + - MachineLayer + - type: Clickable + - type: InteractionOutline + - type: Appearance + - type: Sprite + sprite: Objects/Tools/hijack_beacon.rsi + noRot: true + layers: + - state: extraction_point diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index 9aa13fa74a..bbdd6fa236 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -6,6 +6,7 @@ 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 + TraitorObjectiveGroupOther: 1 - type: weightedRandom id: TraitorObjectiveGroupSteal @@ -43,6 +44,13 @@ # RandomTraitorAliveObjective: 1 - Removed because the objective was boring and didn't progress the round. RandomTraitorProgressObjective: 1 +#misc objectives that don't fall other another category +- type: weightedRandom + id: TraitorObjectiveGroupOther + weights: + HijackTradeStationObjective: 1 + + #Thief groups - type: weightedRandom id: ThiefObjectiveGroups diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index 4825fa4565..9ffc0146a2 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -352,3 +352,20 @@ - type: StealCondition stealGroup: NukeDisk owner: objective-condition-steal-station + +# 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 diff --git a/Resources/Prototypes/Store/categories.yml b/Resources/Prototypes/Store/categories.yml index ccdb349d9c..a4c339d0e7 100644 --- a/Resources/Prototypes/Store/categories.yml +++ b/Resources/Prototypes/Store/categories.yml @@ -89,6 +89,11 @@ name: store-category-pointless priority: 10 +- type: storeCategory + id: UplinkObjective + name: store-category-objective + priority: 11 + #nukie delivery - type: storeCategory diff --git a/Resources/Prototypes/Store/presets.yml b/Resources/Prototypes/Store/presets.yml index 37329d4096..13823a40f1 100644 --- a/Resources/Prototypes/Store/presets.yml +++ b/Resources/Prototypes/Store/presets.yml @@ -16,6 +16,7 @@ - UplinkWearables - UplinkJob - UplinkPointless + - UplinkObjective currencyWhitelist: - Telecrystal balance: 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 0000000000000000000000000000000000000000..7b8dd4c3afea906e37a37e17e2dd2abc21bb8125 GIT binary patch literal 1389 zcmeAS@N?(olHy`uVBq!ia0vp^3P9|@!3HF&`%2d_Ffgvk42dX-@b$4u&d=3LOvz75 z)vL%Y0Ln8k*w|MTBqnF4mMA2prf25aD!t#mUr8Y|#a1cY)Yrhbz&SM|)1#^=HMq(z zB)KX(*)m1R-j2(r!U||WZfZ%QLPc&)Ua?h$trFN=DP$9xM zK*2e`C{@8!&rCPj(8NN)+)~fb%*4RhOh>`Uz|d0Pz(U{9Sl7VN%D~LZ)La1ylHCStb$zJpq2r7wn`Z#B?VUc`sL;2dgaD?`9npUS|%T;u8D7?RQWHp1IqI8fwY*33(MwF`xOqnQ_) zHsv-+m>8-w8D2cX$}O%JvHMD|b$!b}p0af&M_Lsboe#Awk(j+;8>@1NP@HVkt%c4j zEz9$~xi6P_ExT(GUsU{j-~Q@zw##kV)&5mS^oTgz5nu9G@1xUwRR;ACr_TMSC7(xd|&qOAC;I+Fy469y01H``<7#ZzyX={zbp1Xi*ys( zP_Xgx+4PG0Jo(&TujHyU3SGDCteW-R_w%+x$8UQ*Xt6iDRQ_pe)qR!x(=~r&dTLs( zvKZ~YYv!P*vU-ifcn?_Q#J;JT3Cs^X0>fW88M) zDvk~Aff2D5caoD^vOH^k{^1PvpZDR;Q3dDr%g(9Y%kfep4+dqS>Ti~-eyOs}c{~5>*Y3TI)z|XA zMC$xlutLo>zu}84UwfqW?fd;sHhdOW?zgf#imO!Hr%zz1V&5p!Yc|EfIsDJsbdKUi z&TqE$+9%dDJos7_x$r)-l4~-HX1vR{_xcr2?%$Z!CndMWn<+eIcf+Rc+IQby8tl3l zX&ozrH8z@7{S&y)b9uCQ<9z@*Wnk9u`NvCLJpCe3?`C tVCkGdbrHYkvwkvy4VZP{4F47OhWc)GBe(dUstKS{($m$?Wt~$(69BAKJ81v_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tools/hijack_beacon.rsi/meta.json b/Resources/Textures/Objects/Tools/hijack_beacon.rsi/meta.json new file mode 100644 index 0000000000..5177acf42f --- /dev/null +++ b/Resources/Textures/Objects/Tools/hijack_beacon.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/austation/austation/commit/e2a4fefd01e702f48d3d4cc8d6a2686d54d104fa and edited by TheShuEd. Recolored by alexalexmax", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "extraction_point", + "delays": [ + [ + 0.5, + 0.5 + ] + ] + } + ] +} From ecf554549ca70db784ea88dc8af0147f0b793caa Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 3 Apr 2026 10:37:05 +0000 Subject: [PATCH 042/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f6d5385c12..c251692f5c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4030,3 +4023,13 @@ 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 From f688bfc7713126274df5ac1620de079f2c973946 Mon Sep 17 00:00:00 2001 From: Kyle Tyo Date: Fri, 3 Apr 2026 07:58:47 -0400 Subject: [PATCH 043/247] Remove suitstep1 and suitstep2 ogg files. (#43441) commit --- Resources/Audio/Effects/Footsteps/suitstep1.ogg | Bin 10862 -> 0 bytes Resources/Audio/Effects/Footsteps/suitstep2.ogg | Bin 11896 -> 0 bytes .../Prototypes/SoundCollections/footsteps.yml | 6 ------ 3 files changed, 6 deletions(-) delete mode 100644 Resources/Audio/Effects/Footsteps/suitstep1.ogg delete mode 100644 Resources/Audio/Effects/Footsteps/suitstep2.ogg diff --git a/Resources/Audio/Effects/Footsteps/suitstep1.ogg b/Resources/Audio/Effects/Footsteps/suitstep1.ogg deleted file mode 100644 index 8de74124907dc38bf56017d23145ecd2e4a51df4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10862 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 diff --git a/Resources/Audio/Effects/Footsteps/suitstep2.ogg b/Resources/Audio/Effects/Footsteps/suitstep2.ogg deleted file mode 100644 index 56396fb71eef845fd1105c7dd0bf287fadf6aca9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11896 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 OSJ(8DM}jp90R9g${3?F{ diff --git a/Resources/Prototypes/SoundCollections/footsteps.yml b/Resources/Prototypes/SoundCollections/footsteps.yml index 26ecee5269..8c619226ff 100644 --- a/Resources/Prototypes/SoundCollections/footsteps.yml +++ b/Resources/Prototypes/SoundCollections/footsteps.yml @@ -94,12 +94,6 @@ files: - /Audio/Items/Toys/weh.ogg -- type: soundCollection - id: FootstepHeavy - files: - - /Audio/Effects/Footsteps/suitstep1.ogg - - /Audio/Effects/Footsteps/suitstep2.ogg - - type: soundCollection id: FootstepAsteroid files: From 5e456146e31f4174fc9392cf4bda410606602222 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Fri, 3 Apr 2026 19:16:06 +0200 Subject: [PATCH 044/247] fix Changeling transformation making items in slime storage inaccessible (#43393) * fix * fix identity update --- .../Changeling/ChangelingTransformEvents.cs | 23 ++++++++ .../Systems/ChangelingTransformSystem.cs | 58 +++++++++++++------ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/Content.Shared/Changeling/ChangelingTransformEvents.cs b/Content.Shared/Changeling/ChangelingTransformEvents.cs index 9940a60705..9a4ff4454e 100644 --- a/Content.Shared/Changeling/ChangelingTransformEvents.cs +++ b/Content.Shared/Changeling/ChangelingTransformEvents.cs @@ -14,3 +14,26 @@ public sealed partial class ChangelingTransformActionEvent : InstantActionEvent; /// [Serializable, NetSerializable] public sealed partial class ChangelingTransformDoAfterEvent : SimpleDoAfterEvent; + +/// +/// Raised on a changeling before they transform into a stored identity. +/// This is raised after the DoAfter finished. +/// +public readonly record struct BeforeChangelingTransformEvent(EntityUid StoredIdentity) +{ + /// + /// The stored identity the changeling will transform into. + /// + public readonly EntityUid StoredIdentity = StoredIdentity; +}; + +/// +/// Raised on a changeling after they successfully transformed into a stored identity. +/// +public readonly record struct AfterChangelingTransformEvent(EntityUid StoredIdentity) +{ + /// + /// The stored identity the changeling transformed into. + /// + public readonly EntityUid StoredIdentity = StoredIdentity; +}; diff --git a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs index 9e4b1d4d03..5f9cc9526b 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs @@ -5,10 +5,11 @@ using Content.Shared.Changeling.Components; using Content.Shared.Cloning; using Content.Shared.Database; using Content.Shared.DoAfter; -using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; using Content.Shared.Popups; +using Content.Shared.Storage; using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; using Robust.Shared.Network; using Robust.Shared.Prototypes; @@ -17,16 +18,18 @@ 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!; private const string ChangelingBuiXmlGeneratedName = "ChangelingTransformBoundUserInterface"; public override void Initialize() @@ -38,21 +41,24 @@ public sealed partial class ChangelingTransformSystem : EntitySystem SubscribeLocalEvent(OnSuccessfulTransform); SubscribeLocalEvent(OnTransformSelected); 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,9 +71,9 @@ public sealed partial class ChangelingTransformSystem : EntitySystem if (!TryComp(ent, out var userIdentity)) return; - if (!_uiSystem.IsUiOpen((ent, userInterfaceComp), ChangelingTransformUiKey.Key, args.Performer)) + if (!_ui.IsUiOpen((ent, userInterfaceComp), ChangelingTransformUiKey.Key, args.Performer)) { - _uiSystem.OpenUi((ent, userInterfaceComp), ChangelingTransformUiKey.Key, args.Performer); + _ui.OpenUi((ent, userInterfaceComp), ChangelingTransformUiKey.Key, args.Performer); } //TODO: Can add a Else here with TransformInto and CloseUI to make a quick switch, // issue right now is that Radials cover the Action buttons so clicking the action closes the UI (due to clicking off a radial causing it to close, even with UI) // but pressing the number does. @@ -75,7 +81,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem /// /// 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 +91,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem var selfMessage = Loc.GetString("changeling-transform-attempt-self", ("user", Identity.Entity(ent.Owner, EntityManager))); var othersMessage = Loc.GetString("changeling-transform-attempt-others", ("user", Identity.Entity(ent.Owner, EntityManager))); - _popupSystem.PopupPredicted( + _popup.PopupPredicted( selfMessage, othersMessage, ent, @@ -100,7 +106,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem 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, @@ -119,7 +125,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem private void OnTransformSelected(Entity ent, ref ChangelingTransformIdentitySelectMessage args) { - _uiSystem.CloseUi(ent.Owner, ChangelingTransformUiKey.Key, ent); + _ui.CloseUi(ent.Owner, ChangelingTransformUiKey.Key, ent); if (!TryGetEntity(args.TargetIdentity, out var targetIdentity)) return; @@ -153,14 +159,18 @@ public sealed partial class ChangelingTransformSystem : EntitySystem if (args.Target is not { } targetIdentity) return; + var beforeTransformEvent = new BeforeChangelingTransformEvent(targetIdentity); + RaiseLocalEvent(args.User, beforeTransformEvent); + _visualBody.CopyAppearanceFrom(targetIdentity, args.User); - _cloningSystem.CloneComponents(targetIdentity, args.User, settings); + _cloning.CloneComponents(targetIdentity, args.User, settings); if (TryComp(targetIdentity, out var storedIdentity) && storedIdentity.OriginalSession != null) _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(ent.Owner):player} successfully transformed into \"{Name(targetIdentity)}\" ({storedIdentity.OriginalSession:player})"); else _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(ent.Owner):player} successfully transformed into \"{Name(targetIdentity)}\""); - _metaSystem.SetEntityName(ent, Name(targetIdentity), raiseEvents: false); + _metaData.SetEntityName(ent, Name(targetIdentity), raiseEvents: false); // Don't raise events because we don't want to rename the ID card. + _identity.QueueIdentityUpdate(ent); // We have to manually refresh the identity because we did not raise events. Dirty(ent); @@ -169,5 +179,17 @@ public sealed partial class ChangelingTransformSystem : EntitySystem identity.CurrentIdentity = targetIdentity; Dirty(ent.Owner, identity); } + + var afterTransformEvent = new AfterChangelingTransformEvent(targetIdentity); + RaiseLocalEvent(args.User, afterTransformEvent); + } + + private void StorageBeforeTransform(Entity ent, ref BeforeChangelingTransformEvent args) + { + if (HasComp(args.StoredIdentity)) + return; // If we have a storage component and the target has one as well, then do nothing. + + // If the target identity does not have a storage anymore, drop all items inside our storage so that they don't become unreachable. + _container.EmptyContainer(ent.Comp.Container); } } From 7234cec825014418fb5d25b7aec84972d4324ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ph=C3=B6nix?= Date: Fri, 3 Apr 2026 20:26:18 +0300 Subject: [PATCH 045/247] Predict ghost examine (#43150) * never * forgot --- Content.Server/Ghost/GhostSystem.cs | 17 ++--------------- Content.Shared/Ghost/GhostComponent.cs | 2 +- Content.Shared/Ghost/SharedGhostSystem.cs | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 964ba99ac3..d986c83a54 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -12,7 +12,6 @@ using Content.Shared.Damage.Components; using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Systems; using Content.Shared.Database; -using Content.Shared.Examine; using Content.Shared.Eye; using Content.Shared.FixedPoint; using Content.Shared.Follower; @@ -38,7 +37,6 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Timing; namespace Content.Server.Ghost { @@ -48,7 +46,6 @@ namespace Content.Server.Ghost [Dependency] private readonly IAdminLogManager _adminLog = default!; [Dependency] private readonly SharedEyeSystem _eye = default!; [Dependency] private readonly FollowerSystem _followerSystem = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly JobSystem _jobs = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly MindSystem _minds = default!; @@ -88,8 +85,6 @@ namespace Content.Server.Ghost SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnGhostShutdown); - SubscribeLocalEvent(OnGhostExamine); - SubscribeLocalEvent(OnMindRemovedMessage); SubscribeLocalEvent(OnMindUnvisitedMessage); SubscribeLocalEvent(OnPlayerDetached); @@ -207,6 +202,8 @@ namespace Content.Server.Ghost _eye.RefreshVisibilityMask(uid); var time = _gameTiming.RealTime; component.TimeOfDeath = time; + + Dirty(uid, component); } private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentShutdown args) @@ -237,16 +234,6 @@ namespace Content.Server.Ghost _actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction); } - private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args) - { - var timeSinceDeath = _gameTiming.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.Shared/Ghost/GhostComponent.cs b/Content.Shared/Ghost/GhostComponent.cs index cd5aa9be22..37f5c9e693 100644 --- a/Content.Shared/Ghost/GhostComponent.cs +++ b/Content.Shared/Ghost/GhostComponent.cs @@ -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] + [DataField, AutoNetworkedField] public TimeSpan TimeOfDeath = TimeSpan.Zero; /// diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index 7d3561a79f..726c20d31a 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -1,9 +1,11 @@ using Content.Shared.Emoting; +using Content.Shared.Examine; using Content.Shared.Hands; using Content.Shared.Interaction.Events; using Content.Shared.Item; using Content.Shared.Popups; using Robust.Shared.Serialization; +using Robust.Shared.Timing; namespace Content.Shared.Ghost { @@ -14,6 +16,7 @@ namespace Content.Shared.Ghost public abstract class SharedGhostSystem : EntitySystem { [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] protected readonly IGameTiming _gameTiming = default!; public override void Initialize() { @@ -23,6 +26,17 @@ namespace Content.Shared.Ghost SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); SubscribeLocalEvent(OnAttempt); + SubscribeLocalEvent(OnGhostExamine); + } + + private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args) + { + var timeSinceDeath = _gameTiming.RealTime.Subtract(component.TimeOfDeath); + var deathTimeInfo = timeSinceDeath.Minutes > 0 + ? Loc.GetString("comp-ghost-examine-time-minutes", ("minutes", timeSinceDeath.Minutes)) + : Loc.GetString("comp-ghost-examine-time-seconds", ("seconds", timeSinceDeath.Seconds)); + + args.PushMarkup(deathTimeInfo); } private void OnAttemptInteract(Entity ent, ref InteractionAttemptEvent args) From 088dbb2d38e7db3f779311f2ca8636440f2e0262 Mon Sep 17 00:00:00 2001 From: Minemoder5000 Date: Fri, 3 Apr 2026 11:46:20 -0600 Subject: [PATCH 046/247] Add blacklist to entitystoragecomponent, add blacklist to genpop lockers (#41633) * cleanup EntityStorageSystem * add blacklist to entitystoragecomponent, add blacklist to genpop lockers * remove test whitelist tag * add documentation * Borger * remove blacklist from genpop closet, fix code comment --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Storage/Components/EntityStorageComponent.cs | 16 +++++++++++++++- .../EntitySystems/SharedEntityStorageSystem.cs | 8 ++------ .../Storage/Closets/Lockers/lockers.yml | 1 + 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Storage/Components/EntityStorageComponent.cs b/Content.Shared/Storage/Components/EntityStorageComponent.cs index ecfcccc45b..ba267aa5bf 100644 --- a/Content.Shared/Storage/Components/EntityStorageComponent.cs +++ b/Content.Shared/Storage/Components/EntityStorageComponent.cs @@ -131,7 +131,21 @@ public sealed partial class EntityStorageComponent : Component, IGasMixtureHolde /// standard requirement that the entity must be an item or mob is waived. /// [DataField] - public EntityWhitelist? Whitelist; + public EntityWhitelist? Whitelist = new() + { + Components = + [ + "MobState", + "Item", + ], + }; + + /// + /// Blacklist for what entities are not allowed to be inserted into this container. + /// Blacklist takes priority over whitelist. + /// + [DataField] + public EntityWhitelist? Blacklist; /// /// The contents of the storage. diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs index 93a534843a..9618ee900b 100644 --- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs @@ -377,12 +377,8 @@ public abstract class SharedEntityStorageSystem : EntitySystem if (containerAttemptEvent.Cancelled) return false; - // Consult the whitelist. The whitelist ignores the default assumption about how entity storage works. - if (component.Whitelist != null) - return _whitelistSystem.IsValid(component.Whitelist, toInsert); - - // The inserted entity must be a mob or an item. - return HasComp(toInsert) || HasComp(toInsert); + // Check the whitelist/blacklist. + return _whitelistSystem.CheckBoth(toInsert, component.Blacklist, component.Whitelist); } public bool TryOpenStorage(EntityUid user, EntityUid target, bool silent = false) diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml index 8f22f8d68c..1410eb4e1c 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml @@ -454,6 +454,7 @@ hard: True restitution: 0 friction: 0.4 + - type: EntityStorage - type: entity parent: LockerPrisoner From f0cab2d6ed42040f04242e5dde3d02646f70c0ee Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 3 Apr 2026 18:02:20 +0000 Subject: [PATCH 047/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c251692f5c..4b1d22ee97 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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 @@ -4033,3 +4025,10 @@ 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 From 6c9e10e1a9ef8c25904d35ae8e0f92dece76683f Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:47:49 -0700 Subject: [PATCH 048/247] Removed JuicePotato reagent (#43448) No more potatoes Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Entities/Objects/Consumable/Food/produce.yml | 5 ----- Resources/Prototypes/Hydroponics/randomChemicals.yml | 1 - Resources/Prototypes/Reagents/Consumable/Drink/juice.yml | 9 --------- 3 files changed, 15 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index 2e52646dac..118c008291 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -883,11 +883,6 @@ heldPrefix: produce - type: Produce seedId: potato - - type: Extractable - juiceSolution: - reagents: - - ReagentId: JuicePotato - Quantity: 10 - type: Tag tags: - Potato diff --git a/Resources/Prototypes/Hydroponics/randomChemicals.yml b/Resources/Prototypes/Hydroponics/randomChemicals.yml index 3b8d225c36..b3468c0848 100644 --- a/Resources/Prototypes/Hydroponics/randomChemicals.yml +++ b/Resources/Prototypes/Hydroponics/randomChemicals.yml @@ -114,7 +114,6 @@ - SodaWater - Ice - JuiceCarrot - - JuicePotato - Protein - Flour - Soysauce diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml b/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml index 43bd9efedc..e22993d0a3 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 From 417f1b7ceb3414580d1f219ecb8663a097d73700 Mon Sep 17 00:00:00 2001 From: Schuyler Duryee Date: Fri, 3 Apr 2026 10:48:07 -0700 Subject: [PATCH 049/247] Add "failed to load crew manifest" message (#43400) --- .../Cartridges/CrewManifestCartridgeSystem.cs | 7 +++++++ Resources/Locale/en-US/cartridge-loader/cartridges.ftl | 1 + 2 files changed, 8 insertions(+) diff --git a/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs index b1b23b2732..e7687ec3df 100644 --- a/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs +++ b/Content.Server/CartridgeLoader/Cartridges/CrewManifestCartridgeSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Station.Systems; using Content.Shared.CartridgeLoader; using Content.Shared.CartridgeLoader.Cartridges; using Content.Shared.CCVar; +using Content.Shared.CrewManifest; using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.Prototypes; @@ -60,7 +61,13 @@ public sealed class CrewManifestCartridgeSystem : EntitySystem var owningStation = _stationSystem.GetOwningStation(uid); if (owningStation is null) + { + // Display "loading failed" message + var failureMessage = Loc.GetString("crew-manifest-cartridge-loading-failed"); + var failureState = new CrewManifestUiState(failureMessage, new CrewManifestEntries()); + _cartridgeLoader.UpdateCartridgeUiState(loaderUid, failureState); return; + } var (stationName, entries) = _crewManifest.GetCrewManifest(owningStation.Value); diff --git a/Resources/Locale/en-US/cartridge-loader/cartridges.ftl b/Resources/Locale/en-US/cartridge-loader/cartridges.ftl index 11b2fb9402..7dd7c779ac 100644 --- a/Resources/Locale/en-US/cartridge-loader/cartridges.ftl +++ b/Resources/Locale/en-US/cartridge-loader/cartridges.ftl @@ -7,6 +7,7 @@ news-read-program-name = Station news crew-manifest-program-name = Crew manifest crew-manifest-cartridge-loading = Loading ... +crew-manifest-cartridge-loading-failed = Failed to load crew manifest! net-probe-program-name = NetProbe net-probe-scan = Scanned {$device}! From f6fc3edcf7fcc62d6563e7a6f482b5b1e2be96d5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 3 Apr 2026 18:18:04 +0000 Subject: [PATCH 050/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4b1d22ee97..a18eb6fa29 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4032,3 +4024,10 @@ 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 From 7c60ea97e4a4166159185d845b05abc862881a9a Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Fri, 3 Apr 2026 20:16:17 +0200 Subject: [PATCH 051/247] Fix banana creampie visuals, allow station AI to be pied (#43388) * refactor and fixes * fix ling transformation * small fix * Update Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --------- Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> --- .../Nutrition/EntitySystems/CreamPieSystem.cs | 66 +++++++ .../EntitySystems/CreamPiedSystem.cs | 10 - .../Systems/AdminVerbSystem.Smites.cs | 2 +- .../Nutrition/EntitySystems/CreamPieSystem.cs | 102 +--------- .../Effects/WashCreamPieEntityEffectSystem.cs | 2 +- .../Nutrition/Components/CreamPieComponent.cs | 44 +++-- .../Components/CreamPiedComponent.cs | 55 ++++-- .../EntitySystems/SharedCreamPieSystem.cs | 184 +++++++++++++----- .../Prototypes/Body/Species/arachnid.yml | 4 + Resources/Prototypes/Body/Species/moth.yml | 4 + .../Prototypes/Body/Species/reptilian.yml | 4 + Resources/Prototypes/Body/Species/vox.yml | 4 + .../Prototypes/Body/Species/vulpkanin.yml | 4 + .../Prototypes/Body/species_appearance.yml | 5 - Resources/Prototypes/Body/species_base.yml | 9 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 24 +-- .../Prototypes/Entities/Mobs/Player/clone.yml | 1 + .../Entities/Mobs/Player/silicon.yml | 10 + 18 files changed, 324 insertions(+), 210 deletions(-) create mode 100644 Content.Client/Nutrition/EntitySystems/CreamPieSystem.cs delete mode 100644 Content.Client/Nutrition/EntitySystems/CreamPiedSystem.cs diff --git a/Content.Client/Nutrition/EntitySystems/CreamPieSystem.cs b/Content.Client/Nutrition/EntitySystems/CreamPieSystem.cs new file mode 100644 index 0000000000..96a63cc694 --- /dev/null +++ b/Content.Client/Nutrition/EntitySystems/CreamPieSystem.cs @@ -0,0 +1,66 @@ +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Robust.Client.GameObjects; + +namespace Content.Client.Nutrition.EntitySystems; + +public sealed class CreamPieSystem : SharedCreamPieSystem +{ + [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly AppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnComponentShutdown); + SubscribeLocalEvent(OnAppearanceChange); + SubscribeLocalEvent(OnAfterAutoHandleState); + } + + private void OnComponentInit(Entity ent, ref ComponentInit args) + { + UpdateAppearance(ent); + } + + private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) + { + _sprite.RemoveLayer(ent.Owner, CreamPiedVisualLayer.Key); + } + + private void OnAppearanceChange(Entity ent, ref AppearanceChangeEvent args) + { + UpdateAppearance((ent.Owner, ent.Comp, args.Sprite, args.Component)); + } + + private void OnAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + // Update when the sprite datafield is changed so that changelings can transform properly. + UpdateAppearance(ent); + } + + private void UpdateAppearance(Entity ent) + { + if (!Resolve(ent, ref ent.Comp2, false) || !Resolve(ent, ref ent.Comp3, false)) + return; + + var creamPied = ent.Comp1; + var sprite = ent.Comp2; + var appearance = ent.Comp3; + + // If there is no sprite to use, remove the layer. Otherwise ensure that it exists and set the visuals accordingly. + int index; + if (creamPied.Sprite == null) + { + _sprite.RemoveLayer((ent.Owner, sprite), CreamPiedVisualLayer.Key); + return; + } + + index = _sprite.LayerMapReserve((ent.Owner, sprite), CreamPiedVisualLayer.Key); + + _appearance.TryGetData(ent.Owner, CreamPiedVisuals.Creamed, out var isCreamPied, appearance); + _sprite.LayerSetSprite((ent.Owner, sprite), index, creamPied.Sprite); + _sprite.LayerSetVisible((ent.Owner, sprite), index, isCreamPied); + } +} diff --git a/Content.Client/Nutrition/EntitySystems/CreamPiedSystem.cs b/Content.Client/Nutrition/EntitySystems/CreamPiedSystem.cs deleted file mode 100644 index 24632b71fa..0000000000 --- a/Content.Client/Nutrition/EntitySystems/CreamPiedSystem.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Content.Shared.Nutrition.EntitySystems; -using JetBrains.Annotations; - -namespace Content.Client.Nutrition.EntitySystems -{ - [UsedImplicitly] - public sealed class CreamPiedSystem : SharedCreamPieSystem - { - } -} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 540bd7d089..63b4b5646b 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -279,7 +279,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Consumable/Food/Baked/pie.rsi"), "plain-slice"), Act = () => { - _creamPieSystem.SetCreamPied(args.Target, creamPied, true); + _creamPieSystem.SetCreamPied((args.Target, creamPied), true); }, Impact = LogImpact.Extreme, Message = string.Join(": ", creamPieName, Loc.GetString("admin-smite-creampie-description")) diff --git a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs index 33b619732d..202d03c6ea 100644 --- a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs @@ -1,103 +1,5 @@ -using Content.Server.Fluids.EntitySystems; -using Content.Server.Nutrition.Components; -using Content.Server.Popups; -using Content.Shared.Containers.ItemSlots; -using Content.Shared.IdentityManagement; -using Content.Shared.Nutrition; -using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.Rejuvenate; -using Content.Shared.Throwing; -using Content.Shared.Trigger.Components; -using Content.Shared.Trigger.Systems; -using Content.Shared.Chemistry.EntitySystems; -using JetBrains.Annotations; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -namespace Content.Server.Nutrition.EntitySystems -{ - [UsedImplicitly] - public sealed class CreamPieSystem : SharedCreamPieSystem - { - [Dependency] private readonly IngestionSystem _ingestion = default!; - [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly PuddleSystem _puddle = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutions = default!; - [Dependency] private readonly TriggerSystem _trigger = default!; +namespace Content.Server.Nutrition.EntitySystems; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSlice); - - SubscribeLocalEvent(OnRejuvenate); - } - - protected override void SplattedCreamPie(Entity entity) - { - // The entity is deleted, so play the sound at its position rather than parenting - var coordinates = Transform(entity).Coordinates; - _audio.PlayPvs(_audio.ResolveSound(entity.Comp1.Sound), coordinates, AudioParams.Default.WithVariation(0.125f)); - - if (Resolve(entity, ref entity.Comp2, false)) - { - if (_solutions.TryGetSolution(entity.Owner, entity.Comp2.Solution, out _, out var solution)) - _puddle.TrySpillAt(entity.Owner, solution, out _, false); - - _ingestion.SpawnTrash((entity, entity.Comp2)); - } - - ActivatePayload(entity); - - QueueDel(entity); - } - - // TODO - // A regression occured here. Previously creampies would activate their hidden payload if you tried to eat them. - // However, the refactor to IngestionSystem caused the event to not be reached, - // because eating is blocked if an item is inside the food. - - private void OnSlice(Entity entity, ref SliceFoodEvent args) - { - ActivatePayload(entity); - } - - private void ActivatePayload(EntityUid uid) - { - if (_itemSlots.TryGetSlot(uid, CreamPieComponent.PayloadSlotName, out var itemSlot)) - { - if (_itemSlots.TryEject(uid, itemSlot, user: null, out var item)) - { - if (TryComp(item.Value, out var timerTrigger)) - { - _trigger.ActivateTimerTrigger((item.Value, timerTrigger)); - } - } - } - } - - protected override void CreamedEntity(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) - { - _popup.PopupEntity(Loc.GetString("cream-pied-component-on-hit-by-message", - ("thrown", Identity.Entity(args.Thrown, EntityManager))), - uid, args.Target); - - var otherPlayers = Filter.PvsExcept(uid); - - _popup.PopupEntity(Loc.GetString("cream-pied-component-on-hit-by-message-others", - ("owner", Identity.Entity(uid, EntityManager)), - ("thrown", Identity.Entity(args.Thrown, EntityManager))), - uid, otherPlayers, false); - } - - private void OnRejuvenate(Entity entity, ref RejuvenateEvent args) - { - SetCreamPied(entity, entity.Comp, false); - } - } -} +public sealed class CreamPieSystem : SharedCreamPieSystem; diff --git a/Content.Shared/EntityEffects/Effects/WashCreamPieEntityEffectSystem.cs b/Content.Shared/EntityEffects/Effects/WashCreamPieEntityEffectSystem.cs index c54912191e..9b3d731fcc 100644 --- a/Content.Shared/EntityEffects/Effects/WashCreamPieEntityEffectSystem.cs +++ b/Content.Shared/EntityEffects/Effects/WashCreamPieEntityEffectSystem.cs @@ -15,7 +15,7 @@ public sealed partial class WashCreamPieEntityEffectSystem : EntityEffectSystem< protected override void Effect(Entity entity, ref EntityEffectEvent args) { - _creamPie.SetCreamPied(entity, entity.Comp, false); + _creamPie.SetCreamPied((entity, entity.Comp), false); } } diff --git a/Content.Shared/Nutrition/Components/CreamPieComponent.cs b/Content.Shared/Nutrition/Components/CreamPieComponent.cs index b6bd124084..91ba3b3d6e 100644 --- a/Content.Shared/Nutrition/Components/CreamPieComponent.cs +++ b/Content.Shared/Nutrition/Components/CreamPieComponent.cs @@ -1,21 +1,39 @@ using Content.Shared.Nutrition.EntitySystems; using Robust.Shared.Audio; +using Robust.Shared.GameStates; -namespace Content.Shared.Nutrition.Components +namespace Content.Shared.Nutrition.Components; + +/// +/// Component used for banana cream pies. +/// These can be thrown at someone to stun them and cream their face. +/// +[Access(typeof(SharedCreamPieSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class CreamPieComponent : Component { - [Access(typeof(SharedCreamPieSystem))] - [RegisterComponent] - public sealed partial class CreamPieComponent : Component - { - [DataField("paralyzeTime")] - public float ParalyzeTime { get; private set; } = 1f; + /// + /// The time being hit by this entity will stun you. + /// + [DataField, AutoNetworkedField] + public TimeSpan ParalyzeTime = TimeSpan.FromSeconds(1); - [DataField("sound")] - public SoundSpecifier Sound { get; private set; } = new SoundCollectionSpecifier("desecration"); + /// + /// The sound to play when hitting something. + /// + [DataField] + public SoundSpecifier Sound = new SoundCollectionSpecifier("desecration", AudioParams.Default.WithVariation(0.125f)); - [ViewVariables] - public bool Splatted { get; set; } = false; + /// + /// Has this pie been splatted by hitting something? + /// + [DataField, AutoNetworkedField] + public bool Splatted = false; - public const string PayloadSlotName = "payloadSlot"; - } + /// + /// Items in this container will be triggered when the pie hits something. + /// This allows throwable C4 pies or similar. + /// + [ViewVariables] + public const string PayloadSlotName = "payloadSlot"; } diff --git a/Content.Shared/Nutrition/Components/CreamPiedComponent.cs b/Content.Shared/Nutrition/Components/CreamPiedComponent.cs index 6779fe3666..08c583ff7b 100644 --- a/Content.Shared/Nutrition/Components/CreamPiedComponent.cs +++ b/Content.Shared/Nutrition/Components/CreamPiedComponent.cs @@ -1,19 +1,48 @@ using Content.Shared.Nutrition.EntitySystems; +using Robust.Shared.GameStates; using Robust.Shared.Serialization; +using Robust.Shared.Utility; -namespace Content.Shared.Nutrition.Components +namespace Content.Shared.Nutrition.Components; + +/// +/// Allows this entity to be hit by banana cream pies. +/// See . +/// +[Access(typeof(SharedCreamPieSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(raiseAfterAutoHandleState: true)] +public sealed partial class CreamPiedComponent : Component { - [Access(typeof(SharedCreamPieSystem))] - [RegisterComponent] - public sealed partial class CreamPiedComponent : Component - { - [ViewVariables] - public bool CreamPied { get; set; } = false; - } + /// + /// Was this entity hit by a banana cream pie? + /// This is reset if they get splashed with water. + /// + [DataField, AutoNetworkedField] + public bool CreamPied; - [Serializable, NetSerializable] - public enum CreamPiedVisuals - { - Creamed, - } + /// + /// The sprite to draw on someone's face if they were hit by a pie. + /// The layer will be dynamically added with the component. + /// + [DataField, AutoNetworkedField] + public SpriteSpecifier? Sprite; +} + +/// +/// Key to be used with appearance data, indicating if the entity has a banana cream pie in their face. +/// +[Serializable, NetSerializable] +public enum CreamPiedVisuals +{ + Creamed, +} + +/// +/// The visual layer for the creampied face. +/// Will be dynamically added and removed with the component. +/// +[Serializable, NetSerializable] +public enum CreamPiedVisualLayer +{ + Key, } diff --git a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs index a0a82d63ef..84827ed0e3 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() + SubscribeLocalEvent(OnCreamPieHit); + SubscribeLocalEvent(OnCreamPieLand); + SubscribeLocalEvent(OnCreamPiedHitBy); + SubscribeLocalEvent(OnSlice); + SubscribeLocalEvent(OnRejuvenate); + } + + /// + /// SPLAT! + /// + public void SplatCreamPie(Entity creamPie) + { + // Already splatted! Do nothing. + if (creamPie.Comp.Splatted) + return; + + // The pie will be queued for deletion but there may be multiple collisions in the same tick, so we prevent it from splatting more than once. + creamPie.Comp.Splatted = true; + Dirty(creamPie); + + // The entity is being deleted, so play the sound at its position rather than parenting. + if (_net.IsServer) // we don't have a user to pass in TODO: make the popup API sane and remove this guard { - base.Initialize(); - - SubscribeLocalEvent(OnCreamPieHit); - SubscribeLocalEvent(OnCreamPieLand); - SubscribeLocalEvent(OnCreamPiedHitBy); + var coordinates = Transform(creamPie).Coordinates; + _audio.PlayPvs(creamPie.Comp.Sound, coordinates); } - public void SplatCreamPie(Entity creamPie) + if (TryComp(creamPie, out var edibleComp)) { - // Already splatted! Do nothing. - if (creamPie.Comp.Splatted) - return; + if (_solutions.TryGetSolution(creamPie.Owner, edibleComp.Solution, out _, out var solution)) + _puddle.TrySpillAt(creamPie.Owner, solution, out _, false); - creamPie.Comp.Splatted = true; - - 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; - creamPied.CreamPied = value; + 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)); + } - if (TryComp(uid, out AppearanceComponent? appearance)) - { - _appearance.SetData(uid, CreamPiedVisuals.Creamed, value, appearance); - } - } + /// + /// 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; - private void OnCreamPieLand(Entity entity, ref LandEvent args) - { - SplatCreamPie(entity); - } + if (value == ent.Comp.CreamPied) + return; - private void OnCreamPieHit(Entity entity, ref ThrowDoHitEvent args) - { - SplatCreamPie(entity); - } + ent.Comp.CreamPied = value; + Dirty(ent); - private void OnCreamPiedHitBy(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) - { - if (!Exists(args.Thrown) || !TryComp(args.Thrown, out CreamPieComponent? creamPie)) return; + _appearance.SetData(ent.Owner, CreamPiedVisuals.Creamed, value); + } - SetCreamPied(uid, creamPied, true); + private void OnCreamPieLand(Entity ent, ref LandEvent args) + { + SplatCreamPie(ent); + } - CreamedEntity(uid, creamPied, args); + private void OnCreamPieHit(Entity ent, ref ThrowDoHitEvent args) + { + SplatCreamPie(ent); + } - _stunSystem.TryUpdateParalyzeDuration(uid, TimeSpan.FromSeconds(creamPie.ParalyzeTime)); - } + private void OnCreamPiedHitBy(Entity creamPied, ref ThrowHitByEvent args) + { + if (creamPied.Comp.CreamPied || !Exists(args.Thrown) || !TryComp(args.Thrown, out var creamPie)) + return; - protected virtual void CreamedEntity(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) {} + // 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); + } + + 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. + + private void OnSlice(Entity ent, ref SliceFoodEvent args) + { + ActivatePayload(ent); } } diff --git a/Resources/Prototypes/Body/Species/arachnid.yml b/Resources/Prototypes/Body/Species/arachnid.yml index 8455f33981..240d486f5a 100644 --- a/Resources/Prototypes/Body/Species/arachnid.yml +++ b/Resources/Prototypes/Body/Species/arachnid.yml @@ -137,6 +137,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/moth.yml b/Resources/Prototypes/Body/Species/moth.yml index af243333a6..b7928db44c 100644 --- a/Resources/Prototypes/Body/Species/moth.yml +++ b/Resources/Prototypes/Body/Species/moth.yml @@ -148,6 +148,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 f3b24081bf..8f28ea0ab4 100644 --- a/Resources/Prototypes/Body/Species/reptilian.yml +++ b/Resources/Prototypes/Body/Species/reptilian.yml @@ -131,6 +131,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/vox.yml b/Resources/Prototypes/Body/Species/vox.yml index 9fab05ea20..111a1f47fa 100644 --- a/Resources/Prototypes/Body/Species/vox.yml +++ b/Resources/Prototypes/Body/Species/vox.yml @@ -170,6 +170,10 @@ allowedEmotes: ['Click', 'Chitter'] - type: TypingIndicator proto: vox + - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_vox - type: Vocal sounds: Male: UnisexVox diff --git a/Resources/Prototypes/Body/Species/vulpkanin.yml b/Resources/Prototypes/Body/Species/vulpkanin.yml index 2ac31f2eec..c4afe1b8e1 100644 --- a/Resources/Prototypes/Body/Species/vulpkanin.yml +++ b/Resources/Prototypes/Body/Species/vulpkanin.yml @@ -204,6 +204,10 @@ reagents: - ReagentId: SulfurBlood Quantity: 300 + - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_vulpkanin - type: entity parent: OrganBase diff --git a/Resources/Prototypes/Body/species_appearance.yml b/Resources/Prototypes/Body/species_appearance.yml index 44369b1548..e70b6abb38 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 454b376ad3..1f782b0bc3 100644 --- a/Resources/Prototypes/Body/species_base.yml +++ b/Resources/Prototypes/Body/species_base.yml @@ -25,12 +25,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 @@ -126,6 +120,9 @@ factions: - NanoTrasen - type: CreamPied + sprite: + sprite: Effects/creampie.rsi + state: creampie_human - type: ParcelWrapOverride parcelPrototype: WrappedParcelHumanoid wrapDelay: 5 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 0c8e4beedb..2954199554 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 @@ -1505,7 +1498,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 +1622,6 @@ - map: [ "id" ] - map: [ "mask" ] - map: [ "head" ] - - map: [ "clownedon" ] - sprite: "Effects/creampie.rsi" - state: "creampie_human" - visible: false - type: RandomSprite getAllGroups: true available: @@ -2976,6 +2965,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/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 8aa1c4c7b0..2763afefec 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -15,6 +15,7 @@ - Grammar # Pronouns - HumanoidProfile # Age, Sex, Gender - NpcFactionMember + - CreamPied # traits - BlackAndWhiteOverlay - Clumsy diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index 3dfd48bb99..5e06100098 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -283,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 From b690017d44272233025ba4e32033b27ecf467f9b Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 3 Apr 2026 18:36:49 +0000 Subject: [PATCH 052/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a18eb6fa29..611fde4453 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4031,3 +4024,10 @@ 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 From 9f8ca1262a2cb12ed19893e02908f90ce75ffc9a Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Fri, 3 Apr 2026 11:24:45 -0700 Subject: [PATCH 053/247] Fix Bloodstream Overflow on Rejuvenate (#43447) fix bloodstream overflow Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Shared/Body/Systems/SharedBloodstreamSystem.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs index ec0521b7b6..a3caad69be 100644 --- a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs +++ b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs @@ -278,7 +278,8 @@ public abstract class SharedBloodstreamSystem : EntitySystem if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution)) { SolutionContainer.RemoveAllSolution(ent.Comp.BloodSolution.Value); - TryModifyBloodLevel(ent.AsNullable(), ent.Comp.BloodReferenceSolution.Volume); + // TODO: Use Solutions API for this when it exists + TryRegulateBloodLevel(ent.AsNullable(), ent.Comp.BloodReferenceSolution.Volume); } } @@ -404,11 +405,12 @@ public abstract class SharedBloodstreamSystem : EntitySystem return false; referenceFactor = Math.Clamp(referenceFactor, 0f, ent.Comp.MaxVolumeModifier); + var ratio = amount / 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) { From be9b2fbb88dd679441ec15390dac295ffdeb3b24 Mon Sep 17 00:00:00 2001 From: Moony Date: Fri, 3 Apr 2026 11:40:21 -0700 Subject: [PATCH 054/247] Remove unused space worldgen. (#43428) * Remove my unmaintained feature. * Cut world loaders. * Cut unused pool. --- Content.Client/Entry/EntryPoint.cs | 1 - Content.IntegrationTests/PoolManager.Cvars.cs | 1 - Content.IntegrationTests/Tests/EntityTest.cs | 3 - Content.Server/IoC/ServerContentIoC.cs | 2 - .../Components/BiomeSelectionComponent.cs | 21 -- .../Carvers/NoiseRangeCarverComponent.cs | 28 -- .../Debris/BlobFloorPlanBuilderComponent.cs | 36 --- .../DebrisFeaturePlacerControllerComponent.cs | 43 --- .../NoiseDrivenDebrisSelectorComponent.cs | 44 --- .../Components/Debris/OwnedDebrisComponent.cs | 24 -- .../Debris/SimpleDebrisSelectorComponent.cs | 34 --- .../SimpleFloorPlanPopulatorComponent.cs | 47 --- .../Components/LoadedChunkComponent.cs | 17 -- .../Components/LocalityLoaderComponent.cs | 19 -- .../Components/NoiseIndexComponent.cs | 20 -- .../Components/WorldChunkComponent.cs | 22 -- .../Components/WorldControllerComponent.cs | 25 -- .../Components/WorldLoaderComponent.cs | 18 -- .../Worldgen/GridPointsNearEnumerator.cs | 59 ---- .../Worldgen/Prototypes/BiomePrototype.cs | 61 ---- .../Prototypes/NoiseChannelPrototype.cs | 170 ----------- .../Prototypes/WorldgenConfigPrototype.cs | 37 --- .../Worldgen/Systems/BaseWorldSystem.cs | 60 ---- .../Systems/Biomes/BiomeSelectionSystem.cs | 75 ----- .../Systems/Carvers/NoiseRangeCarverSystem.cs | 36 --- .../Debris/BlobFloorPlanBuilderSystem.cs | 89 ------ .../Debris/DebrisFeaturePlacerSystem.cs | 278 ------------------ .../Debris/NoiseDrivenDebrisSelectorSystem.cs | 60 ---- .../Debris/SimpleFloorPlanPopulatorSystem.cs | 50 ---- .../Worldgen/Systems/LocalityLoaderSystem.cs | 63 ---- .../Worldgen/Systems/NoiseIndexSystem.cs | 47 --- .../Worldgen/Systems/WorldControllerSystem.cs | 277 ----------------- .../Worldgen/Systems/WorldgenConfigSystem.cs | 85 ------ .../Tools/EntitySpawnCollectionCache.cs | 96 ------ .../Worldgen/Tools/PoissonDiskSampler.cs | 244 --------------- Content.Server/Worldgen/WorldGen.cs | 72 ----- Content.Shared/CCVar/CCVars.Worldgen.cs | 18 -- .../en-US/worldgen/applyworldgenconfig.ftl | 4 - .../Machines/Computers/computers.yml | 4 - .../Entities/Structures/Machines/salvage.yml | 8 - .../Entities/World/Debris/asteroids.yml | 144 --------- .../Entities/World/Debris/base_debris.yml | 6 - .../Entities/World/Debris/wrecks.yml | 80 ----- Resources/Prototypes/Entities/World/chunk.yml | 14 - Resources/Prototypes/World/Biomes/basic.yml | 26 -- .../Prototypes/World/Biomes/failsafes.yml | 21 -- Resources/Prototypes/World/noise_channels.yml | 44 --- .../Prototypes/World/worldgen_default.yml | 9 - 48 files changed, 2642 deletions(-) delete mode 100644 Content.Server/Worldgen/Components/BiomeSelectionComponent.cs delete mode 100644 Content.Server/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs delete mode 100644 Content.Server/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs delete mode 100644 Content.Server/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs delete mode 100644 Content.Server/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs delete mode 100644 Content.Server/Worldgen/Components/Debris/OwnedDebrisComponent.cs delete mode 100644 Content.Server/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs delete mode 100644 Content.Server/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs delete mode 100644 Content.Server/Worldgen/Components/LoadedChunkComponent.cs delete mode 100644 Content.Server/Worldgen/Components/LocalityLoaderComponent.cs delete mode 100644 Content.Server/Worldgen/Components/NoiseIndexComponent.cs delete mode 100644 Content.Server/Worldgen/Components/WorldChunkComponent.cs delete mode 100644 Content.Server/Worldgen/Components/WorldControllerComponent.cs delete mode 100644 Content.Server/Worldgen/Components/WorldLoaderComponent.cs delete mode 100644 Content.Server/Worldgen/GridPointsNearEnumerator.cs delete mode 100644 Content.Server/Worldgen/Prototypes/BiomePrototype.cs delete mode 100644 Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs delete mode 100644 Content.Server/Worldgen/Prototypes/WorldgenConfigPrototype.cs delete mode 100644 Content.Server/Worldgen/Systems/BaseWorldSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/LocalityLoaderSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/NoiseIndexSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/WorldControllerSystem.cs delete mode 100644 Content.Server/Worldgen/Systems/WorldgenConfigSystem.cs delete mode 100644 Content.Server/Worldgen/Tools/EntitySpawnCollectionCache.cs delete mode 100644 Content.Server/Worldgen/Tools/PoissonDiskSampler.cs delete mode 100644 Content.Server/Worldgen/WorldGen.cs delete mode 100644 Content.Shared/CCVar/CCVars.Worldgen.cs delete mode 100644 Resources/Locale/en-US/worldgen/applyworldgenconfig.ftl delete mode 100644 Resources/Prototypes/Entities/World/Debris/asteroids.yml delete mode 100644 Resources/Prototypes/Entities/World/Debris/base_debris.yml delete mode 100644 Resources/Prototypes/Entities/World/Debris/wrecks.yml delete mode 100644 Resources/Prototypes/Entities/World/chunk.yml delete mode 100644 Resources/Prototypes/World/Biomes/basic.yml delete mode 100644 Resources/Prototypes/World/Biomes/failsafes.yml delete mode 100644 Resources/Prototypes/World/noise_channels.yml delete mode 100644 Resources/Prototypes/World/worldgen_default.yml diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index e0358d54e7..5f7f827cc2 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -119,7 +119,6 @@ namespace Content.Client.Entry _prototypeManager.RegisterIgnore("noiseChannel"); _prototypeManager.RegisterIgnore("playerConnectionWhitelist"); _prototypeManager.RegisterIgnore("spaceBiome"); - _prototypeManager.RegisterIgnore("worldgenConfig"); _prototypeManager.RegisterIgnore("gameRule"); _prototypeManager.RegisterIgnore("worldSpell"); _prototypeManager.RegisterIgnore("entitySpell"); diff --git a/Content.IntegrationTests/PoolManager.Cvars.cs b/Content.IntegrationTests/PoolManager.Cvars.cs index b457d4a40b..189e37fa39 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -25,7 +25,6 @@ public static partial class PoolManager (CCVars.ArrivalsShuttles.Name, "false"), (CCVars.EmergencyShuttleEnabled.Name, "false"), (CCVars.ProcgenPreload.Name, "false"), - (CCVars.WorldgenEnabled.Name, "false"), (CCVars.GatewayGeneratorEnabled.Name, "false"), (CCVars.GameDummyTicker.Name, "true"), (CCVars.GameLobbyEnabled.Name, "false"), diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 59dfec1060..13293b7d46 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -385,9 +385,6 @@ namespace Content.IntegrationTests.Tests "StationData", // errors when removed mid-round "StationJobs", "Actor", // We aren't testing actor components, those need their player session set. - "BlobFloorPlanBuilder", // Implodes if unconfigured. - "DebrisFeaturePlacerController", // Above. - "LoadedChunk", // Worldgen chunk loading malding. "BiomeSelection", // Whaddya know, requires config. "ActivatableUI", // Requires enum key }; diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index 1c6d940e20..6a92b08b1e 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -23,7 +23,6 @@ using Content.Server.Preferences.Managers; using Content.Server.ServerInfo; using Content.Server.ServerUpdates; using Content.Server.Voting.Managers; -using Content.Server.Worldgen.Tools; using Content.Shared.Administration.Logs; using Content.Shared.Administration.Managers; using Content.Shared.Chat; @@ -66,7 +65,6 @@ internal static class ServerContentIoC deps.Register(); deps.Register(); deps.Register(); - deps.Register(); deps.Register(); deps.Register(); deps.Register(); diff --git a/Content.Server/Worldgen/Components/BiomeSelectionComponent.cs b/Content.Server/Worldgen/Components/BiomeSelectionComponent.cs deleted file mode 100644 index 08571cd588..0000000000 --- a/Content.Server/Worldgen/Components/BiomeSelectionComponent.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems.Biomes; -using Robust.Shared.Prototypes; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for selecting the biome(s) to be used during world generation. -/// -[RegisterComponent] -[Access(typeof(BiomeSelectionSystem))] -public sealed partial class BiomeSelectionComponent : Component -{ - /// - /// The list of biomes available to this selector. - /// - /// This is always sorted by priority after ComponentStartup. - [DataField(required: true)] - public List> Biomes = new(); -} - diff --git a/Content.Server/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs b/Content.Server/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs deleted file mode 100644 index 28724d20a4..0000000000 --- a/Content.Server/Worldgen/Components/Carvers/NoiseRangeCarverComponent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems.Carvers; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Worldgen.Components.Carvers; - -/// -/// This is used for carving out empty space in the game world, providing byways through the debris field. -/// -[RegisterComponent] -[Access(typeof(NoiseRangeCarverSystem))] -public sealed partial class NoiseRangeCarverComponent : Component -{ - /// - /// The noise channel to use as a density controller. - /// - /// This noise channel should be mapped to exactly the range [0, 1] unless you want a lot of warnings in the log. - [DataField("noiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string NoiseChannel { get; private set; } = default!; - - /// - /// The index of ranges in which to cut debris generation. - /// - [DataField("ranges", required: true)] - public List Ranges { get; private set; } = default!; -} - diff --git a/Content.Server/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs b/Content.Server/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs deleted file mode 100644 index a1317ae2ed..0000000000 --- a/Content.Server/Worldgen/Components/Debris/BlobFloorPlanBuilderComponent.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Content.Server.Worldgen.Systems.Debris; -using Content.Shared.Maps; -using Robust.Shared.Prototypes; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for constructing asteroid debris. -/// -[RegisterComponent] -[Access(typeof(BlobFloorPlanBuilderSystem))] -public sealed partial class BlobFloorPlanBuilderComponent : Component -{ - /// - /// The probability that placing a floor tile will add up to three-four neighboring tiles as well. - /// - [DataField("blobDrawProb")] public float BlobDrawProb; - - /// - /// The maximum radius for the structure. - /// - [DataField("radius", required: true)] public float Radius; - - /// - /// The tiles to be used for the floor plan. - /// - [DataField(required: true)] - public List> FloorTileset { get; private set; } = default!; - - /// - /// The number of floor tiles to place when drawing the asteroid layout. - /// - [DataField("floorPlacements", required: true)] - public int FloorPlacements { get; private set; } -} - diff --git a/Content.Server/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs b/Content.Server/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs deleted file mode 100644 index ae61f0581e..0000000000 --- a/Content.Server/Worldgen/Components/Debris/DebrisFeaturePlacerControllerComponent.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems.Debris; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for controlling the debris feature placer. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed partial class DebrisFeaturePlacerControllerComponent : Component -{ - /// - /// Whether or not to clip debris that would spawn at a location that has a density of zero. - /// - [DataField("densityClip")] public bool DensityClip = true; - - /// - /// Whether or not entities are already spawned. - /// - public bool DoSpawns = true; - - [DataField("ownedDebris")] public Dictionary OwnedDebris = new(); - - /// - /// The chance spawning a piece of debris will just be cancelled randomly. - /// - [DataField("randomCancelChance")] public float RandomCancellationChance = 0.1f; - - /// - /// Radius in which there should be no objects for debris to spawn. - /// - [DataField("safetyZoneRadius")] public float SafetyZoneRadius = 16.0f; - - /// - /// The noise channel to use as a density controller. - /// - [DataField("densityNoiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string DensityNoiseChannel { get; private set; } = default!; -} - diff --git a/Content.Server/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs b/Content.Server/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs deleted file mode 100644 index af4ef7f1cf..0000000000 --- a/Content.Server/Worldgen/Components/Debris/NoiseDrivenDebrisSelectorComponent.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems.Debris; -using Content.Server.Worldgen.Tools; -using Content.Shared.Storage; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for selecting debris with a probability determined by a noise channel. -/// Takes priority over SimpleDebrisSelectorComponent and should likely be used in combination. -/// -[RegisterComponent] -[Access(typeof(NoiseDrivenDebrisSelectorSystem))] -public sealed partial class NoiseDrivenDebrisSelectorComponent : Component -{ - private EntitySpawnCollectionCache? _cache; - - /// - /// The prototype-facing debris table entries. - /// - [DataField("debrisTable", required: true)] - private List _entries = default!; - - /// - /// The debris entity spawn collection. - /// - public EntitySpawnCollectionCache CachedDebrisTable - { - get - { - _cache ??= new EntitySpawnCollectionCache(_entries); - return _cache; - } - } - - /// - /// The noise channel to use as a density controller. - /// - /// This noise channel should be mapped to exactly the range [0, 1] unless you want a lot of warnings in the log. - [DataField("noiseChannel", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string NoiseChannel { get; private set; } = default!; -} - diff --git a/Content.Server/Worldgen/Components/Debris/OwnedDebrisComponent.cs b/Content.Server/Worldgen/Components/Debris/OwnedDebrisComponent.cs deleted file mode 100644 index b211277997..0000000000 --- a/Content.Server/Worldgen/Components/Debris/OwnedDebrisComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Systems.Debris; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for attaching a piece of debris to it's owning controller. -/// Mostly just syncs deletion. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed partial class OwnedDebrisComponent : Component -{ - /// - /// The last location in the controller's internal structure for this debris. - /// - [DataField("lastKey")] public Vector2 LastKey; - - /// - /// The DebrisFeaturePlacerController-having entity that owns this. - /// - [DataField("owningController")] public EntityUid OwningController; -} - diff --git a/Content.Server/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs b/Content.Server/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs deleted file mode 100644 index 5db9bad925..0000000000 --- a/Content.Server/Worldgen/Components/Debris/SimpleDebrisSelectorComponent.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Content.Server.Worldgen.Systems.Debris; -using Content.Server.Worldgen.Tools; -using Content.Shared.Storage; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for a very simple debris selection for simple biomes. Just uses a spawn table. -/// -[RegisterComponent] -[Access(typeof(DebrisFeaturePlacerSystem))] -public sealed partial class SimpleDebrisSelectorComponent : Component -{ - private EntitySpawnCollectionCache? _cache; - - /// - /// The prototype-facing debris table entries. - /// - [DataField("debrisTable", required: true)] - private List _entries = default!; - - /// - /// The debris entity spawn collection. - /// - public EntitySpawnCollectionCache CachedDebrisTable - { - get - { - _cache ??= new EntitySpawnCollectionCache(_entries); - return _cache; - } - } -} - diff --git a/Content.Server/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs b/Content.Server/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs deleted file mode 100644 index 4865773bf3..0000000000 --- a/Content.Server/Worldgen/Components/Debris/SimpleFloorPlanPopulatorComponent.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Linq; -using Content.Server.Worldgen.Systems.Debris; -using Content.Server.Worldgen.Tools; -using Content.Shared.Maps; -using Content.Shared.Storage; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; - -namespace Content.Server.Worldgen.Components.Debris; - -/// -/// This is used for populating a grid with random entities automatically. -/// -[RegisterComponent] -[Access(typeof(SimpleFloorPlanPopulatorSystem))] -public sealed partial class SimpleFloorPlanPopulatorComponent : Component -{ - private Dictionary? _caches; - - /// - /// The prototype facing floor plan populator entries. - /// - [DataField("entries", required: true, - customTypeSerializer: typeof(PrototypeIdDictionarySerializer, ContentTileDefinition>))] - private Dictionary> _entries = default!; - - /// - /// The spawn collections used to place entities on different tile types. - /// - [ViewVariables] - public Dictionary Caches - { - get - { - if (_caches is null) - { - _caches = _entries - .Select(x => - new KeyValuePair(x.Key, - new EntitySpawnCollectionCache(x.Value))) - .ToDictionary(x => x.Key, x => x.Value); - } - - return _caches; - } - } -} - diff --git a/Content.Server/Worldgen/Components/LoadedChunkComponent.cs b/Content.Server/Worldgen/Components/LoadedChunkComponent.cs deleted file mode 100644 index d2743ad4ab..0000000000 --- a/Content.Server/Worldgen/Components/LoadedChunkComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for marking a chunk as loaded. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed partial class LoadedChunkComponent : Component -{ - /// - /// The current list of entities loading this chunk. - /// - [ViewVariables] public List? Loaders = null; -} - diff --git a/Content.Server/Worldgen/Components/LocalityLoaderComponent.cs b/Content.Server/Worldgen/Components/LocalityLoaderComponent.cs deleted file mode 100644 index 1d37ab34c9..0000000000 --- a/Content.Server/Worldgen/Components/LocalityLoaderComponent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for sending a signal to the entity it's on to load contents whenever a loader gets close enough. -/// Does not support unloading. -/// -[RegisterComponent] -[Access(typeof(LocalityLoaderSystem))] -public sealed partial class LocalityLoaderComponent : Component -{ - /// - /// The maximum distance an entity can be from the loader for it to not load. - /// Once a loader is closer than this, the event is fired and this component removed. - /// - [DataField("loadingDistance")] public int LoadingDistance = 32; -} - diff --git a/Content.Server/Worldgen/Components/NoiseIndexComponent.cs b/Content.Server/Worldgen/Components/NoiseIndexComponent.cs deleted file mode 100644 index 06d84d2f85..0000000000 --- a/Content.Server/Worldgen/Components/NoiseIndexComponent.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Server.Worldgen.Prototypes; -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for containing configured noise generators. -/// -[RegisterComponent] -[Access(typeof(NoiseIndexSystem))] -public sealed partial class NoiseIndexComponent : Component -{ - /// - /// An index of generators, to avoid having to recreate them every time a noise channel is used. - /// Keyed by noise generator prototype ID. - /// - [Access(typeof(NoiseIndexSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.None)] - public Dictionary Generators { get; } = new(); -} - diff --git a/Content.Server/Worldgen/Components/WorldChunkComponent.cs b/Content.Server/Worldgen/Components/WorldChunkComponent.cs deleted file mode 100644 index 3a91c00756..0000000000 --- a/Content.Server/Worldgen/Components/WorldChunkComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for marking an entity as being a world chunk. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed partial class WorldChunkComponent : Component -{ - /// - /// The coordinates of the chunk, in chunk space. - /// - [DataField("coordinates")] public Vector2i Coordinates; - - /// - /// The map this chunk belongs to. - /// - [DataField("map")] public EntityUid Map; -} - diff --git a/Content.Server/Worldgen/Components/WorldControllerComponent.cs b/Content.Server/Worldgen/Components/WorldControllerComponent.cs deleted file mode 100644 index 63580e7541..0000000000 --- a/Content.Server/Worldgen/Components/WorldControllerComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Server.Worldgen.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for controlling overall world loading, containing an index of all chunks in the map. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed partial class WorldControllerComponent : Component -{ - /// - /// The prototype to use for chunks on this world map. - /// - [DataField("chunkProto", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ChunkProto = "WorldChunk"; - - /// - /// An index of chunks owned by the controller. - /// - [DataField("chunks")] public Dictionary Chunks = new(); -} - diff --git a/Content.Server/Worldgen/Components/WorldLoaderComponent.cs b/Content.Server/Worldgen/Components/WorldLoaderComponent.cs deleted file mode 100644 index e6bb7781e9..0000000000 --- a/Content.Server/Worldgen/Components/WorldLoaderComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Server.Worldgen.Systems; - -namespace Content.Server.Worldgen.Components; - -/// -/// This is used for allowing some objects to load the game world. -/// -[RegisterComponent] -[Access(typeof(WorldControllerSystem))] -public sealed partial class WorldLoaderComponent : Component -{ - /// - /// The radius in which the loader loads the world. - /// - [ViewVariables(VVAccess.ReadWrite)] [DataField("radius")] - public int Radius = 128; -} - diff --git a/Content.Server/Worldgen/GridPointsNearEnumerator.cs b/Content.Server/Worldgen/GridPointsNearEnumerator.cs deleted file mode 100644 index 24b710626b..0000000000 --- a/Content.Server/Worldgen/GridPointsNearEnumerator.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; - -namespace Content.Server.Worldgen; - -/// -/// A struct enumerator of points on a grid within the given radius. -/// -public struct GridPointsNearEnumerator -{ - private readonly int _radius; - private readonly Vector2i _center; - private int _x; - private int _y; - - /// - /// Initializes a new enumerator with the given center and radius. - /// - public GridPointsNearEnumerator(Vector2i center, int radius) - { - _radius = radius; - _center = center; - _x = -_radius; - _y = -_radius; - } - - /// - /// Gets the next point in the enumeration. - /// - /// The computed point, if any - /// Success - [Pure] - public bool MoveNext([NotNullWhen(true)] out Vector2i? chunk) - { - while (!(_x * _x + _y * _y <= _radius * _radius)) - { - if (_y > _radius) - { - chunk = null; - return false; - } - - if (_x > _radius) - { - _x = -_radius; - _y++; - } - else - { - _x++; - } - } - - chunk = _center + new Vector2i(_x, _y); - _x++; - return true; - } -} - diff --git a/Content.Server/Worldgen/Prototypes/BiomePrototype.cs b/Content.Server/Worldgen/Prototypes/BiomePrototype.cs deleted file mode 100644 index 75e4670e88..0000000000 --- a/Content.Server/Worldgen/Prototypes/BiomePrototype.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Numerics; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; - -namespace Content.Server.Worldgen.Prototypes; - -/// -/// This is a prototype for biome selection, allowing the component list of a chunk to be amended based on the output -/// of noise channels at that location. -/// -[Prototype("spaceBiome")] -public sealed partial class BiomePrototype : IPrototype, IInheritingPrototype -{ - /// - [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] - public string[]? Parents { get; private set; } - - /// - [NeverPushInheritance] - [AbstractDataField] - public bool Abstract { get; private set; } - - /// - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// The valid ranges of noise values under which this biome can be picked. - /// - [DataField("noiseRanges", required: true)] - public Dictionary> NoiseRanges = default!; - - /// - /// Higher priority biomes get picked before lower priority ones. - /// - [DataField("priority", required: true)] - public int Priority { get; private set; } - - /// - /// The components that get added to the target map. - /// - [DataField("chunkComponents")] - [AlwaysPushInheritance] - public ComponentRegistry ChunkComponents = new(); - - //TODO: Get someone to make this a method on componentregistry that does it Correctly. - /// - /// Applies the worldgen config to the given target (presumably a map.) - /// - public void Apply(EntityUid target, ISerializationManager serialization, IEntityManager entityManager) - { - // Add all components required by the prototype. Engine update for this whenst. - foreach (var data in ChunkComponents.Values) - { - var comp = (Component) serialization.CreateCopy(data.Component, notNullableOverride: true); - entityManager.AddComponent(target, comp); - } - } -} - diff --git a/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs b/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs deleted file mode 100644 index 41d89cbaa7..0000000000 --- a/Content.Server/Worldgen/Prototypes/NoiseChannelPrototype.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System.Numerics; -using Robust.Shared.Noise; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; - -namespace Content.Server.Worldgen.Prototypes; - -/// -/// This is a config for noise channels, used by worldgen. -/// -[Virtual] -public class NoiseChannelConfig -{ - /// - /// The noise type used by the noise generator. - /// - [DataField("noiseType")] - public FastNoiseLite.NoiseType NoiseType { get; private set; } = FastNoiseLite.NoiseType.Cellular; - - /// - /// The fractal type used by the noise generator. - /// - [DataField("fractalType")] - public FastNoiseLite.FractalType FractalType { get; private set; } = FastNoiseLite.FractalType.FBm; - - /// - /// Multiplied by pi in code when used. - /// - [DataField("fractalLacunarityByPi")] - public float FractalLacunarityByPi { get; private set; } = 2.0f / 3.0f; - - /// - /// Ranges of values that get clamped down to the "clipped" value. - /// - [DataField("clippingRanges")] - public List ClippingRanges { get; private set; } = new(); - - /// - /// The value clipped chunks are set to. - /// - [DataField("clippedValue")] - public float ClippedValue { get; private set; } - - /// - /// A value the output is multiplied by. - /// - [DataField("outputMultiplier")] - public float OutputMultiplier { get; private set; } = 1.0f; - - /// - /// A value the input is multiplied by. - /// - [DataField("inputMultiplier")] - public float InputMultiplier { get; private set; } = 1.0f; - - /// - /// Remaps the output of the noise function from the range (-1, 1) to (0, 1). This is done before all other output - /// transformations. - /// - [DataField("remapTo0Through1")] - public bool RemapTo0Through1 { get; private set; } - - /// - /// For when the transformation you need is too complex to describe in YAML. - /// - [DataField("noisePostProcess")] - public NoisePostProcess? NoisePostProcess { get; private set; } - - /// - /// For when you need a complex transformation of the input coordinates. - /// - [DataField("noiseCoordinateProcess")] - public NoiseCoordinateProcess? NoiseCoordinateProcess { get; private set; } - - /// - /// The "center" of the range of values. Or the minimum if mapped 0 through 1. - /// - [DataField("minimum")] - public float Minimum { get; private set; } -} - -[Prototype] -public sealed partial class NoiseChannelPrototype : NoiseChannelConfig, IPrototype, IInheritingPrototype -{ - /// - [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] - public string[]? Parents { get; private set; } - - /// - [NeverPushInheritance] - [AbstractDataField] - public bool Abstract { get; private set; } - - /// - [IdDataField] - public string ID { get; private set; } = default!; -} - -/// -/// A wrapper around FastNoise's noise generation, using noise channel configs. -/// -public struct NoiseGenerator -{ - private readonly NoiseChannelConfig _config; - private readonly FastNoiseLite _noise; - - /// - /// Produces a new noise generator from the given channel config and rng seed. - /// - public NoiseGenerator(NoiseChannelConfig config, int seed) - { - _config = config; - _noise = new FastNoiseLite(); - _noise.SetSeed(seed); - _noise.SetNoiseType(_config.NoiseType); - _noise.SetFractalType(_config.FractalType); - _noise.SetFractalLacunarity(_config.FractalLacunarityByPi * MathF.PI); - } - - /// - /// Evaluates the noise generator at the provided coordinates. - /// - /// Coordinates to use as input - /// Computed noise value - public float Evaluate(Vector2 coords) - { - var finCoords = coords * _config.InputMultiplier; - - if (_config.NoiseCoordinateProcess is not null) - finCoords = _config.NoiseCoordinateProcess.Process(finCoords); - - var value = _noise.GetNoise(finCoords.X, finCoords.Y); - - if (_config.RemapTo0Through1) - value = (value + 1.0f) / 2.0f; - - foreach (var range in _config.ClippingRanges) - { - if (range.X < value && value < range.Y) - { - value = _config.ClippedValue; - break; - } - } - - if (_config.NoisePostProcess is not null) - value = _config.NoisePostProcess.Process(value); - value *= _config.OutputMultiplier; - return value + _config.Minimum; - } -} - -/// -/// A processing class that adjusts the input coordinate space to a noise channel. -/// -[ImplicitDataDefinitionForInheritors] -public abstract partial class NoiseCoordinateProcess -{ - public abstract Vector2 Process(Vector2 inp); -} - -/// -/// A processing class that adjusts the final result of the noise channel. -/// -[ImplicitDataDefinitionForInheritors] -public abstract partial class NoisePostProcess -{ - public abstract float Process(float inp); -} - diff --git a/Content.Server/Worldgen/Prototypes/WorldgenConfigPrototype.cs b/Content.Server/Worldgen/Prototypes/WorldgenConfigPrototype.cs deleted file mode 100644 index c9107fa2bd..0000000000 --- a/Content.Server/Worldgen/Prototypes/WorldgenConfigPrototype.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; - -namespace Content.Server.Worldgen.Prototypes; - -/// -/// This is a prototype for controlling overall world generation. -/// The components included are applied to the map that world generation is configured on. -/// -[Prototype] -public sealed partial class WorldgenConfigPrototype : IPrototype -{ - /// - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// The components that get added to the target map. - /// - [DataField("components", required: true)] - public ComponentRegistry Components { get; private set; } = default!; - - //TODO: Get someone to make this a method on componentregistry that does it Correctly. - /// - /// Applies the worldgen config to the given target (presumably a map.) - /// - public void Apply(EntityUid target, ISerializationManager serialization, IEntityManager entityManager) - { - // Add all components required by the prototype. Engine update for this whenst. - foreach (var data in Components.Values) - { - var comp = (Component) serialization.CreateCopy(data.Component, notNullableOverride: true); - entityManager.AddComponent(target, comp); - } - } -} - diff --git a/Content.Server/Worldgen/Systems/BaseWorldSystem.cs b/Content.Server/Worldgen/Systems/BaseWorldSystem.cs deleted file mode 100644 index 7a9e74375c..0000000000 --- a/Content.Server/Worldgen/Systems/BaseWorldSystem.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Components; -using JetBrains.Annotations; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This provides some additional functions for world generation systems. -/// Exists primarily for convenience and to avoid code duplication. -/// -[PublicAPI] -public abstract class BaseWorldSystem : EntitySystem -{ - [Dependency] private readonly WorldControllerSystem _worldController = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - - /// - /// Gets a chunk's coordinates in chunk space as an integer value. - /// - /// - /// - /// Chunk space coordinates - [Pure] - public Vector2i GetChunkCoords(EntityUid ent, TransformComponent? xform = null) - { - if (!Resolve(ent, ref xform)) - throw new Exception("Failed to resolve transform, somehow."); - - return WorldGen.WorldToChunkCoords(_transformSystem.GetWorldPosition(xform)).Floored(); - } - - /// - /// Gets a chunk's coordinates in chunk space as a floating point value. - /// - /// - /// - /// Chunk space coordinates - [Pure] - public Vector2 GetFloatingChunkCoords(EntityUid ent, TransformComponent? xform = null) - { - if (!Resolve(ent, ref xform)) - throw new Exception("Failed to resolve transform, somehow."); - - return WorldGen.WorldToChunkCoords(_transformSystem.GetWorldPosition(xform)); - } - - /// - /// Attempts to get a chunk, creating it if it doesn't exist. - /// - /// Chunk coordinates to get the chunk entity for. - /// Map the chunk is in. - /// The controller this chunk belongs to. - /// A chunk, if available. - [Pure] - public EntityUid? GetOrCreateChunk(Vector2i chunk, EntityUid map, WorldControllerComponent? controller = null) - { - return _worldController.GetOrCreateChunk(chunk, map, controller); - } -} - diff --git a/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs b/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs deleted file mode 100644 index 1827f6deed..0000000000 --- a/Content.Server/Worldgen/Systems/Biomes/BiomeSelectionSystem.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Linq; -using Content.Server.Worldgen.Components; -using Content.Server.Worldgen.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; - -namespace Content.Server.Worldgen.Systems.Biomes; - -/// -/// This handles biome selection, evaluating which biome to apply to a chunk based on noise channels. -/// -public sealed class BiomeSelectionSystem : BaseWorldSystem -{ - [Dependency] private readonly NoiseIndexSystem _noiseIdx = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly ISerializationManager _ser = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnBiomeSelectionStartup); - SubscribeLocalEvent(OnWorldChunkAdded); - } - - private void OnWorldChunkAdded(EntityUid uid, BiomeSelectionComponent component, ref WorldChunkAddedEvent args) - { - var coords = args.Coords; - foreach (var biomeId in component.Biomes) - { - var biome = _proto.Index(biomeId); - if (!CheckBiomeValidity(args.Chunk, biome, coords)) - continue; - - biome.Apply(args.Chunk, _ser, EntityManager); - return; - } - - Log.Error($"Biome selection ran out of biomes to select? See biomes list: {component.Biomes}"); - } - - private void OnBiomeSelectionStartup(EntityUid uid, BiomeSelectionComponent component, ComponentStartup args) - { - // surely this can't be THAAAAAAAAAAAAAAAT bad right???? - var sorted = component.Biomes - .Select(x => (Id: x, _proto.Index(x).Priority)) - .OrderByDescending(x => x.Priority) - .Select(x => x.Id) - .ToList(); - - component.Biomes = sorted; // my hopes and dreams rely on this being pre-sorted by priority. - } - - private bool CheckBiomeValidity(EntityUid chunk, BiomePrototype biome, Vector2i coords) - { - foreach (var (noise, ranges) in biome.NoiseRanges) - { - var value = _noiseIdx.Evaluate(chunk, noise, coords); - var anyValid = false; - foreach (var range in ranges) - { - if (range.X < value && value < range.Y) - { - anyValid = true; - break; - } - } - - if (!anyValid) - return false; - } - - return true; - } -} - diff --git a/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs b/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs deleted file mode 100644 index 1207d6f157..0000000000 --- a/Content.Server/Worldgen/Systems/Carvers/NoiseRangeCarverSystem.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Content.Server.Worldgen.Components.Carvers; -using Content.Server.Worldgen.Systems.Debris; - -namespace Content.Server.Worldgen.Systems.Carvers; - -/// -/// This handles carving out holes in world generation according to a noise channel. -/// -public sealed class NoiseRangeCarverSystem : EntitySystem -{ - [Dependency] private readonly NoiseIndexSystem _index = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnPrePlaceDebris); - } - - private void OnPrePlaceDebris(EntityUid uid, NoiseRangeCarverComponent component, - ref PrePlaceDebrisFeatureEvent args) - { - var coords = WorldGen.WorldToChunkCoords(_transform.ToMapCoordinates(args.Coords).Position); - var val = _index.Evaluate(uid, component.NoiseChannel, coords); - - foreach (var (low, high) in component.Ranges) - { - if (low > val || high < val) - continue; - - args.Handled = true; - return; - } - } -} - diff --git a/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs b/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs deleted file mode 100644 index ba0a3a7132..0000000000 --- a/Content.Server/Worldgen/Systems/Debris/BlobFloorPlanBuilderSystem.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Linq; -using Content.Server.Worldgen.Components.Debris; -using Content.Shared.Maps; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Systems.Debris; - -/// -/// This handles building the floor plans for "blobby" debris. -/// -public sealed class BlobFloorPlanBuilderSystem : BaseWorldSystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefinition = default!; - [Dependency] private readonly TileSystem _tiles = default!; - [Dependency] private readonly SharedMapSystem _map = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnBlobFloorPlanBuilderStartup); - } - - private void OnBlobFloorPlanBuilderStartup(EntityUid uid, BlobFloorPlanBuilderComponent component, - ComponentStartup args) - { - PlaceFloorplanTiles(uid, component, Comp(uid)); - } - - private void PlaceFloorplanTiles(EntityUid gridUid, BlobFloorPlanBuilderComponent comp, MapGridComponent grid) - { - // NO MORE THAN TWO ALLOCATIONS THANK YOU VERY MUCH. - // TODO: Just put these on a field instead then? - // Also the end of the method has a big LINQ which is gonna blow this out the water. - var spawnPoints = new HashSet(comp.FloorPlacements * 6); - var taken = new Dictionary(comp.FloorPlacements * 5); - - void PlaceTile(Vector2i point) - { - // Assume we already know that the spawn point is safe. - spawnPoints.Remove(point); - var north = point.Offset(Direction.North); - var south = point.Offset(Direction.South); - var east = point.Offset(Direction.East); - var west = point.Offset(Direction.West); - var radsq = Math.Pow(comp.Radius, - 2); // I'd put this outside but i'm not 100% certain caching it between calls is a gain. - - // The math done is essentially a fancy way of comparing the distance from 0,0 to the radius, - // and skipping the sqrt normally needed for dist. - if (!taken.ContainsKey(north) && Math.Pow(north.X, 2) + Math.Pow(north.Y, 2) <= radsq) - spawnPoints.Add(north); - if (!taken.ContainsKey(south) && Math.Pow(south.X, 2) + Math.Pow(south.Y, 2) <= radsq) - spawnPoints.Add(south); - if (!taken.ContainsKey(east) && Math.Pow(east.X, 2) + Math.Pow(east.Y, 2) <= radsq) - spawnPoints.Add(east); - if (!taken.ContainsKey(west) && Math.Pow(west.X, 2) + Math.Pow(west.Y, 2) <= radsq) - spawnPoints.Add(west); - - var tileDef = _tileDefinition[_random.Pick(comp.FloorTileset)]; - taken.Add(point, new Tile(tileDef.TileId, 0, _tiles.PickVariant((ContentTileDefinition) tileDef))); - } - - PlaceTile(Vector2i.Zero); - - for (var i = 0; i < comp.FloorPlacements; i++) - { - var point = _random.Pick(spawnPoints); - PlaceTile(point); - - if (comp.BlobDrawProb > 0.0f) - { - if (!taken.ContainsKey(point.Offset(Direction.North)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.North)); - if (!taken.ContainsKey(point.Offset(Direction.South)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.South)); - if (!taken.ContainsKey(point.Offset(Direction.East)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.East)); - if (!taken.ContainsKey(point.Offset(Direction.West)) && _random.Prob(comp.BlobDrawProb)) - PlaceTile(point.Offset(Direction.West)); - } - } - - _map.SetTiles(gridUid, grid, taken.Select(x => (x.Key, x.Value)).ToList()); - } -} - diff --git a/Content.Server/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs b/Content.Server/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs deleted file mode 100644 index b609f7e1f7..0000000000 --- a/Content.Server/Worldgen/Systems/Debris/DebrisFeaturePlacerSystem.cs +++ /dev/null @@ -1,278 +0,0 @@ -using System.Linq; -using System.Numerics; -using Content.Server.Worldgen.Components; -using Content.Server.Worldgen.Components.Debris; -using Content.Server.Worldgen.Tools; -using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; -using Robust.Shared.Utility; - -namespace Content.Server.Worldgen.Systems.Debris; - -/// -/// This handles placing debris within the world evenly with rng, primarily for structures like asteroid fields. -/// -public sealed class DebrisFeaturePlacerSystem : BaseWorldSystem -{ - [Dependency] private readonly NoiseIndexSystem _noiseIndex = default!; - [Dependency] private readonly PoissonDiskSampler _sampler = default!; - [Dependency] private readonly TransformSystem _xformSys = default!; - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - private ISawmill _sawmill = default!; - - private List> _mapGrids = new(); - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world.debris.feature_placer"); - SubscribeLocalEvent(OnChunkLoaded); - SubscribeLocalEvent(OnChunkUnloaded); - SubscribeLocalEvent(OnDebrisShutdown); - SubscribeLocalEvent(OnDebrisMove); - SubscribeLocalEvent( - OnTryGetPlacableDebrisEvent); - } - - /// - /// Handles debris moving, and making sure it stays parented to a chunk for loading purposes. - /// - private void OnDebrisMove(EntityUid uid, OwnedDebrisComponent component, ref MoveEvent args) - { - if (!HasComp(component.OwningController)) - return; // Redundant logic, prolly needs it's own handler for your custom system. - - var placer = Comp(component.OwningController); - var xform = args.Component; - var ownerXform = Transform(component.OwningController); - if (xform.MapUid is null || ownerXform.MapUid is null) - return; // not our problem - - if (xform.MapUid != ownerXform.MapUid) - { - _sawmill.Error($"Somehow debris {uid} left it's expected map! Unparenting it to avoid issues."); - RemCompDeferred(uid); - placer.OwnedDebris.Remove(component.LastKey); - return; - } - - placer.OwnedDebris.Remove(component.LastKey); - var newChunk = GetOrCreateChunk(GetChunkCoords(uid), xform.MapUid!.Value); - if (newChunk is null || !TryComp(newChunk, out var newPlacer)) - { - // Whelp. - RemCompDeferred(uid); - return; - } - - newPlacer.OwnedDebris[_xformSys.GetWorldPosition(xform)] = uid; // Change our owner. - component.OwningController = newChunk.Value; - } - - /// - /// Handles debris shutdown/detach. - /// - private void OnDebrisShutdown(EntityUid uid, OwnedDebrisComponent component, ComponentShutdown args) - { - if (!TryComp(component.OwningController, out var placer)) - return; - - placer.OwnedDebris[component.LastKey] = null; - if (Terminating(uid)) - placer.OwnedDebris.Remove(component.LastKey); - } - - /// - /// Queues all debris owned by the placer for garbage collection. - /// - private void OnChunkUnloaded(EntityUid uid, DebrisFeaturePlacerControllerComponent component, - ref WorldChunkUnloadedEvent args) - { - component.DoSpawns = true; - } - - /// - /// Handles providing a debris type to place for SimpleDebrisSelectorComponent. - /// This randomly picks a debris type from the EntitySpawnCollectionCache. - /// - private void OnTryGetPlacableDebrisEvent(EntityUid uid, SimpleDebrisSelectorComponent component, - ref TryGetPlaceableDebrisFeatureEvent args) - { - if (args.DebrisProto is not null) - return; - - var l = new List(1); - component.CachedDebrisTable.GetSpawns(_random, ref l); - - switch (l.Count) - { - case 0: - return; - case > 1: - _sawmill.Warning($"Got more than one possible debris type from {uid}. List: {string.Join(", ", l)}"); - break; - } - - args.DebrisProto = l[0]; - } - - /// - /// Handles loading in debris. This does the following: - /// - Checks if the debris is currently supposed to do spawns, if it isn't, aborts immediately. - /// - Evaluates the density value to be used for placement, if it's zero, aborts. - /// - Generates the points to generate debris at, if and only if they've not been selected already by a prior load. - /// - Does the following in a loop over all generated points: - /// - Raises an event to check if something else wants to intercept debris placement, if the event is handled, - /// continues to the next point without generating anything. - /// - Raises an event to get the debris type that should be used for generation. - /// - Spawns the given debris at the point, adding it to the placer's index. - /// - private void OnChunkLoaded(EntityUid uid, DebrisFeaturePlacerControllerComponent component, - ref WorldChunkLoadedEvent args) - { - if (component.DoSpawns == false) - return; - - component.DoSpawns = false; // Don't repeat yourself if this crashes. - - if (!TryComp(args.Chunk, out var chunk)) - return; - - var chunkMap = chunk.Map; - - if (!TryComp(chunkMap, out var map)) - return; - - var densityChannel = component.DensityNoiseChannel; - var density = _noiseIndex.Evaluate(uid, densityChannel, chunk.Coordinates + new Vector2(0.5f, 0.5f)); - if (density == 0) - return; - - List? points = null; - - // If we've been loaded before, reuse the same coordinates. - if (component.OwnedDebris.Count != 0) - { - //TODO: Remove LINQ. - points = component.OwnedDebris - .Where(x => !Deleted(x.Value)) - .Select(static x => x.Key) - .ToList(); - } - - points ??= GeneratePointsInChunk(args.Chunk, density, chunk.Coordinates, chunkMap); - - var mapId = map.MapId; - - var safetyBounds = Box2.UnitCentered.Enlarged(component.SafetyZoneRadius); - var failures = 0; // Avoid severe log spam. - foreach (var point in points) - { - if (component.OwnedDebris.TryGetValue(point, out var existing)) - { - DebugTools.Assert(Exists(existing)); - continue; - } - - var pointDensity = _noiseIndex.Evaluate(uid, densityChannel, WorldGen.WorldToChunkCoords(point)); - if (pointDensity == 0 && component.DensityClip || _random.Prob(component.RandomCancellationChance)) - continue; - - if (HasCollisions(mapId, safetyBounds.Translated(point))) - continue; - - var coords = new EntityCoordinates(chunkMap, point); - - var preEv = new PrePlaceDebrisFeatureEvent(coords, args.Chunk); - RaiseLocalEvent(uid, ref preEv); - if (uid != args.Chunk) - RaiseLocalEvent(args.Chunk, ref preEv); - - if (preEv.Handled) - continue; - - var debrisFeatureEv = new TryGetPlaceableDebrisFeatureEvent(coords, args.Chunk); - RaiseLocalEvent(uid, ref debrisFeatureEv); - - if (debrisFeatureEv.DebrisProto == null) - { - // Try on the chunk...? - if (uid != args.Chunk) - RaiseLocalEvent(args.Chunk, ref debrisFeatureEv); - - if (debrisFeatureEv.DebrisProto == null) - { - // Nope. - failures++; - continue; - } - } - - var ent = Spawn(debrisFeatureEv.DebrisProto, coords); - component.OwnedDebris.Add(point, ent); - - var owned = EnsureComp(ent); - owned.OwningController = uid; - owned.LastKey = point; - } - - if (failures > 0) - _sawmill.Error($"Failed to place {failures} debris at chunk {args.Chunk}"); - } - - /// - /// Checks to see if the potential spawn point is clear - /// - /// - /// - /// - private bool HasCollisions(MapId mapId, Box2 point) - { - _mapGrids.Clear(); - _mapManager.FindGridsIntersecting(mapId, point, ref _mapGrids); - return _mapGrids.Count > 0; - } - - /// - /// Generates the points to put into a chunk using a poisson disk sampler. - /// - private List GeneratePointsInChunk(EntityUid chunk, float density, Vector2 coords, EntityUid map) - { - var offs = (int) ((WorldGen.ChunkSize - WorldGen.ChunkSize / 8.0f) / 2.0f); - var topLeft = new Vector2(-offs, -offs); - var lowerRight = new Vector2(offs, offs); - var enumerator = _sampler.SampleRectangle(topLeft, lowerRight, density); - var debrisPoints = new List(); - - var realCenter = WorldGen.ChunkToWorldCoordsCentered(coords.Floored()); - - while (enumerator.MoveNext(out var debrisPoint)) - { - debrisPoints.Add(realCenter + debrisPoint.Value); - } - - return debrisPoints; - } -} - -/// -/// Fired directed on the debris feature placer controller and the chunk, ahead of placing a debris piece. -/// -[ByRefEvent] -[PublicAPI] -public record struct PrePlaceDebrisFeatureEvent(EntityCoordinates Coords, EntityUid Chunk, bool Handled = false); - -/// -/// Fired directed on the debris feature placer controller and the chunk, to select which debris piece to place. -/// -[ByRefEvent] -[PublicAPI] -public record struct TryGetPlaceableDebrisFeatureEvent(EntityCoordinates Coords, EntityUid Chunk, - string? DebrisProto = null); - diff --git a/Content.Server/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs b/Content.Server/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs deleted file mode 100644 index a02ff81362..0000000000 --- a/Content.Server/Worldgen/Systems/Debris/NoiseDrivenDebrisSelectorSystem.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Content.Server.Worldgen.Components.Debris; -using Robust.Server.GameObjects; -using Robust.Shared.Physics; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Systems.Debris; - -/// -/// This handles selecting debris with probability decided by a noise channel. -/// -public sealed class NoiseDrivenDebrisSelectorSystem : BaseWorldSystem -{ - [Dependency] private readonly NoiseIndexSystem _index = default!; - [Dependency] private readonly TransformSystem _xformSys = default!; - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - private ISawmill _sawmill = default!; - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world.debris.noise_debris_selector"); - // Event is forcibly ordered to always be handled after the simple selector. - SubscribeLocalEvent(OnSelectDebrisKind, - after: new[] {typeof(DebrisFeaturePlacerSystem)}); - } - - private void OnSelectDebrisKind(EntityUid uid, NoiseDrivenDebrisSelectorComponent component, - ref TryGetPlaceableDebrisFeatureEvent args) - { - var coords = WorldGen.WorldToChunkCoords(_xformSys.ToMapCoordinates(args.Coords).Position); - var prob = _index.Evaluate(uid, component.NoiseChannel, coords); - - if (prob is < 0 or > 1) - { - _sawmill.Error( - $"Sampled a probability of {prob}, which is outside the [0, 1] range, at {coords} aka {args.Coords}."); - return; - } - - if (!_random.Prob(prob)) - return; - - var l = new List(1); - component.CachedDebrisTable.GetSpawns(_random, ref l); - - switch (l.Count) - { - case 0: - return; - case > 1: - _sawmill.Warning($"Got more than one possible debris type from {uid}. List: {string.Join(", ", l)}"); - break; - } - - args.DebrisProto = l[0]; - } -} - diff --git a/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs b/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs deleted file mode 100644 index dcb7b7fc8f..0000000000 --- a/Content.Server/Worldgen/Systems/Debris/SimpleFloorPlanPopulatorSystem.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Content.Server.Worldgen.Components.Debris; -using Content.Shared.Maps; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Systems.Debris; - -/// -/// This handles populating simple structures, simply using a loot table for each tile. -/// -public sealed class SimpleFloorPlanPopulatorSystem : BaseWorldSystem -{ - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedMapSystem _map = default!; - [Dependency] private readonly TurfSystem _turf = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnFloorPlanBuilt); - } - - private void OnFloorPlanBuilt(EntityUid uid, SimpleFloorPlanPopulatorComponent component, - LocalStructureLoadedEvent args) - { - var placeables = new List(4); - var grid = Comp(uid); - var enumerator = _map.GetAllTilesEnumerator(uid, grid); - while (enumerator.MoveNext(out var tile)) - { - var coords = _map.GridTileToLocal(uid, grid, tile.Value.GridIndices); - var selector = _turf.GetContentTileDefinition(tile.Value).ID; - if (!component.Caches.TryGetValue(selector, out var cache)) - continue; - - placeables.Clear(); - cache.GetSpawns(_random, ref placeables); - - foreach (var proto in placeables) - { - if (proto is null) - continue; - - Spawn(proto, coords); - } - } - } -} - diff --git a/Content.Server/Worldgen/Systems/LocalityLoaderSystem.cs b/Content.Server/Worldgen/Systems/LocalityLoaderSystem.cs deleted file mode 100644 index fb02e8aa0b..0000000000 --- a/Content.Server/Worldgen/Systems/LocalityLoaderSystem.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Content.Server.Worldgen.Components; -using Robust.Server.GameObjects; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This handles loading in objects based on distance from player, using some metadata on chunks. -/// -public sealed class LocalityLoaderSystem : BaseWorldSystem -{ - [Dependency] private readonly TransformSystem _xformSys = default!; - - /// - public override void Update(float frameTime) - { - var e = EntityQueryEnumerator(); - var loadedQuery = GetEntityQuery(); - var xformQuery = GetEntityQuery(); - var controllerQuery = GetEntityQuery(); - - while (e.MoveNext(out var uid, out var loadable, out var xform)) - { - if (!controllerQuery.TryGetComponent(xform.MapUid, out var controller)) - { - RaiseLocalEvent(uid, new LocalStructureLoadedEvent()); - RemCompDeferred(uid); - continue; - } - - var coords = GetChunkCoords(uid, xform); - var done = false; - for (var i = -1; i < 2 && !done; i++) - { - for (var j = -1; j < 2 && !done; j++) - { - var chunk = GetOrCreateChunk(coords + (i, j), xform.MapUid!.Value, controller); - if (!loadedQuery.TryGetComponent(chunk, out var loaded) || loaded.Loaders is null) - continue; - - foreach (var loader in loaded.Loaders) - { - if (!xformQuery.TryGetComponent(loader, out var loaderXform)) - continue; - - if ((_xformSys.GetWorldPosition(loaderXform) - _xformSys.GetWorldPosition(xform)).Length() > loadable.LoadingDistance) - continue; - - RaiseLocalEvent(uid, new LocalStructureLoadedEvent()); - RemCompDeferred(uid); - done = true; - break; - } - } - } - } - } -} - -/// -/// A directed fired on a loadable entity when a local loader enters it's vicinity. -/// -public record struct LocalStructureLoadedEvent; - diff --git a/Content.Server/Worldgen/Systems/NoiseIndexSystem.cs b/Content.Server/Worldgen/Systems/NoiseIndexSystem.cs deleted file mode 100644 index 5a7e02c803..0000000000 --- a/Content.Server/Worldgen/Systems/NoiseIndexSystem.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Numerics; -using Content.Server.Worldgen.Components; -using Content.Server.Worldgen.Prototypes; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This handles the noise index. -/// -public sealed class NoiseIndexSystem : EntitySystem -{ - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IRobustRandom _random = default!; - - /// - /// Gets a particular noise channel from the index on the given entity. - /// - /// The holder of the index - /// The channel prototype ID - /// An initialized noise generator - public NoiseGenerator Get(EntityUid holder, string protoId) - { - var idx = EnsureComp(holder); - if (idx.Generators.TryGetValue(protoId, out var generator)) - return generator; - var proto = _prototype.Index(protoId); - var gen = new NoiseGenerator(proto, _random.Next()); - idx.Generators[protoId] = gen; - return gen; - } - - /// - /// Attempts to evaluate the given noise channel using the generator on the given entity. - /// - /// The holder of the index - /// The channel prototype ID - /// The coordinates to evaluate at - /// The result of evaluation - public float Evaluate(EntityUid holder, string protoId, Vector2 coords) - { - var gen = Get(holder, protoId); - return gen.Evaluate(coords); - } -} - diff --git a/Content.Server/Worldgen/Systems/WorldControllerSystem.cs b/Content.Server/Worldgen/Systems/WorldControllerSystem.cs deleted file mode 100644 index 19c777e1ad..0000000000 --- a/Content.Server/Worldgen/Systems/WorldControllerSystem.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System.Linq; -using Content.Server.Worldgen.Components; -using Content.Shared.Ghost; -using Content.Shared.Mind.Components; -using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Timing; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This handles putting together chunk entities and notifying them about important changes. -/// -public sealed class WorldControllerSystem : EntitySystem -{ - [Dependency] private readonly TransformSystem _xformSys = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - - private const int PlayerLoadRadius = 2; - - private ISawmill _sawmill = default!; - - /// - public override void Initialize() - { - _sawmill = _logManager.GetSawmill("world"); - SubscribeLocalEvent(OnChunkLoadedCore); - SubscribeLocalEvent(OnChunkUnloadedCore); - SubscribeLocalEvent(OnChunkShutdown); - } - - /// - /// Handles deleting chunks properly. - /// - private void OnChunkShutdown(EntityUid uid, WorldChunkComponent component, ComponentShutdown args) - { - if (!TryComp(component.Map, out var controller)) - return; - - if (HasComp(uid)) - { - var ev = new WorldChunkUnloadedEvent(uid, component.Coordinates); - RaiseLocalEvent(component.Map, ref ev); - RaiseLocalEvent(uid, ref ev, broadcast: true); - } - - controller.Chunks.Remove(component.Coordinates); - } - - /// - /// Handles the inner logic of loading a chunk, i.e. events. - /// - private void OnChunkLoadedCore(EntityUid uid, LoadedChunkComponent component, ComponentStartup args) - { - if (!TryComp(uid, out var chunk)) - return; - - var ev = new WorldChunkLoadedEvent(uid, chunk.Coordinates); - RaiseLocalEvent(chunk.Map, ref ev); - RaiseLocalEvent(uid, ref ev, broadcast: true); - //_sawmill.Debug($"Loaded chunk {ToPrettyString(uid)} at {chunk.Coordinates}"); - } - - /// - /// Handles the inner logic of unloading a chunk, i.e. events. - /// - private void OnChunkUnloadedCore(EntityUid uid, LoadedChunkComponent component, ComponentShutdown args) - { - if (!TryComp(uid, out var chunk)) - return; - - if (Terminating(uid)) - return; // SAFETY: This is in case a loaded chunk gets deleted, to avoid double unload. - - var ev = new WorldChunkUnloadedEvent(uid, chunk.Coordinates); - RaiseLocalEvent(chunk.Map, ref ev); - RaiseLocalEvent(uid, ref ev); - //_sawmill.Debug($"Unloaded chunk {ToPrettyString(uid)} at {coords}"); - } - - /// - public override void Update(float frameTime) - { - //there was a to-do here about every frame alloc but it turns out it's a nothing burger here. - var chunksToLoad = new Dictionary>>(); - - var controllerEnum = EntityQueryEnumerator(); - while (controllerEnum.MoveNext(out var uid, out _)) - { - chunksToLoad[uid] = new Dictionary>(); - } - - if (chunksToLoad.Count == 0) - return; // Just bail early. - - var loaderEnum = EntityQueryEnumerator(); - - while (loaderEnum.MoveNext(out var uid, out var worldLoader, out var xform)) - { - var mapOrNull = xform.MapUid; - if (mapOrNull is null) - continue; - var map = mapOrNull.Value; - if (!chunksToLoad.ContainsKey(map)) - continue; - - var wc = _xformSys.GetWorldPosition(xform); - var coords = WorldGen.WorldToChunkCoords(wc); - var chunks = new GridPointsNearEnumerator(coords.Floored(), - (int) Math.Ceiling(worldLoader.Radius / (float) WorldGen.ChunkSize) + 1); - - var set = chunksToLoad[map]; - - while (chunks.MoveNext(out var chunk)) - { - if (!set.TryGetValue(chunk.Value, out _)) - set[chunk.Value] = new List(4); - set[chunk.Value].Add(uid); - } - } - - var mindEnum = EntityQueryEnumerator(); - var ghostQuery = GetEntityQuery(); - - // Mindful entities get special privilege as they're always a player and we don't want the illusion being broken around them. - while (mindEnum.MoveNext(out var uid, out var mind, out var xform)) - { - if (!mind.HasMind) - continue; - if (ghostQuery.HasComponent(uid)) - continue; - var mapOrNull = xform.MapUid; - if (mapOrNull is null) - continue; - var map = mapOrNull.Value; - if (!chunksToLoad.ContainsKey(map)) - continue; - - var wc = _xformSys.GetWorldPosition(xform); - var coords = WorldGen.WorldToChunkCoords(wc); - var chunks = new GridPointsNearEnumerator(coords.Floored(), PlayerLoadRadius); - - var set = chunksToLoad[map]; - - while (chunks.MoveNext(out var chunk)) - { - if (!set.TryGetValue(chunk.Value, out _)) - set[chunk.Value] = new List(4); - set[chunk.Value].Add(uid); - } - } - - var loadedEnum = EntityQueryEnumerator(); - var chunksUnloaded = 0; - - // Make sure these chunks get unloaded at the end of the tick. - while (loadedEnum.MoveNext(out var uid, out var _, out var chunk)) - { - var coords = chunk.Coordinates; - - if (!chunksToLoad[chunk.Map].ContainsKey(coords)) - { - RemCompDeferred(uid); - chunksUnloaded++; - } - } - - if (chunksUnloaded > 0) - _sawmill.Debug($"Queued {chunksUnloaded} chunks for unload."); - - if (chunksToLoad.All(x => x.Value.Count == 0)) - return; - - var startTime = _gameTiming.RealTime; - var count = 0; - var loadedQuery = GetEntityQuery(); - var controllerQuery = GetEntityQuery(); - foreach (var (map, chunks) in chunksToLoad) - { - var controller = controllerQuery.GetComponent(map); - foreach (var (chunk, loaders) in chunks) - { - var ent = GetOrCreateChunk(chunk, map, controller); // Ensure everything loads. - LoadedChunkComponent? c = null; - if (ent is not null && !loadedQuery.TryGetComponent(ent.Value, out c)) - { - c = AddComp(ent.Value); - count += 1; - } - - if (c is not null) - c.Loaders = loaders; - } - } - - if (count > 0) - { - var timeSpan = _gameTiming.RealTime - startTime; - _sawmill.Debug($"Loaded {count} chunks in {timeSpan.TotalMilliseconds:N2}ms."); - } - } - - /// - /// Attempts to get a chunk, creating it if it doesn't exist. - /// - /// Chunk coordinates to get the chunk entity for. - /// Map the chunk is in. - /// The controller this chunk belongs to. - /// A chunk, if available. - [Pure] - public EntityUid? GetOrCreateChunk(Vector2i chunk, EntityUid map, WorldControllerComponent? controller = null) - { - if (!Resolve(map, ref controller)) - throw new Exception($"Tried to use {ToPrettyString(map)} as a world map, without actually being one."); - - if (controller.Chunks.TryGetValue(chunk, out var ent)) - return ent; - return CreateChunkEntity(chunk, map, controller); - } - - /// - /// Constructs a new chunk entity, attaching it to the map. - /// - /// The coordinates the new chunk should be initialized for. - /// - /// - /// - private EntityUid CreateChunkEntity(Vector2i chunkCoords, EntityUid map, WorldControllerComponent controller) - { - var chunk = Spawn(controller.ChunkProto, MapCoordinates.Nullspace); - StartupChunkEntity(chunk, chunkCoords, map, controller); - _metaData.SetEntityName(chunk, $"Chunk {chunkCoords.X}/{chunkCoords.Y}"); - return chunk; - } - - private void StartupChunkEntity(EntityUid chunk, Vector2i coords, EntityUid map, - WorldControllerComponent controller) - { - if (!TryComp(chunk, out var chunkComponent)) - { - _sawmill.Error($"Chunk {ToPrettyString(chunk)} is missing WorldChunkComponent."); - return; - } - - ref var chunks = ref controller.Chunks; - - chunks[coords] = chunk; // Add this entity to chunk index. - chunkComponent.Coordinates = coords; - chunkComponent.Map = map; - var ev = new WorldChunkAddedEvent(chunk, coords); - RaiseLocalEvent(map, ref ev, broadcast: true); - } -} - -/// -/// A directed event fired when a chunk is initially set up in the world. The chunk is not loaded at this point. -/// -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkAddedEvent(EntityUid Chunk, Vector2i Coords); - -/// -/// A directed event fired when a chunk is loaded into the world, i.e. a player or other world loader has entered vicinity. -/// -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkLoadedEvent(EntityUid Chunk, Vector2i Coords); - -/// -/// A directed event fired when a chunk is unloaded from the world, i.e. no world loaders remain nearby. -/// -[ByRefEvent] -[PublicAPI] -public readonly record struct WorldChunkUnloadedEvent(EntityUid Chunk, Vector2i Coords); diff --git a/Content.Server/Worldgen/Systems/WorldgenConfigSystem.cs b/Content.Server/Worldgen/Systems/WorldgenConfigSystem.cs deleted file mode 100644 index cc0ec62733..0000000000 --- a/Content.Server/Worldgen/Systems/WorldgenConfigSystem.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Content.Server.Administration; -using Content.Server.GameTicking; -using Content.Server.GameTicking.Events; -using Content.Server.Worldgen.Components; -using Content.Server.Worldgen.Prototypes; -using Content.Shared.Administration; -using Content.Shared.CCVar; -using Robust.Shared.Configuration; -using Robust.Shared.Console; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager; -using Robust.Shared.Utility; - -namespace Content.Server.Worldgen.Systems; - -/// -/// This handles configuring world generation during round start. -/// -public sealed class WorldgenConfigSystem : EntitySystem -{ - [Dependency] private readonly GameTicker _gameTicker = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly IConsoleHost _conHost = default!; - [Dependency] private readonly SharedMapSystem _map = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly ISerializationManager _ser = default!; - - private bool _enabled; - private string _worldgenConfig = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnLoadingMaps); - _conHost.RegisterCommand("applyworldgenconfig", Loc.GetString("cmd-applyworldgenconfig-description"), Loc.GetString("cmd-applyworldgenconfig-help"), ApplyWorldgenConfigCommand); - Subs.CVar(_cfg, CCVars.WorldgenEnabled, b => _enabled = b, true); - Subs.CVar(_cfg, CCVars.WorldgenConfig, s => _worldgenConfig = s, true); - } - - [AdminCommand(AdminFlags.Mapping)] - private void ApplyWorldgenConfigCommand(IConsoleShell shell, string argstr, string[] args) - { - if (args.Length != 2) - { - shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", ("properAmount", 2), ("currentAmount", args.Length))); - return; - } - - if (!int.TryParse(args[0], out var mapInt) || !_map.MapExists(new MapId(mapInt))) - { - shell.WriteError(Loc.GetString("shell-invalid-map-id")); - return; - } - - var map = _map.GetMapOrInvalid(new MapId(mapInt)); - - if (!_proto.TryIndex(args[1], out var proto)) - { - shell.WriteError(Loc.GetString("shell-argument-must-be-prototype", ("index", 2), ("prototypeName", "cmd-applyworldgenconfig-prototype"))); - return; - } - - proto.Apply(map, _ser, EntityManager); - shell.WriteLine(Loc.GetString("cmd-applyworldgenconfig-success")); - } - - /// - /// Applies the world config to the default map if enabled. - /// - private void OnLoadingMaps(RoundStartingEvent ev) - { - if (_enabled == false) - return; - - var target = _map.GetMapOrInvalid(_gameTicker.DefaultMap); - Log.Debug($"Trying to configure {_gameTicker.DefaultMap}, aka {ToPrettyString(target)} aka {target}"); - var cfg = _proto.Index(_worldgenConfig); - - cfg.Apply(target, _ser, EntityManager); // Apply the config to the map. - - DebugTools.Assert(HasComp(target)); - } -} - diff --git a/Content.Server/Worldgen/Tools/EntitySpawnCollectionCache.cs b/Content.Server/Worldgen/Tools/EntitySpawnCollectionCache.cs deleted file mode 100644 index 5480575427..0000000000 --- a/Content.Server/Worldgen/Tools/EntitySpawnCollectionCache.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.Linq; -using Content.Shared.Storage; -using Robust.Shared.Random; - -namespace Content.Server.Worldgen.Tools; - -/// -/// A faster version of EntitySpawnCollection that requires caching to work. -/// -public sealed class EntitySpawnCollectionCache -{ - [ViewVariables] private readonly Dictionary _orGroups = new(); - - public EntitySpawnCollectionCache(IEnumerable entries) - { - // collect groups together, create singular items that pass probability - foreach (var entry in entries) - { - if (!_orGroups.TryGetValue(entry.GroupId ?? string.Empty, out var orGroup)) - { - orGroup = new OrGroup(); - _orGroups.Add(entry.GroupId ?? string.Empty, orGroup); - } - - orGroup.Entries.Add(entry); - orGroup.CumulativeProbability += entry.SpawnProbability; - } - } - - /// - /// Using a collection of entity spawn entries, picks a random list of entity prototypes to spawn from that collection. - /// - /// - /// This does not spawn the entities. The caller is responsible for doing so, since it may want to do something - /// special to those entities (offset them, insert them into storage, etc) - /// - /// Resolve param. - /// List that spawned entities are inserted into. - /// A list of entity prototypes that should be spawned. - /// This is primarily useful if you're calling it many times over, as it lets you reuse the list repeatedly. - public void GetSpawns(IRobustRandom random, ref List spawned) - { - // handle orgroup spawns - foreach (var spawnValue in _orGroups.Values) - { - //HACK: This doesn't seem to work without this if there's only a single orgroup entry. Not sure how to fix the original math properly, but it works in every other case. - if (spawnValue.Entries.Count == 1) - { - var entry = spawnValue.Entries.First(); - var amount = entry.Amount; - - if (entry.MaxAmount > amount) - amount = random.Next(amount, entry.MaxAmount); - - for (var index = 0; index < amount; index++) - { - spawned.Add(entry.PrototypeId); - } - - continue; - } - - // For each group use the added cumulative probability to roll a double in that range - var diceRoll = random.NextDouble() * spawnValue.CumulativeProbability; - // Add the entry's spawn probability to this value, if equals or lower, spawn item, otherwise continue to next item. - var cumulative = 0.0; - foreach (var entry in spawnValue.Entries) - { - cumulative += entry.SpawnProbability; - if (diceRoll > cumulative) - continue; - // Dice roll succeeded, add item and break loop - - var amount = entry.Amount; - - if (entry.MaxAmount > amount) - amount = random.Next(amount, entry.MaxAmount); - - for (var index = 0; index < amount; index++) - { - spawned.Add(entry.PrototypeId); - } - - break; - } - } - } - - private sealed class OrGroup - { - [ViewVariables] public List Entries { get; } = new(); - - [ViewVariables] public float CumulativeProbability { get; set; } - } -} - diff --git a/Content.Server/Worldgen/Tools/PoissonDiskSampler.cs b/Content.Server/Worldgen/Tools/PoissonDiskSampler.cs deleted file mode 100644 index cb7c5a1411..0000000000 --- a/Content.Server/Worldgen/Tools/PoissonDiskSampler.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Numerics; -using Robust.Shared.Random; -using Robust.Shared.Utility; - -namespace Content.Server.Worldgen.Tools; - -/// -/// An implementation of Poisson Disk Sampling, for evenly spreading points across a given area. -/// -public sealed class PoissonDiskSampler -{ - public const int DefaultPointsPerIteration = 30; - [Dependency] private readonly IRobustRandom _random = default!; - - /// - /// Samples for points within the given circle. - /// - /// Center of the sample - /// Radius of the sample - /// Minimum distance between points. Must be above 0! - /// The number of points placed per iteration of the algorithm - /// An enumerator of points - public SampleEnumerator SampleCircle(Vector2 center, float radius, float minimumDistance, - int pointsPerIteration = DefaultPointsPerIteration) - { - return Sample(center - new Vector2(radius, radius), center + new Vector2(radius, radius), radius, - minimumDistance, pointsPerIteration); - } - - /// - /// Samples for points within the given rectangle. - /// - /// The top left of the rectangle - /// The bottom right of the rectangle - /// Minimum distance between points. Must be above 0! - /// The number of points placed per iteration of the algorithm - /// An enumerator of points - public SampleEnumerator SampleRectangle(Vector2 topLeft, Vector2 lowerRight, float minimumDistance, - int pointsPerIteration = DefaultPointsPerIteration) - { - return Sample(topLeft, lowerRight, null, minimumDistance, pointsPerIteration); - } - - /// - /// Samples for points within the given rectangle, with an optional rejection distance. - /// - /// The top left of the rectangle - /// The bottom right of the rectangle - /// The distance at which points will be discarded, if any - /// Minimum distance between points. Must be above 0! - /// The number of points placed per iteration of the algorithm - /// An enumerator of points - public SampleEnumerator Sample(Vector2 topLeft, Vector2 lowerRight, float? rejectionDistance, - float minimumDistance, int pointsPerIteration) - { - // This still doesn't guard against dangerously low but non-zero distances, but this will do for now. - DebugTools.Assert(minimumDistance > 0, "Minimum distance must be above 0, or else an infinite number of points would be generated."); - - var settings = new SampleSettings - { - TopLeft = topLeft, LowerRight = lowerRight, - Dimensions = lowerRight - topLeft, - Center = (topLeft + lowerRight) / 2, - CellSize = minimumDistance / (float) Math.Sqrt(2), - MinimumDistance = minimumDistance, - RejectionSqDistance = rejectionDistance * rejectionDistance - }; - - settings.GridWidth = (int) (settings.Dimensions.X / settings.CellSize) + 1; - settings.GridHeight = (int) (settings.Dimensions.Y / settings.CellSize) + 1; - - var state = new State - { - Grid = new Vector2?[settings.GridWidth, settings.GridHeight], - ActivePoints = new List() - }; - - return new SampleEnumerator(this, state, settings, pointsPerIteration); - } - - private Vector2 AddFirstPoint(ref SampleSettings settings, ref State state) - { - while (true) - { - var d = _random.NextDouble(); - var xr = settings.TopLeft.X + settings.Dimensions.X * d; - - d = _random.NextDouble(); - var yr = settings.TopLeft.Y + settings.Dimensions.Y * d; - - var p = new Vector2((float) xr, (float) yr); - if (settings.RejectionSqDistance != null && - (settings.Center - p).LengthSquared() > settings.RejectionSqDistance) - continue; - - var index = Denormalize(p, settings.TopLeft, settings.CellSize); - - state.Grid[(int) index.X, (int) index.Y] = p; - - state.ActivePoints.Add(p); - return p; - } - } - - private Vector2? AddNextPoint(Vector2 point, ref SampleSettings settings, ref State state) - { - var q = GenerateRandomAround(point, settings.MinimumDistance); - - if (q.X >= settings.TopLeft.X && q.X < settings.LowerRight.X && - q.Y > settings.TopLeft.Y && q.Y < settings.LowerRight.Y && - (settings.RejectionSqDistance == null || - (settings.Center - q).LengthSquared() <= settings.RejectionSqDistance)) - { - var qIndex = Denormalize(q, settings.TopLeft, settings.CellSize); - var tooClose = false; - - for (var i = (int) Math.Max(0, qIndex.X - 2); - i < Math.Min(settings.GridWidth, qIndex.X + 3) && !tooClose; - i++) - for (var j = (int) Math.Max(0, qIndex.Y - 2); - j < Math.Min(settings.GridHeight, qIndex.Y + 3) && !tooClose; - j++) - { - if (state.Grid[i, j].HasValue && (state.Grid[i, j]!.Value - q).Length() < settings.MinimumDistance) - tooClose = true; - } - - if (!tooClose) - { - state.ActivePoints.Add(q); - state.Grid[(int) qIndex.X, (int) qIndex.Y] = q; - return q; - } - } - - return null; - } - - private Vector2 GenerateRandomAround(Vector2 center, float minimumDistance) - { - var d = _random.NextDouble(); - var radius = minimumDistance + minimumDistance * d; - - d = _random.NextDouble(); - var angle = Math.PI * 2 * d; - - var newX = radius * Math.Sin(angle); - var newY = radius * Math.Cos(angle); - - return new Vector2((float) (center.X + newX), (float) (center.Y + newY)); - } - - private static Vector2 Denormalize(Vector2 point, Vector2 origin, double cellSize) - { - return new Vector2((int) ((point.X - origin.X) / cellSize), (int) ((point.Y - origin.Y) / cellSize)); - } - - public struct SampleEnumerator - { - private PoissonDiskSampler _pds; - private State _state; - private SampleSettings _settings; - // These variables make up the state machine. - private bool _returnedFirstPoint; - private int _pointsPerIteration; - private int _iterationListIndex; - private bool _iterationFound; - private int _iterationPosition; - - // This has internal access because C# nested type access is being weird. - internal SampleEnumerator(PoissonDiskSampler pds, State state, SampleSettings settings, int ppi) - { - _pds = pds; - _state = state; - _settings = settings; - _pointsPerIteration = ppi; - } - - public bool MoveNext([NotNullWhen(true)] out Vector2? point) - { - // First point is chosen via a very particular method. - if (!_returnedFirstPoint) - { - _returnedFirstPoint = true; - point = _pds.AddFirstPoint(ref _settings, ref _state); - return true; - } - - // Remaining points have to be fed out carefully. - // We can be interrupted (by a successful point) mid-stream. - while (_state.ActivePoints.Count != 0) - { - if (_iterationPosition == 0) - { - // First point of iteration. - _iterationListIndex = _pds._random.Next(_state.ActivePoints.Count); - _iterationFound = false; - } - - var basePoint = _state.ActivePoints[_iterationListIndex]; - - point = _pds.AddNextPoint(basePoint, ref _settings, ref _state); - - // Set this now, return later after processing is complete. - _iterationFound |= point != null; - - // Iteration loop advance. - _iterationPosition++; - if (_iterationPosition == _pointsPerIteration) - { - // Reached end of this iteration. - _iterationPosition = 0; - if (!_iterationFound) - _state.ActivePoints.RemoveAt(_iterationListIndex); - } - - if (point != null) - return true; - } - point = null; - return false; - } - } - - internal struct State - { - public Vector2?[,] Grid; - public List ActivePoints; - } - - internal struct SampleSettings - { - public Vector2 TopLeft, LowerRight, Center; - public Vector2 Dimensions; - public float? RejectionSqDistance; - public float MinimumDistance; - public float CellSize; - public int GridWidth, GridHeight; - } -} - - - diff --git a/Content.Server/Worldgen/WorldGen.cs b/Content.Server/Worldgen/WorldGen.cs deleted file mode 100644 index 1ed20b9f1f..0000000000 --- a/Content.Server/Worldgen/WorldGen.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Diagnostics.Contracts; -using System.Numerics; - -namespace Content.Server.Worldgen; - -/// -/// Contains a few world-generation related constants and static functions. -/// -public static class WorldGen -{ - /// - /// The size of each chunk (isn't that self-explanatory.) - /// Be careful about how small you make this. - /// - public const int ChunkSize = 128; - - /// - /// Converts world coordinates to chunk coordinates. - /// - /// World coordinates - /// Chunk coordinates - [Pure] - public static Vector2i WorldToChunkCoords(Vector2i inp) - { - return (inp * new Vector2(1.0f / ChunkSize, 1.0f / ChunkSize)).Floored(); - } - - /// - /// Converts world coordinates to chunk coordinates. - /// - /// World coordinates - /// Chunk coordinates - [Pure] - public static Vector2 WorldToChunkCoords(Vector2 inp) - { - return inp * new Vector2(1.0f / ChunkSize, 1.0f / ChunkSize); - } - - /// - /// Converts chunk coordinates to world coordinates. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoords(Vector2i inp) - { - return inp * ChunkSize; - } - - /// - /// Converts chunk coordinates to world coordinates. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoords(Vector2 inp) - { - return inp * ChunkSize; - } - - /// - /// Converts chunk coordinates to world coordinates, getting the center of the chunk. - /// - /// Chunk coordinates - /// World coordinates - [Pure] - public static Vector2 ChunkToWorldCoordsCentered(Vector2i inp) - { - return inp * ChunkSize + Vector2i.One * (ChunkSize / 2); - } -} - diff --git a/Content.Shared/CCVar/CCVars.Worldgen.cs b/Content.Shared/CCVar/CCVars.Worldgen.cs deleted file mode 100644 index da165ce74a..0000000000 --- a/Content.Shared/CCVar/CCVars.Worldgen.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Robust.Shared.Configuration; - -namespace Content.Shared.CCVar; - -public sealed partial class CCVars -{ - /// - /// Whether or not world generation is enabled. - /// - public static readonly CVarDef WorldgenEnabled = - CVarDef.Create("worldgen.enabled", false, CVar.SERVERONLY); - - /// - /// The worldgen config to use. - /// - public static readonly CVarDef WorldgenConfig = - CVarDef.Create("worldgen.worldgen_config", "Default", CVar.SERVERONLY); -} diff --git a/Resources/Locale/en-US/worldgen/applyworldgenconfig.ftl b/Resources/Locale/en-US/worldgen/applyworldgenconfig.ftl deleted file mode 100644 index a2144d08b2..0000000000 --- a/Resources/Locale/en-US/worldgen/applyworldgenconfig.ftl +++ /dev/null @@ -1,4 +0,0 @@ -cmd-applyworldgenconfig-description = Applies the given worldgen configuration to a map, setting it up for chunk loading/etc. -cmd-applyworldgenconfig-help = applyworldgenconfig -cmd-applyworldgenconfig-prototype = worldgen config prototype -cmd-applyworldgenconfig-success = Config applied successfully. Do not rerun this command on this map. diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 96e30a5aa7..5a96e342fb 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/salvage.yml b/Resources/Prototypes/Entities/Structures/Machines/salvage.yml index cafdc7cef2..dad4176617 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/salvage.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/salvage.yml @@ -54,11 +54,3 @@ - type: SalvageMagnet - type: ApcPowerReceiver powerLoad: 1000 - -- type: weightedRandomEntity - id: RandomAsteroidPool - weights: - AsteroidSalvageSmall: 3 - AsteroidSalvageMedium: 7 - AsteroidSalvageLarge: 5 - AsteroidSalvageHuge: 3 diff --git a/Resources/Prototypes/Entities/World/Debris/asteroids.yml b/Resources/Prototypes/Entities/World/Debris/asteroids.yml deleted file mode 100644 index bb33801e2d..0000000000 --- a/Resources/Prototypes/Entities/World/Debris/asteroids.yml +++ /dev/null @@ -1,144 +0,0 @@ -- type: entity - id: BaseAsteroidDebris - parent: BaseDebris - name: asteroid debris - abstract: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorTileset: - - FloorAsteroidSand - blobDrawProb: 0.5 - radius: 6 - floorPlacements: 16 - - type: SimpleFloorPlanPopulator - entries: - FloorAsteroidSand: - - id: WallRock - prob: 0.5 - orGroup: rock - - id: WallRockCoal - prob: 0.15 - orGroup: rock - - id: WallRockTin - prob: 0.15 - orGroup: rock - - id: WallRockQuartz - prob: 0.15 - orGroup: rock - - id: WallRockSalt - prob: 0.15 - orGroup: rock - - id: WallRockGold - prob: 0.05 - orGroup: rock - - id: WallRockDiamond - prob: 0.005 - orGroup: rock - - id: WallRockSilver - prob: 0.05 - orGroup: rock - - id: WallRockPlasma - prob: 0.05 - orGroup: rock - - id: WallRockUranium - prob: 0.02 - orGroup: rock - - id: WallRockBananium - prob: 0.02 - orGroup: rock - - id: WallRockArtifactFragment - prob: 0.01 - orGroup: rock - - type: IFF - flags: HideLabel - color: "#d67e27" - -- type: entity - id: AsteroidDebrisSmall - parent: BaseAsteroidDebris - name: asteroid debris small - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 8 - -- type: entity - id: AsteroidDebrisMedium - parent: BaseAsteroidDebris - name: asteroid debris medium - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 16 - -- type: entity - id: AsteroidDebrisLarge - parent: BaseAsteroidDebris - name: asteroid debris large - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 24 - -- type: entity - id: AsteroidDebrisLarger - parent: BaseAsteroidDebris - name: asteroid debris larger - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - radius: 12 - floorPlacements: 36 - -- type: entity - id: AsteroidSalvageSmall - parent: BaseAsteroidDebris - name: salvage asteroid small - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - blobDrawProb: 0.66 - radius: 15 - floorPlacements: 100 - -- type: entity - id: AsteroidSalvageMedium - parent: BaseAsteroidDebris - name: salvage asteroid medium - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - blobDrawProb: 0.66 - radius: 17 - floorPlacements: 150 - -- type: entity - id: AsteroidSalvageLarge - parent: BaseAsteroidDebris - name: salvage asteroid large - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - blobDrawProb: 0.66 - radius: 20 - floorPlacements: 200 - -- type: entity - id: AsteroidSalvageHuge - parent: BaseAsteroidDebris - name: salvage asteroid huge - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - blobDrawProb: 0.66 - radius: 23 - floorPlacements: 250 diff --git a/Resources/Prototypes/Entities/World/Debris/base_debris.yml b/Resources/Prototypes/Entities/World/Debris/base_debris.yml deleted file mode 100644 index c125d991fd..0000000000 --- a/Resources/Prototypes/Entities/World/Debris/base_debris.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: entity - id: BaseDebris - abstract: true - components: - - type: OwnedDebris - - type: LocalityLoader diff --git a/Resources/Prototypes/Entities/World/Debris/wrecks.yml b/Resources/Prototypes/Entities/World/Debris/wrecks.yml deleted file mode 100644 index 7bbeadeb5b..0000000000 --- a/Resources/Prototypes/Entities/World/Debris/wrecks.yml +++ /dev/null @@ -1,80 +0,0 @@ -- type: entity - id: BaseScrapDebris - parent: BaseDebris - name: scrap debris - abstract: true - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorTileset: - - Plating - - Plating - - Plating - - FloorSteel - - Lattice - blobDrawProb: 0.5 - radius: 6 - floorPlacements: 16 - - type: SimpleFloorPlanPopulator - entries: - Plating: - - prob: 3 # Intentional blank. - - id: SalvageMaterialCrateSpawner - prob: 1 - - id: SalvageCanisterSpawner - prob: 0.2 - - id: SalvageMobSpawner - prob: 0.7 - - id: WallSolid - prob: 1 - - id: Grille - prob: 0.5 - Lattice: - - prob: 2 - - id: Grille - prob: 0.2 - - id: SalvageMaterialCrateSpawner - prob: 0.3 - - id: SalvageCanisterSpawner - prob: 0.2 - FloorSteel: - - prob: 3 # Intentional blank. - - id: SalvageMaterialCrateSpawner - prob: 1 - - id: SalvageCanisterSpawner - prob: 0.2 - - id: SalvageMobSpawner - prob: 0.7 - - type: IFF - flags: HideLabel - color: "#88b0d1" - -- type: entity - id: ScrapDebrisSmall - parent: BaseScrapDebris - name: scrap debris small - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 8 - -- type: entity - id: ScrapDebrisMedium - parent: BaseScrapDebris - name: scrap debris medium - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 16 - -- type: entity - id: ScrapDebrisLarge - parent: BaseScrapDebris - name: scrap debris large - categories: [ HideSpawnMenu ] - components: - - type: MapGrid - - type: BlobFloorPlanBuilder - floorPlacements: 24 diff --git a/Resources/Prototypes/Entities/World/chunk.yml b/Resources/Prototypes/Entities/World/chunk.yml deleted file mode 100644 index c7cb09c2a4..0000000000 --- a/Resources/Prototypes/Entities/World/chunk.yml +++ /dev/null @@ -1,14 +0,0 @@ -- type: entity - id: WorldChunk - parent: MarkerBase - name: world chunk - description: | - It's rude to stare. - It's also a bit odd you're looking at the abstract representation of the grid of reality. - categories: [ HideSpawnMenu ] - components: - - type: WorldChunk - - type: Sprite - sprite: Markers/cross.rsi - layers: - - state: blue diff --git a/Resources/Prototypes/World/Biomes/basic.yml b/Resources/Prototypes/World/Biomes/basic.yml deleted file mode 100644 index 5ecd85bbc4..0000000000 --- a/Resources/Prototypes/World/Biomes/basic.yml +++ /dev/null @@ -1,26 +0,0 @@ -- type: spaceBiome - id: AsteroidsStandard - priority: 0 # This probably shouldn't get selected. - noiseRanges: {} - chunkComponents: - - type: DebrisFeaturePlacerController - densityNoiseChannel: Density - - type: SimpleDebrisSelector - debrisTable: - - id: AsteroidDebrisSmall - - id: AsteroidDebrisMedium - - id: AsteroidDebrisLarge - prob: 0.7 - - id: AsteroidDebrisLarger - prob: 0.4 - - type: NoiseDrivenDebrisSelector - noiseChannel: Wreck - debrisTable: - - id: ScrapDebrisSmall - - id: ScrapDebrisMedium - - id: ScrapDebrisLarge - prob: 0.5 - - type: NoiseRangeCarver - ranges: - - 0.4, 0.6 - noiseChannel: Carver diff --git a/Resources/Prototypes/World/Biomes/failsafes.yml b/Resources/Prototypes/World/Biomes/failsafes.yml deleted file mode 100644 index 5e3c50b44c..0000000000 --- a/Resources/Prototypes/World/Biomes/failsafes.yml +++ /dev/null @@ -1,21 +0,0 @@ -- type: spaceBiome - id: Failsafe - priority: -999999 # This DEFINITELY shouldn't get selected! - noiseRanges: {} - -- type: spaceBiome - id: AsteroidsFallback - priority: -999998 # This probably shouldn't get selected. - noiseRanges: {} - chunkComponents: - - type: DebrisFeaturePlacerController - densityNoiseChannel: Density - - type: SimpleDebrisSelector - debrisTable: - - id: AsteroidDebrisSmall - - id: AsteroidDebrisMedium - - id: AsteroidDebrisLarge - prob: 0.7 - - id: AsteroidDebrisLarger - prob: 0.4 - diff --git a/Resources/Prototypes/World/noise_channels.yml b/Resources/Prototypes/World/noise_channels.yml deleted file mode 100644 index 668b338dd3..0000000000 --- a/Resources/Prototypes/World/noise_channels.yml +++ /dev/null @@ -1,44 +0,0 @@ -- type: noiseChannel - id: Density - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - clippingRanges: - - 0.4, 0.6 - clippedValue: 1.658 # magic number for chunk size. - inputMultiplier: 6 # Makes density hopefully low noise in the local area while still being interesting at scale. - outputMultiplier: 50.0 # We scale density up significantly for more human-friendly numbers. - minimum: 45.0 - -- type: noiseChannel - id: DensityUnclipped - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 # Makes density hopefully low noise in the local area while still being interesting at scale. - outputMultiplier: 50.0 # We scale density up significantly for more human-friendly numbers. - minimum: 45.0 - -- type: noiseChannel - id: Carver - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 - -- type: noiseChannel - id: Wreck - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - clippingRanges: - - 0.0, 0.4 - clippedValue: 0 - remapTo0Through1: true - inputMultiplier: 16 # Makes wreck concentration very low noise at scale. - -- type: noiseChannel - id: Temperature - noiseType: Perlin - fractalLacunarityByPi: 0.666666666 - remapTo0Through1: true - inputMultiplier: 6 # Makes wreck concentration very low noise at scale. diff --git a/Resources/Prototypes/World/worldgen_default.yml b/Resources/Prototypes/World/worldgen_default.yml deleted file mode 100644 index af52c30cf1..0000000000 --- a/Resources/Prototypes/World/worldgen_default.yml +++ /dev/null @@ -1,9 +0,0 @@ -- type: worldgenConfig - id: Default - components: - - type: WorldController - - type: BiomeSelection - biomes: - - AsteroidsFallback - - Failsafe - - AsteroidsStandard From ee618e3037132019d1b6c8111e9ced9b5fdfe52e Mon Sep 17 00:00:00 2001 From: Samuka <47865393+Samuka-C@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:30:12 -0300 Subject: [PATCH 055/247] Add tile gun module (#41503) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add tile gun module * fix stacks being loaded all at once in guns * fix tile gun module * fixed indentation * remove not necessary stuff Co-authored-by: āda * Use alias Co-authored-by: āda * fix OnBallisticInteractUsing * forgot one Co-authored-by: āda * fix FillAmmo * add another todo * pass user instead of event args Co-authored-by: āda * fix last commit * update todo Co-authored-by: āda * wrong place * move gun to first slot * custom sprite * call a event * important detail in the commentary * remove recipe * not used * also not used anymore * when did i remove this? * fix sprite * shoot faster * move to shared * use API method * remove useless stuff * Revert "remove useless stuff" This reverts commit b198303aed7eb50aafb60e5a91b7af535219006b. * fix code * shot sound * change spread * fix attribution * load sound * empty sound * commentary * more commentary fix * rename event * remove duplicate * who knew * turn to mono * yet another beep * increase volume * make it a crowbar * make it able to self load clicking tiles * make it able to pry tiles * who needs a crowbar anymore * make it networked * no more hands * move BeforeAmmoLoadedEvent call to TryBallisticInsert * before now * change sprite * slight update to sprite * mag visuals * move split back to server * get one method * remove subscription * use API method in the gun system * move API call up Co-authored-by: āda * remove unused stuff * fix get one method * comment change * half the firerate --------- Co-authored-by: āda Co-authored-by: beck-thompson Co-authored-by: iaada --- Content.Server/Stack/StackSystem.cs | 4 +- .../Stacks/SharedStackSystem.API.cs | 23 +++++++ .../BallisticAmmoInteractLoaderComponent.cs | 11 ++++ .../Ranged/Events/BeforeAmmoLoadedEvent.cs | 21 +++++++ .../Systems/SharedGunSystem.Ballistic.cs | 34 +++++++++- .../Audio/Weapons/Guns/Empty/attributions.yml | 4 ++ .../Audio/Weapons/Guns/Empty/empty_beep.ogg | Bin 0 -> 4175 bytes .../Weapons/Guns/Gunshots/attributions.yml | 5 ++ .../Weapons/Guns/Gunshots/magnetic_shot.ogg | Bin 0 -> 9951 bytes .../Audio/Weapons/Guns/MagIn/attributions.yml | 4 ++ .../Audio/Weapons/Guns/MagIn/tile_load.ogg | Bin 0 -> 6374 bytes .../Specific/Robotics/borg_modules.yml | 16 +++++ .../Objects/Weapons/Guns/tile_gun.yml | 58 ++++++++++++++++++ .../Actions/actions_borg.rsi/meta.json | 3 + .../actions_borg.rsi/xenoborg-tile-module.png | Bin 0 -> 275 bytes .../borgmodule.rsi/icon-xenoborg-tile.png | Bin 0 -> 179 bytes .../Robotics/borgmodule.rsi/meta.json | 3 + .../Weapons/Guns/Basic/tilegun.rsi/icon.png | Bin 0 -> 410 bytes .../Weapons/Guns/Basic/tilegun.rsi/mag-1.png | Bin 0 -> 191 bytes .../Weapons/Guns/Basic/tilegun.rsi/mag-2.png | Bin 0 -> 192 bytes .../Weapons/Guns/Basic/tilegun.rsi/mag-3.png | Bin 0 -> 193 bytes .../Weapons/Guns/Basic/tilegun.rsi/mag-4.png | Bin 0 -> 194 bytes .../Weapons/Guns/Basic/tilegun.rsi/mag-5.png | Bin 0 -> 195 bytes .../Weapons/Guns/Basic/tilegun.rsi/mag-6.png | Bin 0 -> 193 bytes .../Weapons/Guns/Basic/tilegun.rsi/meta.json | 40 ++++++++++++ 25 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 Content.Shared/Weapons/Ranged/Components/BallisticAmmoInteractLoaderComponent.cs create mode 100644 Content.Shared/Weapons/Ranged/Events/BeforeAmmoLoadedEvent.cs create mode 100644 Resources/Audio/Weapons/Guns/Empty/attributions.yml create mode 100644 Resources/Audio/Weapons/Guns/Empty/empty_beep.ogg create mode 100644 Resources/Audio/Weapons/Guns/Gunshots/magnetic_shot.ogg create mode 100644 Resources/Audio/Weapons/Guns/MagIn/attributions.yml create mode 100644 Resources/Audio/Weapons/Guns/MagIn/tile_load.ogg create mode 100644 Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml create mode 100644 Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-tile-module.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-tile.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/icon.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-1.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-2.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-3.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-4.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-5.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/mag-6.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Basic/tilegun.rsi/meta.json diff --git a/Content.Server/Stack/StackSystem.cs b/Content.Server/Stack/StackSystem.cs index a0d923dd1e..a6b75f953c 100644 --- a/Content.Server/Stack/StackSystem.cs +++ b/Content.Server/Stack/StackSystem.cs @@ -21,7 +21,9 @@ namespace Content.Server.Stack /// Spawns a new entity and moves an amount to it from the stack. /// Moves nothing if amount is greater than ent's stack count. /// - /// How much to move to the new entity. + /// Entity to split in a new stack. + /// How much to move to the new entity. + /// Where to spawn the new stack /// Null if StackComponent doesn't resolve, or amount to move is greater than ent has available. [PublicAPI] public EntityUid? Split(Entity ent, int amount, EntityCoordinates spawnPosition) diff --git a/Content.Shared/Stacks/SharedStackSystem.API.cs b/Content.Shared/Stacks/SharedStackSystem.API.cs index 1356c8ecda..372255701c 100644 --- a/Content.Shared/Stacks/SharedStackSystem.API.cs +++ b/Content.Shared/Stacks/SharedStackSystem.API.cs @@ -7,6 +7,29 @@ namespace Content.Shared.Stacks; // Partial for public API functions. public abstract partial class SharedStackSystem { + #region Spawning + // Interactions with spawned entities can not currently be predicted. + // This means that when spawning a stack it should not be given directly to the player, but have some intermediary. + + /// + /// Gets or spawns an entity with a stack count of 1. + /// Useful when you don't know if something is a stack, and want to make sure you just have a single entity. + /// + /// An entity to pop one count off the stack. + /// An entity with a stack count of 1, or a non-stack. + [PublicAPI] + public EntityUid GetOne(Entity stackEnt) + { + if (!Resolve(stackEnt.Owner, ref stackEnt.Comp, logMissing: false) // If it's not a stack, you already have the one + || stackEnt.Comp.Count == 1) // If it's at one, just use this + return stackEnt.Owner; + + ReduceCount(stackEnt, 1); + var stackId = _prototype.Index(stackEnt.Comp.StackTypeId); + return PredictedSpawnNextToOrDrop(stackId.Spawn, stackEnt.Owner); + } + + #endregion #region Merge Stacks /// diff --git a/Content.Shared/Weapons/Ranged/Components/BallisticAmmoInteractLoaderComponent.cs b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoInteractLoaderComponent.cs new file mode 100644 index 0000000000..04d4a80774 --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Components/BallisticAmmoInteractLoaderComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Weapons.Ranged.Components; + +/// +/// If an entity with has this component, it can be used to interact +/// with the ammo entity to load it into the gun (or magazine). +/// Basically the reverse order (used vs target) to achieve the same result (loading the gun) +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class BallisticAmmoInteractLoaderComponent : Component; diff --git a/Content.Shared/Weapons/Ranged/Events/BeforeAmmoLoadedEvent.cs b/Content.Shared/Weapons/Ranged/Events/BeforeAmmoLoadedEvent.cs new file mode 100644 index 0000000000..896bfbcb6d --- /dev/null +++ b/Content.Shared/Weapons/Ranged/Events/BeforeAmmoLoadedEvent.cs @@ -0,0 +1,21 @@ +using Content.Shared.Weapons.Ranged.Components; + +namespace Content.Shared.Weapons.Ranged.Events; + +/// +/// Raised on the ammo before it is loaded into a gun (or a magazine) +/// +[ByRefEvent] +public struct BeforeAmmoLoadedEvent() +{ + /// + /// if the entity can be used to load the gun or magazine + /// + public bool CanLoad = true; + + /// + /// If null the entity itself is used to load a gun or magazine, + /// if not null, the entity provided is used to load the gun or magazine + /// + public EntityUid? AmmoOverride; +} diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index 831db83c3b..eb80b26b58 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -3,6 +3,7 @@ using Content.Shared.Emp; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Stacks; using Content.Shared.Verbs; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; @@ -16,6 +17,7 @@ public abstract partial class SharedGunSystem { [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly SharedStackSystem _stack = null!; [MustCallBase] protected virtual void InitializeBallistic() @@ -34,6 +36,8 @@ public abstract partial class SharedGunSystem SubscribeLocalEvent(OnBallisticRefillerMapInit); SubscribeLocalEvent(OnRefillerEmpPulsed); + + SubscribeLocalEvent(OnBallisticAmmoLoad); } private void OnBallisticRefillerMapInit(Entity entity, ref MapInitEvent _) @@ -324,11 +328,20 @@ public abstract partial class SharedGunSystem bool suppressInsertionSound = false ) { - if (!CanInsertBallistic(entity, inserted)) + inserted = _stack.GetOne(inserted); + var ammoEv = new BeforeAmmoLoadedEvent(); + RaiseLocalEvent(inserted, ref ammoEv); + + if (!ammoEv.CanLoad) return false; - entity.Comp.Entities.Add(inserted); - Containers.Insert(inserted, entity.Comp.Container); + var ammo = ammoEv.AmmoOverride ?? inserted; + + if (!CanInsertBallistic(entity, ammo)) + return false; + + entity.Comp.Entities.Add(ammo); + Containers.Insert(ammo, entity.Comp.Container); if (!suppressInsertionSound) { Audio.PlayPredicted(entity.Comp.SoundInsert, entity, user); @@ -369,6 +382,21 @@ public abstract partial class SharedGunSystem PauseSelfRefill(entity, args.Duration); } + private void OnBallisticAmmoLoad(Entity ent, ref AfterInteractEvent args) + { + if (args.Handled || args.Target == null) + return; + + if (!TryComp(ent, out var ballisticAmmoProviderComp)) + return; + + if (TryBallisticInsert( + (ent, ballisticAmmoProviderComp), + args.Target.Value, + args.User)) + args.Handled = true; + } + private void UpdateBallistic(float frameTime) { var query = EntityQueryEnumerator(); diff --git a/Resources/Audio/Weapons/Guns/Empty/attributions.yml b/Resources/Audio/Weapons/Guns/Empty/attributions.yml new file mode 100644 index 0000000000..2789237e39 --- /dev/null +++ b/Resources/Audio/Weapons/Guns/Empty/attributions.yml @@ -0,0 +1,4 @@ +- files: ["empty_beep.ogg"] + license: "CC-BY-4.0" + copyright: "Created by altemark" + source: "https://freesound.org/people/altemark/sounds/39747" diff --git a/Resources/Audio/Weapons/Guns/Empty/empty_beep.ogg b/Resources/Audio/Weapons/Guns/Empty/empty_beep.ogg new file mode 100644 index 0000000000000000000000000000000000000000..512cd8c12fe3c3dd68bf8c155835de56d82ea1a1 GIT binary patch literal 4175 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 BjjI3v literal 0 HcmV?d00001 diff --git a/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml b/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml index 1a0136111c..167935d540 100644 --- a/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml +++ b/Resources/Audio/Weapons/Guns/Gunshots/attributions.yml @@ -42,3 +42,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 0000000000000000000000000000000000000000..9f77a610bbe756d133ffc81fb28e689deb70aa72 GIT binary patch literal 9951 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 x+m&toS-Y&i(cwy!7_}6B11s24^H$p4D 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 VdX>2qIe->3c)I$ztaD0e0sw_kHMRf% literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json index 345a622f3f..df98b24960 100644 --- a/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/meta.json @@ -154,6 +154,9 @@ { "name": "icon-xenoborg-sword2" }, + { + "name": "icon-xenoborg-tile" + }, { "name": "icon-xenoborg-tools" }, 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 0000000000000000000000000000000000000000..5e345c10e74dda39c5979e3fac7bc44263b7176c GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`SO)lnxB_V* zAqjDDd0AOCWkq!zO#@>?Q#WV#h_HyPjI8?ldM++kLB32O?tVqyo36UQ&K^GrVRPyy zg#$GUmjw9*GyF#b)28jqyJ`=V{^0527!uL?cA7ii5d|Js-;Hyp?);zYuF*T~K+onA z9yLc^)i!GAF$AAK>7Hrf8f*P)&SE{;<~?N}EZm(AU3xh2_)f#N37oY`Q3Z3FH@98u z?K^1q^x-spf$)S=ZIaJD7#cyHa*Lx&N9ny6AC*&cf6aS6 z`PG#x-X_1Br+;C;VOh{m$U&>gTe~DWM4fq_?q@ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..171ad181625a3e7903d1d77c06965167cdc88102 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|ECYN(T!FNZ zkc7CnysWI6vZA_@hFJ6_CrC_enANwl{|w+{^TN% O3I Date: Fri, 3 Apr 2026 21:46:08 +0000 Subject: [PATCH 056/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 611fde4453..9219d5e6a5 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4031,3 +4023,11 @@ 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 From 8d052e7f8510bf169acc7b7ffeb5e8018451257f Mon Sep 17 00:00:00 2001 From: baynarikattu <189896646+baynarikattu@users.noreply.github.com> Date: Sat, 4 Apr 2026 01:29:08 +0300 Subject: [PATCH 057/247] fix: small typo in the restart alert V2 (#43454) fix: round end typo Co-authored-by: baynarikattu <> --- Content.Server/RoundEnd/RoundEndSystem.cs | 2 +- Resources/Locale/en-US/round-end/round-end-system.ftl | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Content.Server/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs index ed1ea90a16..57fabd7787 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -313,7 +313,7 @@ namespace Content.Server.RoundEnd Loc.GetString( "round-end-system-round-restart-eta-announcement", ("time", time), - ("units", Loc.GetString(unitsLocString)))); + ("units", Loc.GetString(unitsLocString, ("amount", time))))); Timer.Spawn(countdownTime.Value, AfterEndRoundRestart, _countdownTokenSource.Token); } diff --git a/Resources/Locale/en-US/round-end/round-end-system.ftl b/Resources/Locale/en-US/round-end/round-end-system.ftl index 30069f7171..6068e4385e 100644 --- a/Resources/Locale/en-US/round-end/round-end-system.ftl +++ b/Resources/Locale/en-US/round-end/round-end-system.ftl @@ -7,5 +7,11 @@ round-end-system-shuttle-recalled-announcement = The emergency shuttle has been round-end-system-shuttle-sender-announcement = Station round-end-system-round-restart-eta-announcement = Restarting the round in {$time} {$units}... -eta-units-minutes = minutes -eta-units-seconds = seconds +eta-units-minutes = {$amount -> + [one] minute + *[other] minutes +} +eta-units-seconds = {$amount -> + [one] second + *[other] seconds +} From 4fb78b777c498b18337c88b9e4303b2f705d30c8 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 4 Apr 2026 01:22:43 +0200 Subject: [PATCH 058/247] fix tailwag action for changelings (#43452) --- .../Cloning/CloningSystem.Subscriptions.cs | 4 ++-- Content.Server/Wagging/WaggingSystem.cs | 8 +++++++- .../Movement/Systems/SharedJumpAbilitySystem.cs | 8 +++++--- Content.Shared/Rootable/RootableSystem.cs | 7 +++++-- Content.Shared/Sericulture/SericultureSystem.cs | 16 +++++++++------- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Content.Server/Cloning/CloningSystem.Subscriptions.cs b/Content.Server/Cloning/CloningSystem.Subscriptions.cs index a05c7069f0..10ac8bc9ed 100644 --- a/Content.Server/Cloning/CloningSystem.Subscriptions.cs +++ b/Content.Server/Cloning/CloningSystem.Subscriptions.cs @@ -53,7 +53,7 @@ public sealed partial class CloningSystem SubscribeLocalEvent(OnCloneVocal); SubscribeLocalEvent(OnCloneStorage); SubscribeLocalEvent(OnCloneInventory); - SubscribeLocalEvent(OnCloneInventory); + SubscribeLocalEvent(OnCloneMovementSpeedModifier); } private void OnCloneItemStack(Entity ent, ref CloningItemEvent args) @@ -120,7 +120,7 @@ public sealed partial class CloningSystem _inventory.CopyComponent(ent.AsNullable(), args.CloneUid); } - private void OnCloneInventory(Entity ent, ref CloningEvent args) + private void OnCloneMovementSpeedModifier(Entity ent, ref CloningEvent args) { if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; diff --git a/Content.Server/Wagging/WaggingSystem.cs b/Content.Server/Wagging/WaggingSystem.cs index 6ece6f0d95..d369ea8689 100644 --- a/Content.Server/Wagging/WaggingSystem.cs +++ b/Content.Server/Wagging/WaggingSystem.cs @@ -35,7 +35,13 @@ public sealed class WaggingSystem : EntitySystem if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; - EnsureComp(args.CloneUid); + // Make sure to set the datafields before adding the component so that the correct action gets spawned on map init. + var cloneComp = Factory.GetComponent(); + cloneComp.Action = ent.Comp.Action; + cloneComp.Layer = ent.Comp.Layer; + cloneComp.Organ = ent.Comp.Organ; + cloneComp.Suffix = ent.Comp.Suffix; + AddComp(args.CloneUid, cloneComp, true); } private void OnWaggingMapInit(Entity ent, ref MapInitEvent args) diff --git a/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs b/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs index 598e4b564a..d55596ad7d 100644 --- a/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs +++ b/Content.Shared/Movement/Systems/SharedJumpAbilitySystem.cs @@ -99,13 +99,15 @@ public sealed partial class SharedJumpAbilitySystem : EntitySystem if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; + // Make sure to set the datafields before adding the component so that the correct action gets spawned on map init. var targetComp = Factory.GetComponent(); targetComp.Action = ent.Comp.Action; - targetComp.CanCollide = ent.Comp.CanCollide; - targetComp.JumpSound = ent.Comp.JumpSound; - targetComp.CollideKnockdown = ent.Comp.CollideKnockdown; targetComp.JumpDistance = ent.Comp.JumpDistance; targetComp.JumpThrowSpeed = ent.Comp.JumpThrowSpeed; + targetComp.CanCollide = ent.Comp.CanCollide; + targetComp.CollideKnockdown = ent.Comp.CollideKnockdown; + targetComp.JumpSound = ent.Comp.JumpSound; + targetComp.JumpFailedPopup = ent.Comp.JumpFailedPopup; AddComp(args.CloneUid, targetComp, true); } } diff --git a/Content.Shared/Rootable/RootableSystem.cs b/Content.Shared/Rootable/RootableSystem.cs index 1d718dbdf3..1b9be3537b 100644 --- a/Content.Shared/Rootable/RootableSystem.cs +++ b/Content.Shared/Rootable/RootableSystem.cs @@ -121,12 +121,15 @@ public sealed class RootableSystem : EntitySystem if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; - var cloneComp = EnsureComp(args.CloneUid); + // Make sure to set the datafields before adding the component so that the correct action gets spawned on map init. + var cloneComp = Factory.GetComponent(); + cloneComp.Action = ent.Comp.Action; + cloneComp.RootedAlert = ent.Comp.RootedAlert; cloneComp.TransferRate = ent.Comp.TransferRate; cloneComp.TransferFrequency = ent.Comp.TransferFrequency; cloneComp.SpeedModifier = ent.Comp.SpeedModifier; cloneComp.RootSound = ent.Comp.RootSound; - Dirty(args.CloneUid, cloneComp); + AddComp(args.CloneUid, cloneComp, true); } private void OnRootableMapInit(Entity ent, ref MapInitEvent args) diff --git a/Content.Shared/Sericulture/SericultureSystem.cs b/Content.Shared/Sericulture/SericultureSystem.cs index fb87c907f4..e733b93483 100644 --- a/Content.Shared/Sericulture/SericultureSystem.cs +++ b/Content.Shared/Sericulture/SericultureSystem.cs @@ -41,13 +41,15 @@ public abstract partial class SharedSericultureSystem : EntitySystem if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) return; - var comp = EnsureComp(args.CloneUid); - comp.PopupText = ent.Comp.PopupText; - comp.ProductionLength = ent.Comp.ProductionLength; - comp.HungerCost = ent.Comp.HungerCost; - comp.EntityProduced = ent.Comp.EntityProduced; - comp.MinHungerThreshold = ent.Comp.MinHungerThreshold; - Dirty(args.CloneUid, comp); + // Make sure to set the datafields before adding the component so that the correct action gets spawned on map init. + var cloneComp = Factory.GetComponent(); + cloneComp.PopupText = ent.Comp.PopupText; + cloneComp.EntityProduced = ent.Comp.EntityProduced; + cloneComp.Action = ent.Comp.Action; + cloneComp.ProductionLength = ent.Comp.ProductionLength; + cloneComp.HungerCost = ent.Comp.HungerCost; + cloneComp.MinHungerThreshold = ent.Comp.MinHungerThreshold; + AddComp(args.CloneUid, cloneComp, true); } /// From 85b4a5d0f604ee68e7337f682de4a3d9efc92e55 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 4 Apr 2026 02:29:43 +0200 Subject: [PATCH 059/247] changeling devour cleanup (#43451) * devour cleanup * fix audio cancel * fix for transform as well --- .../Components/ChangelingDevourComponent.cs | 57 +++++------ .../Systems/ChangelingDevourSystem.cs | 99 ++++++++----------- .../Systems/ChangelingTransformSystem.cs | 7 +- .../Locale/en-US/changeling/changeling.ftl | 3 +- 4 files changed, 75 insertions(+), 91 deletions(-) diff --git a/Content.Shared/Changeling/Components/ChangelingDevourComponent.cs b/Content.Shared/Changeling/Components/ChangelingDevourComponent.cs index f2c5c82ca9..f0a7bb44ba 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/Systems/ChangelingDevourSystem.cs b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs index 3406038e9c..d08e6315ce 100644 --- a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs @@ -2,9 +2,7 @@ using Content.Shared.Actions; using Content.Shared.Administration.Logs; using Content.Shared.Armor; using Content.Shared.Atmos.Rotting; -using Content.Shared.Body; using Content.Shared.Changeling.Components; -using Content.Shared.Damage.Components; using Content.Shared.Damage.Systems; using Content.Shared.Database; using Content.Shared.DoAfter; @@ -19,13 +17,11 @@ 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!; @@ -47,7 +43,6 @@ public sealed class ChangelingDevourSystem : EntitySystem SubscribeLocalEvent(OnDevourAction); SubscribeLocalEvent(OnDevourWindup); SubscribeLocalEvent(OnDevourConsume); - SubscribeLocalEvent>(OnConsumeAttemptTick); SubscribeLocalEvent(OnShutdown); } @@ -64,29 +59,6 @@ public sealed class ChangelingDevourSystem : EntitySystem } } - //TODO: Allow doafters to have proper update loop support. Attempt events should not be doing state changes. - private void OnConsumeAttemptTick(Entity ent, - ref DoAfterAttemptEvent eventData) - { - - 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. /// @@ -110,10 +82,13 @@ public sealed class ChangelingDevourSystem : EntitySystem 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; @@ -122,6 +97,12 @@ public sealed class ChangelingDevourSystem : EntitySystem if (target == ent.Owner) return; // don't eat yourself + if (!_mobState.IsDead(target)) + { + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-not-dead"), args.Performer, args.Performer, PopupType.Medium); + return; + } + if (HasComp(target)) { _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-rotting"), args.Performer, args.Performer, PopupType.Medium); @@ -136,6 +117,7 @@ public sealed class ChangelingDevourSystem : EntitySystem if (_net.IsServer) { + ent.Comp.CurrentDevourSound = _audio.Stop(ent.Comp.CurrentDevourSound); var pvsSound = _audio.PlayPvs(ent.Comp.DevourWindupNoise, ent); if (pvsSound != null) ent.Comp.CurrentDevourSound = pvsSound.Value.Entity; @@ -146,7 +128,7 @@ public sealed class ChangelingDevourSystem : EntitySystem _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,17 +142,21 @@ public sealed class ChangelingDevourSystem : EntitySystem 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( @@ -188,44 +174,41 @@ public sealed class ChangelingDevourSystem : EntitySystem ent.Comp.CurrentDevourSound = pvsSound.Value.Entity; } - - ent.Comp.NextTick = curTime + ent.Comp.DamageTimeBetweenTicks; - - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} began to devour {ToPrettyString(args.Target):player} identity"); + _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; - - if (target == null) - return; - - if (Exists(ent.Comp.CurrentDevourSound)) - _audio.Stop(ent.Comp.CurrentDevourSound!); + ent.Comp.CurrentDevourSound = _audio.Stop(ent.Comp.CurrentDevourSound); if (args.Cancelled) return; - if (!_mobState.IsDead((EntityUid)target)) + if (args.Target is not { } target) + return; + + _damageable.ChangeDamage(target, ent.Comp.DevourDamage, true, true, ent.Owner); + + if (!_mobState.IsDead(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); + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-not-dead"), args.User, args.User, PopupType.Medium); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} unsuccessfully devoured {ToPrettyString(args.Target):player}'s identity"); return; } @@ -238,22 +221,22 @@ public sealed class ChangelingDevourSystem : EntitySystem args.User, PopupType.LargeCaution); - if (_mobState.IsDead(target.Value) - && TryComp(target, out var body) + if (_mobState.IsDead(target) && HasComp(target) && TryComp(args.User, out var identityStorage)) { - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} successfully devoured {ToPrettyString(args.Target):player}'s identity"); - _changelingIdentitySystem.CloneToPausedMap((ent, identityStorage), target.Value); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} successfully devoured {ToPrettyString(target):player}'s identity"); + _changelingIdentitySystem.CloneToPausedMap((ent, identityStorage), target); - if (_inventorySystem.TryGetSlotEntity(target.Value, "jumpsuit", out var item) + if (_inventorySystem.TryGetSlotEntity(target, "jumpsuit", out var item) && TryComp(item, out var butcherable)) - RipClothing(target.Value, (item.Value, butcherable)); + RipClothing(target, (item.Value, butcherable)); } Dirty(ent); } + // TODO: This should just be an API method in the butcher system private void RipClothing(EntityUid victim, Entity item) { var spawnEntities = EntitySpawnCollection.GetSpawns(item.Comp.SpawnedEntities, _robustRandom); diff --git a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs index 5f9cc9526b..94d2cd77a8 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs @@ -99,7 +99,10 @@ public sealed partial class ChangelingTransformSystem : EntitySystem PopupType.MediumCaution); if (_net.IsServer) + { + ent.Comp.CurrentTransformSound = _audio.Stop(ent.Comp.CurrentTransformSound); // cancel any previous sounds first ent.Comp.CurrentTransformSound = _audio.PlayPvs(ent.Comp.TransformAttemptNoise, ent)?.Entity; + } if (TryComp(targetIdentity, out var storedIdentity) && storedIdentity.OriginalSession != null) _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} begun an attempt to transform into \"{Name(targetIdentity)}\" ({storedIdentity.OriginalSession:player}) "); @@ -146,9 +149,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem 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; diff --git a/Resources/Locale/en-US/changeling/changeling.ftl b/Resources/Locale/en-US/changeling/changeling.ftl index 57ad3550bf..e33e5bc715 100644 --- a/Resources/Locale/en-US/changeling/changeling.ftl +++ b/Resources/Locale/en-US/changeling/changeling.ftl @@ -1,6 +1,7 @@ roles-antag-changeling-name = Changeling roles-antag-changeling-objective = A intelligent predator that assumes the identities of its victims. +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,8 +9,6 @@ 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. From af5819e30e7a42aea135fec90a55d8835c12eafa Mon Sep 17 00:00:00 2001 From: 0-Anon Date: Fri, 3 Apr 2026 23:51:43 -0400 Subject: [PATCH 060/247] Traitor Syndicate Reinforcement rebalance (#43057) * Traitor Reinforcement Rebalance As per discussion with traitor workgroup, there was consensus that the reinforcements felt extremely underwhelming to use. As a result I took a stab at reworking their kits to make them all feel more unique, specialized into their respective niche, and FUNCTIONAL in that niche. Medic is now a dedicated medic with a flimsy but existent cover story, Spy is now a great low-investment / immediate-value pick, and Thief is now a bit better at breakins and other aggressive tactics while leaning into being a very obvious foreign invader. * camera bug instead of operative ID card I may have gotten too silly, so I'm drawing this back a bit. * Backpack Space Errors Put chameleon in hand, changed backpack to duffel to fit in the survival box, * Remove Necrosol, Add Viper When you think about it lead is the best medicine because your patient never gets sick again --- Resources/Prototypes/Roles/Antags/traitor.yml | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/Resources/Prototypes/Roles/Antags/traitor.yml b/Resources/Prototypes/Roles/Antags/traitor.yml index edc130ef8b..2920b2c56f 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 From cfb6e2c376776eac8d960e86b6de8b809144cc15 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 4 Apr 2026 04:07:39 +0000 Subject: [PATCH 061/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9219d5e6a5..ff82a9595f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4031,3 +4023,11 @@ 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 From 8fca1e064effacef4f1b3bab8b376be7b200d009 Mon Sep 17 00:00:00 2001 From: B_Kirill <153602297+B-Kirill@users.noreply.github.com> Date: Sat, 4 Apr 2026 16:43:03 +1000 Subject: [PATCH 062/247] Fix accessing deleted entities in SharedMeleeWeaponSystem (#42340) * Fix accessing deleted entities in SharedMeleeWeaponSystem * retry * Review --- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index f827249dd3..02d561d942 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -574,7 +574,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem _meleeSound.PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component); - if (damageResult.GetTotal() > FixedPoint2.Zero) + if (damageResult.GetTotal() > FixedPoint2.Zero && !TerminatingOrDeleted(target.Value)) { DoDamageEffect(targets, user, targetXform); } @@ -633,7 +633,15 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem // Validate client for (var i = entities.Count - 1; i >= 0; i--) { - if (ArcRaySuccessful(entities[i], + var entity = entities[i]; + + if (TerminatingOrDeleted(entity)) + { + entities.RemoveAt(i); + continue; + } + + if (!ArcRaySuccessful(entity, userPos, direction.ToWorldAngle(), component.Angle, @@ -642,11 +650,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem user, session)) { - continue; + // Bad input + entities.RemoveAt(i); } - - // Bad input - entities.RemoveAt(i); } var targets = new List(); @@ -728,6 +734,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem $"{ToPrettyString(user):actor} melee attacked (heavy) {ToPrettyString(entity):subject} using {ToPrettyString(meleeUid):tool} and dealt {damageResult.GetTotal():damage} damage"); } } + + if (TerminatingOrDeleted(entity)) + targets.RemoveAt(i); } if (entities.Count != 0) @@ -736,7 +745,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem _meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component); } - if (appliedDamage.GetTotal() > FixedPoint2.Zero) + if (appliedDamage.GetTotal() > FixedPoint2.Zero && targets.Count > 0) { DoDamageEffect(targets, user, Transform(targets[0])); } From 85550217dcd2d8c221c7cc35172864b4af51a7a4 Mon Sep 17 00:00:00 2001 From: Spanky <180730777+spanky-spanky@users.noreply.github.com> Date: Sat, 4 Apr 2026 03:00:50 -0400 Subject: [PATCH 063/247] Make Chargers Items (Pickupable) (#40645) * Itemize and add in-hands for tabletop chargers. * Tweak inhands to be vox friendly * Update copyright * Parent chargers off of BaseItem * Unparent chargers off BaseItem since it doesn't work, for now --- .../Entities/Structures/Power/chargers.yml | 9 +++++++++ .../Power/cell_recharger.rsi/inhand-left.png | Bin 0 -> 788 bytes .../Power/cell_recharger.rsi/inhand-right.png | Bin 0 -> 793 bytes .../Structures/Power/cell_recharger.rsi/meta.json | 10 +++++++++- .../Power/recharger.rsi/inhand-left.png | Bin 0 -> 751 bytes .../Power/recharger.rsi/inhand-right.png | Bin 0 -> 751 bytes .../Structures/Power/recharger.rsi/meta.json | 10 +++++++++- .../Power/turbo_recharger.rsi/inhand-left.png | Bin 0 -> 862 bytes .../Power/turbo_recharger.rsi/inhand-right.png | Bin 0 -> 862 bytes .../Power/turbo_recharger.rsi/meta.json | 10 +++++++++- 10 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 Resources/Textures/Structures/Power/cell_recharger.rsi/inhand-left.png create mode 100644 Resources/Textures/Structures/Power/cell_recharger.rsi/inhand-right.png create mode 100644 Resources/Textures/Structures/Power/recharger.rsi/inhand-left.png create mode 100644 Resources/Textures/Structures/Power/recharger.rsi/inhand-right.png create mode 100644 Resources/Textures/Structures/Power/turbo_recharger.rsi/inhand-left.png create mode 100644 Resources/Textures/Structures/Power/turbo_recharger.rsi/inhand-right.png diff --git a/Resources/Prototypes/Entities/Structures/Power/chargers.yml b/Resources/Prototypes/Entities/Structures/Power/chargers.yml index e80bb90941..469de4bad3 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/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 0000000000000000000000000000000000000000..12d689adf9f9a16da40824dacc760142e1f35ed2 GIT binary patch literal 788 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..40ab6caf127f4211e8c0d2f077f281ae8ca358a8 GIT binary patch literal 862 zcmV-k1EKthP)?9?qJcNAO+DF2Dw;+8mP7C^ie!q9UVVprTTsDtiChr>V`) zwbI=|aJ|^}$29csL!)NfKPR@`I0ff3T$MWhavc`Gz**~P-ed6Wz{Pb(OCP}HE{s3v zDI?XCoPa~q;Qbs%5-@rPLmREG+V`A2LTnqtw{Um_Ga7g{e*u+*XW2_XP7?qC0kcU& zK~#90?b<<00znuC;Md565Gqfd{RN(bAof&Oe}GWo#e+x)K|iB=1>GVOA&P>KPMwV~ zJcSgg2bm@hd(gvnh!v-FS)Ey>cpng!nc1C>&)vQ33lI?z5fKrQKbdsS=?L3-t2dQj zN7zm#opWYg-#F$cIjPZT;^MqCa_%pgr~%P%0r`VF06;d?1ppkLE&u?w)}_h%0vGbT zFq6(XJGOYj}FB0sy4lLTq&r#bQxelT0Qd z?Uwl}1LV5_(r)4MwxEnzmIWaM03a5NnXhgN#(D=xDV5`Lxvb1vmSwK8De>KacDw!S zIFU#wYlINWIwB$>A|fIpA|fIpqJLvlPQYYVAm}|N`aJIP$_cFf%7w}rU z?q>sn$O?F65wfW+qG4O>`dZ)d2Qve%tCp}9y;c?s3uUl5PSwTc=?rBczl!?YTJADIEKG1GgPqTK*D oA>taABhbOT0TB@q5m6w%0EEN+Y!G@fIRF3v07*qoM6N<$g2>B!B>(^b literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Power/turbo_recharger.rsi/meta.json b/Resources/Textures/Structures/Power/turbo_recharger.rsi/meta.json index 50271d1e33..0483760670 100644 --- a/Resources/Textures/Structures/Power/turbo_recharger.rsi/meta.json +++ b/Resources/Textures/Structures/Power/turbo_recharger.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "adapted from https://github.com/discordia-space/CEV-Eris/raw/9ea3eccbe22e18d24653949067f3d7dd12194ea9/icons/obj/stationobjs.dmi by EmoGarbage404 (github)", + "copyright": "adapted from https://github.com/discordia-space/CEV-Eris/raw/9ea3eccbe22e18d24653949067f3d7dd12194ea9/icons/obj/stationobjs.dmi by EmoGarbage404 (github), in-hands by spanky-spanky (GitHub)", "states": [ { "name": "empty" @@ -57,6 +57,14 @@ 0.05 ] ] + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 } ] } From b365636086a3786f7424ba1d2064262e97a304fa Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 4 Apr 2026 07:16:32 +0000 Subject: [PATCH 064/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ff82a9595f..9f6b2cac26 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4031,3 +4024,11 @@ 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 From 6afc7919e3f1a22c9c700ffe81af582a2ae4116a Mon Sep 17 00:00:00 2001 From: ThatGuyUSA Date: Sat, 4 Apr 2026 08:36:41 -0700 Subject: [PATCH 065/247] [BUGFIX] Midround Wizards now show up on the end results screen (#42766) very shrimple --- Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml | 1 + Resources/Prototypes/GameRules/events.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index e620f376c5..1ac053f872 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/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 8c46bae789..6368d2e23e 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -323,7 +323,7 @@ components: - type: StationEvent weight: 1 # rare - duration: 1 + duration: null earliestStart: 30 reoccurrenceDelay: 60 minimumPlayers: 30 From d521606348620687d1442c8ddbb3515a0e3acf19 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 4 Apr 2026 15:52:22 +0000 Subject: [PATCH 066/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9f6b2cac26..fc9d8f8224 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4032,3 +4025,10 @@ 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 From 92fa40b79dceeb51b950b35738c4e45233e4f639 Mon Sep 17 00:00:00 2001 From: Zekins <136648667+Zekins3366@users.noreply.github.com> Date: Sat, 4 Apr 2026 19:32:42 +0300 Subject: [PATCH 067/247] Fix Human HeadTop (#43465) forgottop --- Resources/Prototypes/Body/Species/human.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/Prototypes/Body/Species/human.yml b/Resources/Prototypes/Body/Species/human.yml index b569527a61..d6c76015e5 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 From f41670c77028176926f78328285da2087e4b120f Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 4 Apr 2026 16:49:12 +0000 Subject: [PATCH 068/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fc9d8f8224..2ec57487ce 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4032,3 +4025,11 @@ 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 From 17490ca615a8a51c36684c3813750af5ce301e14 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:40:51 +0200 Subject: [PATCH 069/247] Add random sell value to player mobs (#43445) * Initial commit * Review fixes --- .../Cargo/Components/RandomPriceComponent.cs | 38 +++++++++++++++++++ Content.Server/Cargo/Systems/PricingSystem.cs | 37 +++++++++++++++++- Resources/Prototypes/Body/species_base.yml | 2 + .../Prototypes/Entities/Mobs/Player/clone.yml | 1 + 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 Content.Server/Cargo/Components/RandomPriceComponent.cs diff --git a/Content.Server/Cargo/Components/RandomPriceComponent.cs b/Content.Server/Cargo/Components/RandomPriceComponent.cs new file mode 100644 index 0000000000..3ece8334e6 --- /dev/null +++ b/Content.Server/Cargo/Components/RandomPriceComponent.cs @@ -0,0 +1,38 @@ +using Content.Server.Cargo.Systems; + +namespace Content.Server.Cargo.Components; + +/// +/// Adds a random value between 0 and X to an entity's sell value. +/// +[RegisterComponent, Access(typeof(PricingSystem))] +public sealed partial class RandomPriceComponent : Component +{ + /// + /// The max random price the entity may be priced at. Non-inclusive. + /// + [DataField(required: true)] + public double MaxRandomPrice; + + /// + /// How the random pricing modifier (0.0 - 1.0) should be distributed. + /// + [DataField] + public RandomPricingCurve PricingCurve = RandomPricingCurve.Cubed; + + /// + /// The generated price for the specific entity. + /// + [DataField] + public double? RandomPrice = null; +} + +/// +/// The random distribution used when generating a random price. +/// +public enum RandomPricingCurve +{ + Linear, + Squared, + Cubed, +} diff --git a/Content.Server/Cargo/Systems/PricingSystem.cs b/Content.Server/Cargo/Systems/PricingSystem.cs index 0101b913d6..bbf8176035 100644 --- a/Content.Server/Cargo/Systems/PricingSystem.cs +++ b/Content.Server/Cargo/Systems/PricingSystem.cs @@ -8,13 +8,14 @@ using Content.Shared.Chemistry.Reagent; using Content.Shared.Materials; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Research.Prototypes; using Content.Shared.Stacks; using Robust.Shared.Console; using Robust.Shared.Containers; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; +using Robust.Shared.Random; using Robust.Shared.Utility; -using Content.Shared.Research.Prototypes; namespace Content.Server.Cargo.Systems; @@ -24,6 +25,7 @@ namespace Content.Server.Cargo.Systems; public sealed class PricingSystem : EntitySystem { [Dependency] private readonly IConsoleHost _consoleHost = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; @@ -32,6 +34,8 @@ public sealed class PricingSystem : EntitySystem public override void Initialize() { SubscribeLocalEvent(CalculateMobPrice); + SubscribeLocalEvent(SetRandomPrice); + SubscribeLocalEvent(CalculateRandomPrice); _consoleHost.RegisterCommand("appraisegrid", "Calculates the total value of the given grids.", @@ -95,6 +99,37 @@ public sealed class PricingSystem : EntitySystem args.Price += component.Price * (_mobStateSystem.IsAlive(uid, state) ? 1.0 : component.DeathPenalty); } + private void SetRandomPrice(Entity entity, ref MapInitEvent args) + { + if (entity.Comp.RandomPrice == null) + { + var modifier = _random.NextDouble(); + switch (entity.Comp.PricingCurve) + { + default: + case RandomPricingCurve.Linear: + break; + case RandomPricingCurve.Squared: + modifier = modifier * modifier; + break; + case RandomPricingCurve.Cubed: + modifier = modifier * modifier * modifier; + break; + } + + entity.Comp.RandomPrice = modifier * entity.Comp.MaxRandomPrice; + } + } + + private void CalculateRandomPrice(Entity entity, ref PriceCalculationEvent args) + { + // TODO: Estimated pricing. + if (args.Handled) + return; + + args.Price += entity.Comp.RandomPrice ?? 0; + } + private double GetSolutionPrice(Entity entity) { if (Comp(entity).EntityLifeStage < EntityLifeStage.MapInitialized) diff --git a/Resources/Prototypes/Body/species_base.yml b/Resources/Prototypes/Body/species_base.yml index 1f782b0bc3..f5c428fe1e 100644 --- a/Resources/Prototypes/Body/species_base.yml +++ b/Resources/Prototypes/Body/species_base.yml @@ -151,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/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 2763afefec..6942263c65 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -15,6 +15,7 @@ - Grammar # Pronouns - HumanoidProfile # Age, Sex, Gender - NpcFactionMember + - RandomPrice - CreamPied # traits - BlackAndWhiteOverlay From d1f913c5fe1a0e9b49fefe37c38f011393588fa6 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 4 Apr 2026 17:05:35 +0000 Subject: [PATCH 070/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 2ec57487ce..1ffc17eec7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4033,3 +4026,10 @@ 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 From 515f686950ce59504aa90e6d5314a6c8f4147355 Mon Sep 17 00:00:00 2001 From: Benas Kiburtas <26272940+eveloop@users.noreply.github.com> Date: Sat, 4 Apr 2026 19:53:05 +0300 Subject: [PATCH 071/247] Hide humanoid appearance entities from Entity Spawn menu (#43467) * Hide humanoid appearance entities from Entity Spawn menu * Update species appearance entities to follow YAML convention --- Resources/Prototypes/Body/Species/arachnid.yml | 1 + Resources/Prototypes/Body/Species/diona.yml | 1 + Resources/Prototypes/Body/Species/dwarf.yml | 1 + Resources/Prototypes/Body/Species/gingerbread.yml | 1 + Resources/Prototypes/Body/Species/human.yml | 1 + Resources/Prototypes/Body/Species/moth.yml | 1 + Resources/Prototypes/Body/Species/reptilian.yml | 1 + Resources/Prototypes/Body/Species/skeleton.yml | 1 + Resources/Prototypes/Body/Species/slime.yml | 1 + Resources/Prototypes/Body/Species/vox.yml | 1 + Resources/Prototypes/Body/Species/vulpkanin.yml | 1 + 11 files changed, 11 insertions(+) diff --git a/Resources/Prototypes/Body/Species/arachnid.yml b/Resources/Prototypes/Body/Species/arachnid.yml index 240d486f5a..e1b8e5dcdd 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 diff --git a/Resources/Prototypes/Body/Species/diona.yml b/Resources/Prototypes/Body/Species/diona.yml index 7790c67ecf..bab251032b 100644 --- a/Resources/Prototypes/Body/Species/diona.yml +++ b/Resources/Prototypes/Body/Species/diona.yml @@ -35,6 +35,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceDiona + categories: [ HideSpawnMenu ] name: diona appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/dwarf.yml b/Resources/Prototypes/Body/Species/dwarf.yml index 1d64142ca9..5b053f0735 100644 --- a/Resources/Prototypes/Body/Species/dwarf.yml +++ b/Resources/Prototypes/Body/Species/dwarf.yml @@ -1,6 +1,7 @@ - type: entity parent: AppearanceHuman id: AppearanceDwarf + categories: [ HideSpawnMenu ] name: dwarf appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/gingerbread.yml b/Resources/Prototypes/Body/Species/gingerbread.yml index b32cbac91d..5c429af01c 100644 --- a/Resources/Prototypes/Body/Species/gingerbread.yml +++ b/Resources/Prototypes/Body/Species/gingerbread.yml @@ -1,6 +1,7 @@ - type: entity parent: BaseSpeciesAppearance id: AppearanceGingerbread + categories: [ HideSpawnMenu ] name: gingerbread appearance components: - type: Inventory diff --git a/Resources/Prototypes/Body/Species/human.yml b/Resources/Prototypes/Body/Species/human.yml index d6c76015e5..9b50ba8c53 100644 --- a/Resources/Prototypes/Body/Species/human.yml +++ b/Resources/Prototypes/Body/Species/human.yml @@ -51,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 b7928db44c..4b46381c3c 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 diff --git a/Resources/Prototypes/Body/Species/reptilian.yml b/Resources/Prototypes/Body/Species/reptilian.yml index 8f28ea0ab4..ea2336c16d 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 diff --git a/Resources/Prototypes/Body/Species/skeleton.yml b/Resources/Prototypes/Body/Species/skeleton.yml index 8372e4923d..953a8c7bae 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 82c8b30ca6..f0ba746a6f 100644 --- a/Resources/Prototypes/Body/Species/slime.yml +++ b/Resources/Prototypes/Body/Species/slime.yml @@ -46,6 +46,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 111a1f47fa..b947b3e15f 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 diff --git a/Resources/Prototypes/Body/Species/vulpkanin.yml b/Resources/Prototypes/Body/Species/vulpkanin.yml index c4afe1b8e1..c4e1577149 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 From e150b9cbcc44d3025beda93fda851a9604e88076 Mon Sep 17 00:00:00 2001 From: B_Kirill <153602297+B-Kirill@users.noreply.github.com> Date: Sun, 5 Apr 2026 03:13:25 +1000 Subject: [PATCH 072/247] A small cleanup in meteor rules (#43402) --- .../Prototypes/GameRules/meteorswarms.yml | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Resources/Prototypes/GameRules/meteorswarms.yml b/Resources/Prototypes/GameRules/meteorswarms.yml index ed3a510d9a..134995b801 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 @@ -105,8 +105,8 @@ - type: MeteorSwarm - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleSpaceDustMinor + parent: MeteorSwarm + id: SpaceDustMinor components: - type: StationEvent weight: 44 @@ -126,8 +126,8 @@ max: 5 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleSpaceDustMajor + parent: MeteorSwarm + id: SpaceDustMajor components: - type: StationEvent weight: 22 @@ -146,8 +146,8 @@ max: 12 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleMeteorSwarmSmall + parent: MeteorSwarm + id: MeteorSwarmSmall components: - type: StationEvent weight: 18 @@ -158,8 +158,8 @@ MeteorMedium: 3 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleMeteorSwarmMedium + parent: MeteorSwarm + id: MeteorSwarmMedium components: - type: StationEvent weight: 10 @@ -170,8 +170,8 @@ MeteorLarge: 1 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleMeteorSwarmLarge + parent: MeteorSwarm + id: MeteorSwarmLarge components: - type: StationEvent weight: 5 @@ -182,8 +182,8 @@ MeteorLarge: 4 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleUristSwarm + parent: MeteorSwarm + id: UristSwarm components: - type: StationEvent weight: 0.05 @@ -243,8 +243,8 @@ orGroup: rodProto - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleCowSwarm + parent: MeteorSwarm + id: CowSwarm components: - type: StationEvent weight: 0.05 @@ -261,8 +261,8 @@ max: 10 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleClownSwarm + parent: MeteorSwarm + id: ClownSwarm components: - type: StationEvent weight: 0.05 @@ -279,8 +279,8 @@ max: 10 - type: entity - parent: GameRuleMeteorSwarm - id: GameRulePotatoSwarm + parent: MeteorSwarm + id: PotatoSwarm components: - type: StationEvent weight: 0.05 @@ -297,8 +297,8 @@ max: 10 - type: entity - parent: GameRuleMeteorSwarm - id: GameRuleFunSwarm + parent: MeteorSwarm + id: FunSwarm components: - type: StationEvent weight: 0.03 From 968862db28baa8b25fa0af6cc941b1192e804916 Mon Sep 17 00:00:00 2001 From: salarua Date: Sat, 4 Apr 2026 10:44:58 -0700 Subject: [PATCH 073/247] Fix typography on loading screen tips (#43348) Also a couple of minor copyedits. --- Resources/Locale/en-US/tips.ftl | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Resources/Locale/en-US/tips.ftl b/Resources/Locale/en-US/tips.ftl index c02e72da19..37a52a8d4d 100644 --- a/Resources/Locale/en-US/tips.ftl +++ b/Resources/Locale/en-US/tips.ftl @@ -6,9 +6,9 @@ tips-dataset-5 = Artifacts have the ability to gain permanent effects for some t tips-dataset-6 = You can avoid slipping on most puddles by walking. However, some strong chemicals like space lube will slip people anyway. tips-dataset-7 = Some plants, such as galaxy thistle, can be ground up into extremely useful and potent medicines. tips-dataset-8 = Mopping up puddles and draining them into other containers conserves the reagents found in the puddle. -tips-dataset-9 = Floor drains, usually found in the chef's freezer or janitor's office, rapidly consume reagent found in puddles around them--including blood. +tips-dataset-9 = Floor drains, usually found in the chef's freezer or janitor's office, rapidly consume reagent found in puddles around them — including blood. tips-dataset-10 = Cognizine, a hard to manufacture chemical, makes animals sentient when they are injected with it. -tips-dataset-11 = Loaded mousetraps are incredibly effective at dealing with all manner of low-mass mobs--including Rat Servants. +tips-dataset-11 = Loaded mousetraps are incredibly effective at dealing with all manner of low-mass mobs — including Rat Servants. tips-dataset-12 = Fire extinguishers can be loaded with any reagent in the game. tips-dataset-13 = Some reagents, like chlorine trifluoride, have unique effects when applied by touch, such as through a spray bottle or foam. tips-dataset-14 = Remember to touch grass in between playing Space Station 14 every once in a while. @@ -19,7 +19,7 @@ tips-dataset-18 = When running the Singularity, make sure to check on it periodi tips-dataset-19 = If the Singularity is up, make sure to refuel the radiation collectors once in a while. tips-dataset-20 = Chemicals don't react while inside the ChemMaster's buffer. tips-dataset-21 = Don't anger the bartender by throwing their glasses! Politely place them on the table by tapping Q. -tips-dataset-22 = You can hold SPACE by default to slow the movement of the shuttle when piloting, to allow for precise movements--or even coming to a complete stop. +tips-dataset-22 = You can hold SPACE by default to slow the movement of the shuttle when piloting, to allow for precise movements — or even coming to a complete stop. tips-dataset-23 = Dexalin, Dexalin Plus, and Epinephrine will all purge heartbreaker toxin from your bloodstream while metabolizing. tips-dataset-24 = Every crewmember comes with an emergency medipen in their survival box containing epinephrine and tranexamic acid. tips-dataset-25 = The AME is a high-priority target and is easily sabotaged. Make sure to set up the Singularity or Solars so that you don't run out of power if it blows. @@ -29,15 +29,15 @@ tips-dataset-28 = Riot armor is significantly more powerful against opponents th tips-dataset-29 = As a ghost, you can use the Verb Menu to orbit around and follow any entity in game automatically. tips-dataset-30 = As a Traitor, you may sometimes be assigned to hunt other traitors, and in turn be hunted by others. tips-dataset-31 = As a Traitor, the syndicate encryption key can be used to communicate through a secure channel with any other traitors who have purchased it. -tips-dataset-32 = As a Traitor, compromising important communications channels like security or engineering can give valuable intelligence. Be aware that this goes in both ways - security can compromise syndicate communications as well! +tips-dataset-32 = As a Traitor, compromising important communications channels like security or engineering can give valuable intelligence. Be aware that this goes in both ways — security can compromise syndicate communications as well! tips-dataset-33 = As a Traitor, the syndicate toolbox is extremely versatile. For only 2 telecrystals, you can get a full set of tools to help you in an emergency, insulated combat gloves and a syndicate gas mask. tips-dataset-34 = As a Traitor, never underestimate the web vest. It may not provide space protection, but its cheap cost and robust protection makes it handy for protecting against trigger-happy foes. tips-dataset-35 = As a Traitor, any purchased grenade penguins won't attack you, and will explode if killed. tips-dataset-36 = As a Traitor, be careful when using vestine from the chemical synthesis kit. If someone checks your station, they could easily out you. tips-dataset-37 = As a Traitor, remember that power sinks will create a loud sound and alert the crew after running for long enough. Try to hide them in a tricky-to-find spot, or reinforce the area around them so that they're harder to reach. -tips-dataset-38 = As a Traitor, plasma gas is an excellent way to create chaos. It can be ignited to make an area extra-uninhabitable, and can cause toxin damage to those that inhale it. +tips-dataset-38 = As a Traitor, plasma gas is an excellent way to create chaos. It can be ignited to make an area extra uninhabitable, and can cause toxin damage to those that inhale it. tips-dataset-39 = As a Traitor, dehydrated carps are useful for killing a large hoard of people. As long as you pat it before rehydrating it, it can be used as a great distraction. -tips-dataset-40 = As a Traitor, have you tried injecting plasma into batteries? In the case of a defibrillator, it explodes on use; hurting the user and the patient! +tips-dataset-40 = As a Traitor, have you tried injecting plasma into batteries? In the case of a defibrillator, it explodes on use, hurting the user and the patient! tips-dataset-41 = As a Nuclear Operative, stick together! While your equipment is robust, your fellow operatives are much better at saving your life: they can drag you away from danger while stunned and provide cover fire. tips-dataset-42 = As a Nuclear Operative, communication is key! Use your radio to speak to your fellow operatives and coordinate an attack plan. tips-dataset-43 = As a Nuclear Operative, remember that stealth is an option. It'll be hard for the captain to fight back if he gets caught off guard by what he thinks is just a regular passenger! @@ -49,7 +49,7 @@ tips-dataset-48 = As a Salvage Specialist, never forget to mine ore! Ore can be tips-dataset-49 = As a Salvage Specialist, try asking science for a tethergun. It can be used to grab items off of salvage wrecks extremely efficiently! tips-dataset-50 = As a Salvage Specialist, try asking science for a grappling hook. It can be used to propel yourself onto wrecks, or if stuck in space you don't have to rely on the proto-kinetic accelerator. tips-dataset-51 = Tip #51 does not exist and has never existed. Ignore any rumors to the contrary. -tips-dataset-52 = As a Salvage Specialist, consider cooperating with the Cargo Technicians. They can order you a wide variety of useful items, including ones that may be hard to get otherwise, such laser guns and shuttle building materials. +tips-dataset-52 = As a Salvage Specialist, consider cooperating with the Cargo Technicians. They can order you a wide variety of useful items, including ones that may be hard to get otherwise, such as laser guns and shuttle building materials. tips-dataset-53 = As a Cargo Technician, consider asking science for a Ripley APLU. When paired with a hydraulic clamp, you can grab valuable maintenance objects like fuel tanks much more easily, and make deliveries in a swift manner. tips-dataset-54 = As a Cargo Technician, try to maintain a surplus of materials. They are extremely useful for Scientists and Station Engineers to have immediate access to. tips-dataset-55 = As a Cargo Technician, if you have a surplus of cash try gambling! Sometimes you gain more money than you begin with. @@ -70,28 +70,28 @@ tips-dataset-69 = As an Atmospheric Technician, your ATMOS holofan projector blo tips-dataset-70 = As an Atmospheric Technician, try to resist the temptation of making canister bombs for Nuclear Operatives, unless you're in a last-ditch scenario. They often lead to large amounts of unnecessary friendly fire! tips-dataset-71 = As an Engineer, you can repair cracked windows by using a lit welding tool on them while not in combat mode. tips-dataset-72 = As an Engineer, you can electrify grilles by placing powered cables beneath them. -tips-dataset-73 = As an Engineer, always double check when you're setting up the singularity. It is easier than you think to loose it! +tips-dataset-73 = As an Engineer, always double-check when you're setting up the singularity. It is easier than you think to loose it! tips-dataset-74 = As an Engineer, you can use plasma glass to reinforce an area and prevent radiation. Uranium glass can also be used to prevent radiation. -tips-dataset-75 = As the Captain, you are one of the highest priority targets on the station. Everything from revolutions, to nuclear operatives, to traitors that need to rob you of your unique laser pistol or your life are things to worry about. +tips-dataset-75 = As the Captain, you are one of the highest-priority targets on the station. Everything from revolutions, to Nuclear Operatives, to traitors that need to rob you of your unique laser pistol or your life are things to worry about. tips-dataset-76 = As the Captain, always take the nuclear disk and pinpointer with you every shift. It's a good idea to give one of these to another head you can trust with keeping it safe. tips-dataset-77 = As the Captain, you have absolute access and control over the station, but this does not mean that being a horrible person won't result in mutiny. tips-dataset-78 = As the Captain, try to be active and patrol the station. Staying in the bridge might be tempting, but you'll just end up putting a bigger target on your back! tips-dataset-79 = As a Scientist, you can use the node scanner to see when an artifact is able to react to triggers, and which triggers it is currently reacting to; each number it shows corresponds to a different trigger! tips-dataset-80 = As a Scientist, you can utilize upgraded versions of machines to increase its effectiveness. This can make certain machines significantly better; salvage will love you if you upgrade their ore processor! -tips-dataset-81 = As a Scientist, you can build cyborgs using positronic brains and a chassis, they are just as useful as a new crew member. +tips-dataset-81 = As a Scientist, you can build cyborgs using positronic brains and a chassis; they are just as useful as a new crew member. tips-dataset-82 = As a Medical Doctor, try to be wary of overdosing your patients, especially if someone else has already been on the scene. Overdoses are often lethal to patients in crit! tips-dataset-83 = As a Medical Doctor, don't underestimate your cryo pods! They heal almost every type of damage, making them very useful when you are overloaded or need to heal someone in a pinch. tips-dataset-84 = As a Medical Doctor, exercise caution when putting reptilians in cryopods. They will take a lot of extra cold damage, but you can mitigate this with some burn medicine or leporazine. tips-dataset-85 = As a Medical Doctor, remember that the health analyzer can be used if you lose your PDA. tips-dataset-86 = As a Chemist, once you've made everything you've needed to, don't be afraid to make more silly reagents. Have you tried desoxyephedrine or licoxide? -tips-dataset-87 = As a Medical Doctor, Chemist, or Chief Medical Officer, you can use chloral hydrate to non-lethally sedate unruly patients. +tips-dataset-87 = As a Medical Doctor, Chemist, or Chief Medical Officer, you can use chloral hydrate to nonlethally sedate unruly patients. tips-dataset-88 = Don't be afraid to ask for help, whether from your peers in character or through LOOC, or from admins! -tips-dataset-89 = You'll quickly lose your interest in the game if you play to win and kill. If you find yourself doing this, take a step back and talk to people--it's a much better experience! +tips-dataset-89 = You'll quickly lose your interest in the game if you play to win and kill. If you find yourself doing this, take a step back and talk to people — it's a much better experience! tips-dataset-90 = If there's something you need from another department, try asking! This game isn't singleplayer and you'd be surprised what you can get accomplished together! tips-dataset-91 = The station's nuke is invincible. Go find the disk instead of trying to destroy it. tips-dataset-92 = Maintenance is full of equipment that is randomized every round. Look around and see if anything is worth using. -tips-dataset-93 = We were all new once, be patient and guide new players, especially those playing intern roles, in the right direction. -tips-dataset-94 = Firesuits, winter coats and emergency EVA suits offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all. +tips-dataset-93 = We were all new once. Be patient and guide new players, especially those playing intern roles, in the right direction. +tips-dataset-94 = Firesuits, winter coats, and emergency EVA suits offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all. tips-dataset-95 = In an emergency, you can always rely on firesuits and emergency EVA suits; they will always spawn in their respective lockers. They might be awkward to move around in, but can easily save your life in a dangerous situation. tips-dataset-96 = In an emergency, remember that you can craft improvised weapons! A baseball bat or spear could easily mean the difference between deterring an attacker or perishing from the hands of one. tips-dataset-97 = Spears can be tipped with chemicals, and will inject a few units every time you hit someone with them directly. @@ -112,7 +112,7 @@ tips-dataset-111 = You can move an item out of the way by dragging it, and then tips-dataset-112 = When dealing with security, you can often get your sentence negated entirely through cooperation and deception. tips-dataset-113 = Fire can spread to other players through touch! Be careful around flaming bodies or large crowds with people on fire in them. tips-dataset-114 = Hull breaches take a few seconds to fully space an area. You can use this time to patch up the hole if you're confident enough, or just run away. -tips-dataset-115 = Burn damage, such as that from a welding tool or lightbulb, can be used to cauterize wounds and stop bleeding. +tips-dataset-115 = Burn damage, such as from a welding tool or lightbulb, can be used to cauterize wounds and stop bleeding. tips-dataset-116 = Bleeding is no joke! If you've been shot or acquired any other major injury, make sure to treat it quickly. tips-dataset-117 = In an emergency, you can butcher a jumpsuit with a sharp object to get cloth, which can be crafted into gauze. tips-dataset-118 = You can use sharp objects to butcher clothes or animals in the right click context menu. This includes glass shards. @@ -121,7 +121,7 @@ tips-dataset-120 = You can stun grenade penguins, which can bide valuable time f tips-dataset-121 = You can click on the names of items to pick them up in the right click menu, instead of hovering over the item and then selecting pick up. tips-dataset-122 = Space Station 14 is open source! If there's a change you want to make, or a simple item you want to add, then try contributing to the game. It's not as hard as you'd think it is. tips-dataset-123 = In a pinch, you can throw drinks or other reagent containers behind you to create a spill that can slip people chasing you. -tips-dataset-124 = Some weapons, such as knives & shivs, have a fast attack speed. +tips-dataset-124 = Some weapons, such as knives and shivs, have a fast attack speed. tips-dataset-125 = The jaws of life can be used to open powered doors. tips-dataset-126 = If you're not a human, you can drink blood to heal back some of your blood volume, albeit very inefficiently. tips-dataset-127 = If you're a human, don't drink blood! It makes you sick and you'll begin to take damage. @@ -135,5 +135,5 @@ tips-dataset-134 = You can tell if an area with firelocks up is spaced by lookin tips-dataset-135 = Instead of picking it up, you can alt-click food to eat it. This also works for mice and other creatures without hands. tips-dataset-136 = If you're trapped behind an electrified door, disable the APC or throw your ID at the door to avoid getting shocked! tips-dataset-137 = If the AI electrifies a door and you have insulated gloves, snip and mend the power wire to reset their electrification! -tips-dataset-138 = If you want to stop your prisoner from escaping from the cell right after being uncuffed, turn on combat mode while uncuffing - this will shove the prisoner down. +tips-dataset-138 = If you want to stop your prisoner from escaping from the cell right after being uncuffed, turn on combat mode while uncuffing — this will shove the prisoner down. tips-dataset-139 = Make sure to clean your illegal implanters with a soap after you use them! Detectives can scan used implanters for incriminating DNA evidence, but not if they've been wiped clean. From d595590f39da8f224852962f2b5b72126e3baead Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Sat, 4 Apr 2026 14:20:46 -0400 Subject: [PATCH 074/247] Replace space dragon timer deletion with gibbing (#43296) * save * fix test failure --- .../Dragon/Components/DragonComponent.cs | 13 ++++++++++ Content.Server/Dragon/DragonSystem.cs | 24 +++++++++++++++---- .../Entities/Mobs/Player/dragon.yml | 13 ++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/Content.Server/Dragon/Components/DragonComponent.cs b/Content.Server/Dragon/Components/DragonComponent.cs index 80461e156a..c634836b5a 100644 --- a/Content.Server/Dragon/Components/DragonComponent.cs +++ b/Content.Server/Dragon/Components/DragonComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chemistry.Components; using Content.Shared.NPC.Prototypes; using Robust.Shared.Audio; using Robust.Shared.Prototypes; @@ -65,5 +66,17 @@ namespace Content.Server.Dragon /// [DataField] public ProtoId Faction = "Dragon"; + + /// + /// The smoke to spawn upon rift timeout death. + /// + [DataField] + public EntProtoId SmokePrototype = "BloodSmoke"; + + /// + /// The solution to place into the smoke (mostly just needed for color) + /// + [DataField] + public Solution SmokeSolution = new ([new("Blood", 1)]); } } diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index 1c838939ec..8bc5fd83b1 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -1,8 +1,11 @@ +using Content.Server.Fluids.EntitySystems; using Content.Server.Objectives.Components; using Content.Server.Objectives.Systems; using Content.Server.Popups; using Content.Shared.Actions; +using Content.Shared.Chemistry.Components; using Content.Shared.Dragon; +using Content.Shared.Gibbing; using Content.Shared.Maps; using Content.Shared.Mind; using Content.Shared.Mind.Components; @@ -12,6 +15,7 @@ using Content.Shared.Movement.Systems; using Content.Shared.NPC.Systems; using Content.Shared.Zombies; using Robust.Shared.Audio.Systems; +using Robust.Shared.Map; using Robust.Shared.Map.Components; namespace Content.Server.Dragon; @@ -29,6 +33,8 @@ public sealed partial class DragonSystem : EntitySystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly TurfSystem _turf = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; + [Dependency] private readonly SmokeSystem _smoke = default!; private EntityQuery _objQuery; @@ -96,11 +102,14 @@ public sealed partial class DragonSystem : EntitySystem if (!_mobState.IsDead(uid)) comp.RiftAccumulator += frameTime; - // Delete it, naughty dragon! + // Gib it, naughty dragon! if (comp.RiftAccumulator >= comp.RiftMaxAccumulator) { - Roar(uid, comp); - QueueDel(uid); + Roar(uid, comp, Transform(uid).Coordinates); + var smoke = Spawn(comp.SmokePrototype, Transform(uid).Coordinates); + if (TryComp(smoke, out var smokeComp)) + _smoke.StartSmoke(smoke, comp.SmokeSolution, smokeComp.Duration, smokeComp.SpreadAmount, smokeComp); + _gibbing.Gib(uid); } } } @@ -200,10 +209,15 @@ public sealed partial class DragonSystem : EntitySystem _faction.AddFaction(ent.Owner, ent.Comp.Faction); } - private void Roar(EntityUid uid, DragonComponent comp) + private void Roar(EntityUid uid, DragonComponent comp, EntityCoordinates? coords = null) { if (comp.SoundRoar != null) - _audio.PlayPvs(comp.SoundRoar, uid); + { + if (coords != null) + _audio.PlayPvs(comp.SoundRoar, coords.Value); + else + _audio.PlayPvs(comp.SoundRoar, uid); + } } /// diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index 57b682caea..a1e2e962df 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -285,3 +285,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 From fb940e46696aa643066131152b1065f806833599 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 4 Apr 2026 18:36:52 +0000 Subject: [PATCH 075/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 1ffc17eec7..e6b75e0bb0 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- 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. @@ -4033,3 +4024,11 @@ 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 From bcb84b52cd7ed59b51dd241f68854d8985bb4dea Mon Sep 17 00:00:00 2001 From: August Sun <45527070+august-sun@users.noreply.github.com> Date: Sat, 4 Apr 2026 13:10:02 -0600 Subject: [PATCH 076/247] Adds the scout xenoborg jump module (#43243) * Added prototype and sprites for borg jump module * Made new action, switched sprites/inheritance * cleanup * Update Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml * improved icon --------- Co-authored-by: august-sun <45527070+august.sun@users.noreply.github.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- Resources/Prototypes/Actions/types.yml | 11 +++++++++++ .../Objects/Specific/Robotics/borg_modules.yml | 17 +++++++++++++++++ .../Actions/actions_borg.rsi/meta.json | 3 +++ .../actions_borg.rsi/xenoborg-jump-module.png | Bin 0 -> 365 bytes .../borgmodule.rsi/icon-xenoborg-jump.png | Bin 0 -> 183 bytes .../Specific/Robotics/borgmodule.rsi/meta.json | 3 +++ 6 files changed, 34 insertions(+) create mode 100644 Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-jump-module.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-jump.png diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index e0956a76f7..c07584e737 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/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index fa45506e33..a10437d5b7 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -1625,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/Textures/Interface/Actions/actions_borg.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json index 8538e24fb9..e61a7ab6eb 100644 --- a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json @@ -133,6 +133,9 @@ { "name":"xenoborg-basic-module" }, + { + "name":"xenoborg-jump-module" + }, { "name":"xenoborg-camera-computer" }, 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 0000000000000000000000000000000000000000..bb09b413daa9186553a3c3bfd5f67991da7f5ffa GIT binary patch literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|v;urWT!Azg zh%+!~3knv*#r@aR{I9HhMqIohAmFB)+~%&X{|ycQot!rJc=WypD&;E)@(X78j|{@S zZi@hAu6w#ThFJ8joqSN}hysUmw6sOQ=l}b6GhewGRa$l9tm(tfrrY266IVWa&mGJ8 zCUxOn*1pznng`Fv2{X)?ndn#`zD%h}lkuI_kycK|G%nG`j#WttJLZ245TD0#+tb|^n}KMt7T5*+`BgQ`aDU#$1&wphBEWC$2$aF zUY)!okt-F?v~=F{nLI7`&nq!Z?D#IRd?tf`%!KE5TO8ItDOqe3u-Zf6*xWC2cf>wD v=n0VUcACa_;jGg9qK#Mc<)43j{dWbwSp%bP0l+XkKZ6cQ~ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e66775448107f48ab1628887f453a5ef3a9b0102 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}g`O^sArY;~ z2@g6&|Nc&( Date: Sat, 4 Apr 2026 15:23:39 -0400 Subject: [PATCH 077/247] Add lobby screen invisiblewall (#42947) * add lobby screen: invisiblewall * Update lobby-state-background.ftl * Update attributions.yml had two lines swapped. fixed. --- .../en-US/lobby/lobby-state-background.ftl | 3 +++ Resources/Prototypes/lobbyscreens.yml | 6 ++++++ .../Textures/LobbyScreens/attributions.yml | 7 ++++++- .../Textures/LobbyScreens/invisiblewall.webp | Bin 0 -> 108088 bytes .../LobbyScreens/invisiblewall.webp.yml | 2 ++ 5 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Resources/Textures/LobbyScreens/invisiblewall.webp create mode 100644 Resources/Textures/LobbyScreens/invisiblewall.webp.yml diff --git a/Resources/Locale/en-US/lobby/lobby-state-background.ftl b/Resources/Locale/en-US/lobby/lobby-state-background.ftl index c0046ca693..7191ebbe12 100644 --- a/Resources/Locale/en-US/lobby/lobby-state-background.ftl +++ b/Resources/Locale/en-US/lobby/lobby-state-background.ftl @@ -1,6 +1,9 @@ lobby-state-background-warden-title = Warden lobby-state-background-warden-artist = Solbusaur +lobby-state-background-invisiblewall-title = Invisible Wall +lobby-state-background-invisiblewall-artist = Vandersloot + lobby-state-background-pharmacy-title = Pharmacy lobby-state-background-pharmacy-artist = Solbusaur diff --git a/Resources/Prototypes/lobbyscreens.yml b/Resources/Prototypes/lobbyscreens.yml index d0f6da99d5..94fc8bd3ba 100644 --- a/Resources/Prototypes/lobbyscreens.yml +++ b/Resources/Prototypes/lobbyscreens.yml @@ -4,6 +4,12 @@ title: lobby-state-background-warden-title artist: lobby-state-background-warden-artist +- type: lobbyBackground + id: InvisibleWall + background: /Textures/LobbyScreens/invisiblewall.webp + title: lobby-state-background-invisiblewall-title + artist: lobby-state-background-invisiblewall-artist + - type: lobbyBackground id: Pharmacy background: /Textures/LobbyScreens/pharmacy.webp diff --git a/Resources/Textures/LobbyScreens/attributions.yml b/Resources/Textures/LobbyScreens/attributions.yml index 1df5da9a2b..dead0a28fe 100644 --- a/Resources/Textures/LobbyScreens/attributions.yml +++ b/Resources/Textures/LobbyScreens/attributions.yml @@ -48,6 +48,11 @@ copyright: "plantyfern on discord" source: "https://github.com/space-wizards/space-station-14" +- files: ["invisiblewall.webp"] + license: "CC-BY-SA-3.0" + copyright: "vanderslootassgiraffe on discord" + source: "https://github.com/space-wizards/space-station-14" + - files: ["janishootout.webp"] license: "CC0-1.0" copyright: "psychpsyo on Github/Twitter" @@ -56,4 +61,4 @@ - files: ["reclaimer-nuke.webp"] license: "CC-BY-NC-SA-3.0" copyright: "GetOutMarutak,aka.Snicket on Discord/github/Cara/Ko-fi" - source: "https://github.com/space-wizards/space-station-14" \ No newline at end of file + source: "https://github.com/space-wizards/space-station-14" diff --git a/Resources/Textures/LobbyScreens/invisiblewall.webp b/Resources/Textures/LobbyScreens/invisiblewall.webp new file mode 100644 index 0000000000000000000000000000000000000000..ac38cdbac02cb34a2cf073a8545e6aae01fdf4c8 GIT binary patch literal 108088 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�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

EJu5VGc?n%Uuz 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 Date: Sat, 4 Apr 2026 19:39:20 +0000 Subject: [PATCH 078/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e6b75e0bb0..1f585a949d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4032,3 +4025,11 @@ 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 From 094736c510baddb3aaea2b881dc211c6b7ade795 Mon Sep 17 00:00:00 2001 From: 0-Anon Date: Sat, 4 Apr 2026 15:28:25 -0400 Subject: [PATCH 079/247] Add Tail Drag to Rat King (#42701) Add pulling to Rat King Taildrag power --- Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index ce31df386d..2c2e2a9cf8 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: From ef3929be8645bbc0b8ab9dd3e6d5113dac77e4a1 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 4 Apr 2026 19:59:43 +0000 Subject: [PATCH 080/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 1f585a949d..7b35a1062c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4033,3 +4026,10 @@ 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 From 0d616cf6df20b2d1f6c875fb5dbde347eb5fedec Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:46:43 +0200 Subject: [PATCH 081/247] Universal uplink codes (#38712) * Halfway commit * Finishing commit, maybe? * Fix test, update ringtone UI to look nicer * Fix command, add failsafe * Documentation * Can we just mark ValidatePrototypeId as obsolete please * I'm too tired and my bones hurt * Change uplink code generation method * Move RingerAccessUplinkComponent to Server, because cheat clients could make use of it * Fix uplink implant changes, review changes, repair broken serialization. * cleanup and master merge * forgot the Linq * Linqd * Our store system is pretty goddamn awful wow --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Client/Store/StoreSystem.cs | 5 + .../Store/Ui/StoreBoundUserInterface.cs | 11 +- Content.IntegrationTests/Tests/StoreTests.cs | 13 +- .../GameTicking/Rules/TraitorRuleSystem.cs | 30 +-- Content.Server/PDA/PdaSystem.cs | 33 ++- .../PDA/Ringer/RingerAccessUplinkComponent.cs | 24 ++ Content.Server/PDA/Ringer/RingerSystem.cs | 224 +++++++++++++++--- .../Store/Systems/StoreSystem.Ui.cs | 48 +++- Content.Server/Store/Systems/StoreSystem.cs | 20 +- .../Uplink/Commands/AddUplinkCommand.cs | 8 +- Content.Server/Traitor/Uplink/UplinkSystem.cs | 111 +++++++-- .../PDA/Ringer/RingerUplinkComponent.cs | 15 +- Content.Shared/PDA/SharedRingerSystem.cs | 4 + .../Store/Components/RemoteStoreComponent.cs | 17 ++ Content.Shared/Store/SharedStoreSystem.cs | 59 +++++ Content.Shared/Store/StoreUi.cs | 3 +- .../commands/add-uplink-command.ftl | 4 +- .../Entities/Objects/Devices/pda.yml | 3 +- Resources/Prototypes/Store/presets.yml | 7 + 19 files changed, 525 insertions(+), 114 deletions(-) create mode 100644 Content.Client/Store/StoreSystem.cs create mode 100644 Content.Server/PDA/Ringer/RingerAccessUplinkComponent.cs create mode 100644 Content.Shared/Store/Components/RemoteStoreComponent.cs create mode 100644 Content.Shared/Store/SharedStoreSystem.cs diff --git a/Content.Client/Store/StoreSystem.cs b/Content.Client/Store/StoreSystem.cs new file mode 100644 index 0000000000..cb6e42a0d7 --- /dev/null +++ b/Content.Client/Store/StoreSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Store; + +namespace Content.Client.Store; + +public sealed class StoreSystem : SharedStoreSystem; diff --git a/Content.Client/Store/Ui/StoreBoundUserInterface.cs b/Content.Client/Store/Ui/StoreBoundUserInterface.cs index d8236604bf..36eec4a671 100644 --- a/Content.Client/Store/Ui/StoreBoundUserInterface.cs +++ b/Content.Client/Store/Ui/StoreBoundUserInterface.cs @@ -1,7 +1,6 @@ +using System.Linq; using Content.Shared.Store; using JetBrains.Annotations; -using System.Linq; -using Content.Shared.Store.Components; using Robust.Client.UserInterface; using Robust.Shared.Prototypes; @@ -11,6 +10,7 @@ namespace Content.Client.Store.Ui; public sealed class StoreBoundUserInterface : BoundUserInterface { private IPrototypeManager _prototypeManager = default!; + private readonly StoreSystem _storeSystem = default!; [ViewVariables] private StoreMenu? _menu; @@ -23,6 +23,7 @@ public sealed class StoreBoundUserInterface : BoundUserInterface public StoreBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { + _storeSystem = EntMan.System(); } protected override void Open() @@ -30,12 +31,12 @@ public sealed class StoreBoundUserInterface : BoundUserInterface base.Open(); _menu = this.CreateWindow(); - if (EntMan.TryGetComponent(Owner, out var store)) - _menu.Title = Loc.GetString(store.Name); + if (_storeSystem.TryGetStore(Owner, out var store)) + _menu.Title = Loc.GetString(store.Value.Comp.Name); _menu.OnListingButtonPressed += (_, listing) => { - SendMessage(new StoreBuyListingMessage(listing.ID)); + SendMessage(new StoreBuyListingMessage(listing.ID, EntMan.GetNetEntity(Owner))); }; _menu.OnCategoryButtonPressed += (_, category) => diff --git a/Content.IntegrationTests/Tests/StoreTests.cs b/Content.IntegrationTests/Tests/StoreTests.cs index 811bf40548..411f4762dd 100644 --- a/Content.IntegrationTests/Tests/StoreTests.cs +++ b/Content.IntegrationTests/Tests/StoreTests.cs @@ -22,7 +22,7 @@ public sealed class StoreTests : GameTest - type: entity name: InventoryPdaDummy id: InventoryPdaDummy - parent: BasePDA + parent: [BasePDA, StorePresetUplink] components: - type: Clothing QuickEquip: false @@ -89,10 +89,13 @@ public sealed class StoreTests : GameTest mindSystem.TransferTo(mind, human, mind: mind); FixedPoint2 originalBalance = 20; - uplinkSystem.AddUplink(human, originalBalance, null, true); + uplinkSystem.AddUplink(human, originalBalance, out var notes, pda, null, true); - var storeComponent = entManager.GetComponent(pda); - var discountComponent = entManager.GetComponent(pda); + var remote = entManager.GetComponent(pda); + var storeEnt = remote.Store; + Assert.That(storeEnt.HasValue); + var storeComponent = entManager.GetComponent(storeEnt.Value); + var discountComponent = entManager.GetComponent(storeEnt.Value); Assert.That( discountComponent.Discounts, Has.Exactly(6).Items, @@ -138,7 +141,7 @@ public sealed class StoreTests : GameTest 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}; + var buyMsg = new StoreBuyListingMessage(discountedListingItem.ID, null){Actor = human}; server.EntMan.EventBus.RaiseLocalEvent(pda, buyMsg); var newBalance = storeComponent.Balance[UplinkSystem.TelecrystalCurrencyPrototype]; diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 3568f17306..bc053c80ba 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -18,6 +18,7 @@ using Robust.Shared.Random; using System.Linq; using System.Text; using Content.Server.Codewords; +using Robust.Shared.Map; namespace Content.Server.GameTicking.Rules; @@ -148,32 +149,22 @@ public sealed class TraitorRuleSystem : GameRuleSystem private (Note[]?, string) RequestUplink(EntityUid traitor, FixedPoint2 startingBalance, string briefing) { var pda = _uplink.FindUplinkTarget(traitor); - Note[]? code = null; Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink add"); - var uplinked = _uplink.AddUplink(traitor, startingBalance, pda, true); + var uplinked = _uplink.AddUplink(traitor, startingBalance, out var code, pda, giveDiscounts: true, bindToPda: false); - if (pda is not null && uplinked) + if (code != null && uplinked == AddUplinkResult.Pda) { Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink is PDA"); - // Codes are only generated if the uplink is a PDA - var ev = new GenerateUplinkCodeEvent(); - RaiseLocalEvent(pda.Value, ref ev); - if (ev.Code is { } generatedCode) - { - code = generatedCode; - - // If giveUplink is false the uplink code part is omitted - briefing = string.Format("{0}\n{1}", - briefing, - Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))); - return (code, briefing); - } - - Log.Error($"MakeTraitor {ToPrettyString(traitor)} failed to generate an uplink code on {ToPrettyString(pda)}."); + // If giveUplink is false the uplink code part is omitted + briefing = string.Format("{0}\n{1}", + briefing, + Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))); + return (code, briefing); } - else if (pda is null && uplinked) + + if (uplinked == AddUplinkResult.Implant) { Log.Debug($"MakeTraitor {ToPrettyString(traitor)} - Uplink is implant"); briefing += "\n" + Loc.GetString("traitor-role-uplink-implant-short"); @@ -183,6 +174,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem Log.Error($"MakeTraitor failed on {ToPrettyString(traitor)} - No uplink could be added"); } + return (null, briefing); } diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index 9122c96964..afc5876760 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Content.Server.Access.Systems; using Content.Server.AlertLevel; using Content.Server.CartridgeLoader; @@ -17,6 +18,7 @@ using Content.Shared.Light; using Content.Shared.Light.EntitySystems; using Content.Shared.PDA; using Content.Shared.PDA.Ringer; +using Content.Shared.Store.Components; using Content.Shared.VoiceMask; using Robust.Server.Containers; using Robust.Server.GameObjects; @@ -189,7 +191,7 @@ namespace Content.Server.PDA var address = GetDeviceNetAddress(uid); var hasInstrument = HasComp(uid); - var showUplink = HasComp(uid) && IsUnlocked(uid); + var showUplink = TryGetUnlockedStore(uid, out _); UpdateStationName(uid, pda); UpdateAlertLevel(uid, pda); @@ -274,8 +276,19 @@ namespace Content.Server.PDA return; // check if its locked again to prevent malicious clients opening locked uplinks - if (HasComp(uid) && IsUnlocked(uid)) - _store.ToggleUi(msg.Actor, uid); + if (TryGetUnlockedStore(uid, out var store)) + { + if (store != uid) + { + if (TryComp(uid, out var remoteStore)) + remoteStore.Store = store; + _store.ToggleUi(msg.Actor, store.Value, remoteAccess: uid); + } + else + { + _store.ToggleUi(msg.Actor, store.Value); + } + } } private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaLockUplinkMessage msg) @@ -285,14 +298,24 @@ namespace Content.Server.PDA if (TryComp(uid, out var uplink)) { + if (TryComp(uid, out var remoteStore)) + remoteStore.Store = null; _ringer.LockUplink((uid, uplink)); UpdatePdaUi(uid, pda); } } - private bool IsUnlocked(EntityUid uid) + ///

+ /// Returns the currently unlocked store, if there is one. + /// + private bool TryGetUnlockedStore(EntityUid uid, [NotNullWhen(true)] out EntityUid? store) { - return !TryComp(uid, out var uplink) || uplink.Unlocked; + store = null; + if (!TryComp(uid, out var uplink) || !uplink.Unlocked || uplink.TargetStore == null) + return false; + + store = uplink.TargetStore; + return true; } private void UpdateStationName(EntityUid uid, PdaComponent pda) diff --git a/Content.Server/PDA/Ringer/RingerAccessUplinkComponent.cs b/Content.Server/PDA/Ringer/RingerAccessUplinkComponent.cs new file mode 100644 index 0000000000..f7d124adef --- /dev/null +++ b/Content.Server/PDA/Ringer/RingerAccessUplinkComponent.cs @@ -0,0 +1,24 @@ +using Content.Shared.PDA; + +namespace Content.Server.PDA.Ringer; + +/// +/// Opens the store UI when a PDA's ringtone is set to the secret code. +/// Traitors are told the code when greeted. +/// +[RegisterComponent, Access(typeof(RingerSystem))] +public sealed partial class RingerAccessUplinkComponent : Component +{ + /// + /// Notes to set ringtone to in order to lock or unlock the uplink. + /// Set via GenerateUplinkCodeEvent. + /// + [DataField] + public Note[]? Code; + + /// + /// If set, the uplink store can only be opened with the given entity. + /// + [DataField] + public EntityUid? BoundEntity; +} diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs index b47ca0fde3..06df92bb68 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 Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Server.PDA.Ringer; @@ -14,15 +16,35 @@ public sealed class RingerSystem : SharedRingerSystem { [Dependency] private readonly IRobustRandom _random = default!; + public static Note[] AllowedNotes = + { + Note.C, + Note.D, + Note.E, + Note.F, + Note.G, + Note.A, + Note.B + }; + + /// + /// Stores the serialized version of any ringtone that can be excluded from new ringtone generations. + /// + [ViewVariables] + public readonly HashSet ReservedSerializedRingtones = new(); + /// public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnCurrencyInsert); - SubscribeLocalEvent(OnGenerateUplinkCode); + SubscribeLocalEvent(OnGenerateUplinkCode); + + SubscribeLocalEvent(CleanupReserved); + + InitialSetup(); } /// @@ -30,7 +52,11 @@ public sealed class RingerSystem : SharedRingerSystem /// private void OnMapInit(Entity ent, ref MapInitEvent args) { - UpdateRingerRingtone(ent, GenerateRingtone()); + var ringtone = GenerateRingtone(); + + ringtone ??= new Note[RingtoneLength] { Note.A, Note.A, Note.A, Note.A, Note.A, Note.A }; // Fallback + + UpdateRingerRingtone(ent, ringtone); } /// @@ -53,71 +79,209 @@ public sealed class RingerSystem : SharedRingerSystem /// /// Handles the for generating an uplink code. /// - private void OnGenerateUplinkCode(Entity ent, ref GenerateUplinkCodeEvent ev) + private void OnGenerateUplinkCode(Entity ent, ref GenerateUplinkCodeEvent ev) { - var code = GenerateRingtone(); + var code = GenerateRingtone(true, true); // Set the code on the component ent.Comp.Code = code; - // Return the code via the event ev.Code = code; } + private void InitialSetup() + { + ReservedSerializedRingtones.Clear(); + } + /// public override bool TryToggleUplink(EntityUid uid, Note[] ringtone, EntityUid? user = null) { if (!TryComp(uid, out var uplink)) return false; - if (!HasComp(uid)) - return false; - - // Wasn't generated yet - if (uplink.Code is null) - return false; - // On the server, we always check if the code matches - if (!uplink.Code.SequenceEqual(ringtone)) + if (!TryMatchRingtoneToStore(ringtone, out var store, uid)) return false; + uplink.TargetStore = store; + return ToggleUplinkInternal((uid, uplink)); } /// - /// Generates a random ringtone using the C pentatonic scale. + /// Generates a random ringtone using the C major scale. /// + /// Exclude any ringtone registered to ReservedSerializedRingtones. + /// Add the generated ringtone to ReservedSerializedRingtones. Requires ExcludeReserved to be true. /// An array of Notes representing the ringtone. /// The logic for this is on the Server so that we don't get a different result on the Client every time. - private Note[] GenerateRingtone() + private Note[]? GenerateRingtone(bool excludeReserved = false, bool reserveRingtone = false) { - // Default to using C pentatonic so it at least sounds not terrible. - return GenerateRingtone(new[] - { - Note.C, - Note.D, - Note.E, - Note.G, - Note.A - }); + // Default to using C major so it at least sounds not terrible. + return GenerateRingtone(AllowedNotes, excludeReserved, reserveRingtone); } /// /// Generates a random ringtone using the specified notes. /// /// The notes to choose from when generating the ringtone. + /// Exclude any ringtone registered to ReservedSerializedRingtones. + /// Add the generated ringtone to ReservedSerializedRingtones. Requires ExcludeReserved to be true. /// An array of Notes representing the ringtone. /// The logic for this is on the Server so that we don't get a different result on the Client every time. - private Note[] GenerateRingtone(Note[] notes) + private Note[]? GenerateRingtone(Note[] notes, bool excludeReserved = false, bool reserveRingtone = false) { - var ringtone = new Note[RingtoneLength]; + var excludedRingtones = excludeReserved ? ReservedSerializedRingtones.ToArray() : null; + + var maxPow = Math.Pow(notes.Length, RingtoneLength); + if (maxPow > int.MaxValue) + { + return null; + } + + var generatedRingtone = NextIntInRangeButExclude(0, Convert.ToInt32(maxPow) - 1, excludedRingtones); + + if (!TryDeserializeRingtone(notes, generatedRingtone, out var ringtone)) + return null; + + if (excludeReserved && reserveRingtone) + ReservedSerializedRingtones.Add(generatedRingtone); + + return ringtone; + } + + /// + /// Serialize a ringtone, representing it as an Int32. + /// + /// The array of notes used to generate the ringtone. + /// The ringtone which needs to be serialized. + /// The ringtone in a serialized format. + /// Whether the ringtone could be serialized or not. + private bool TrySerializeRingtone(Note[] allowedNotes, Note[] ringtone, [NotNullWhen(true)] out int? serializedRingtone) + { + var noteLength = allowedNotes.Length; + + // The serialization stores as an Int32, and therefore using Pow risks overshooting the max value, so we check for if that's a risk. + // If using 12 possible notes, you can have a ringtone sequence of 7 notes safely without overshooting. + var maxPow = Math.Pow(noteLength, ringtone.Length); + if (maxPow > int.MaxValue) + { + serializedRingtone = null; + return false; + } + + var serializationValue = 0; + + for (var i = 0; i < ringtone.Length; i++) + { + var pow = Math.Pow(noteLength, i); + var index = Array.IndexOf(allowedNotes, ringtone[i]); + if (index == -1) + { + serializedRingtone = null; + return false; + } + + serializationValue += Convert.ToInt32(pow) * index; + } + + serializedRingtone = serializationValue; + return true; + } + + /// + /// Deserialize a serialized ringtone into a Note array. + /// + /// The array of notes used to generate the ringtone. + /// The ringtone in a serialized format. + /// The ringtone resulting from the deserialization. + /// Whether the ringtone could be deserialized or not. + private bool TryDeserializeRingtone(Note[] allowedNotes, int serializedRingtone, [NotNullWhen(true)] out Note[]? ringtone) + { + var noteLength = allowedNotes.Length; + ringtone = new Note[RingtoneLength]; + + // The serialization stores as an Int32, and therefore using Pow risks overshooting the max value, so we check for if that's a risk. + // If using 12 possible notes, you can have a ringtone sequence of 7 notes safely without overshooting. + var maxPow = Math.Pow(noteLength, RingtoneLength); + if (maxPow > int.MaxValue) + { + ringtone = null; + return false; + } for (var i = 0; i < RingtoneLength; i++) { - ringtone[i] = _random.Pick(notes); + var pow = Math.Pow(noteLength, RingtoneLength - 1 - i); + var powInt = Convert.ToInt32(pow); + var val = serializedRingtone / powInt; + if (!AllowedNotes.TryGetValue(val, out var note)) + { + ringtone = null; + return false; + } + + ringtone[RingtoneLength - 1 - i] = note; + serializedRingtone -= val * powInt; } - return ringtone; + return true; + } + + /// + /// Try to get the store entity that has the matching ringer access. + /// + /// Notes from the ringer. + /// The store entity, if there is one. + /// The entity providing the code. + public bool TryMatchRingtoneToStore(Note[] notes, [NotNullWhen(true)] out EntityUid? store, EntityUid? ringer = null) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.Code != null && notes.SequenceEqual(comp.Code)) + { + if (comp.BoundEntity != null && comp.BoundEntity != ringer) + break; + + store = uid; + return true; + } + } + + store = null; + return false; + } + + private void CleanupReserved(RoundRestartCleanupEvent ev) + { + ReservedSerializedRingtones.Clear(); + } + + private int NextIntInRangeButExclude(int start, int end, int[]? excludes) + { + excludes ??= new int[0]; + Array.Sort(excludes); + var rangeLength = end - start - excludes.Length; + var randomInt = _random.Next(rangeLength) + start; + + for (var i = 0; i < excludes.Length; i++) + { + if (excludes[i] > randomInt) + { + return randomInt; + } + + randomInt++; + } + + return randomInt; + } + + public void SetBoundUplinkEntity(Entity entity, EntityUid? targetEntity) + { + entity.Comp.BoundEntity = targetEntity; } } diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 6b67ff5d60..ccf7c3f36c 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -41,6 +41,16 @@ public sealed partial class StoreSystem SubscribeLocalEvent(OnRequestWithdraw); SubscribeLocalEvent(OnRequestRefund); SubscribeLocalEvent(OnRefundEntityDeleted); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); + SubscribeLocalEvent((e, c, ev) => + RemoteStoreRelay((e, c), ev)); } private void OnRefundEntityDeleted(Entity ent, ref RefundEntityDeletedEvent args) @@ -48,21 +58,34 @@ public sealed partial class StoreSystem ent.Comp.BoughtEntities.Remove(args.Uid); } + private void RemoteStoreRelay(Entity entity, object ev) + { + if (entity.Comp.Store == null || !TryComp(entity.Comp.Store, out var store)) + return; + + RaiseLocalEvent(entity.Comp.Store.Value, ev); + } + /// /// 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) + /// 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(storeEnt, StoreUiKey.Key, actor.PlayerSession)) + if (!_ui.TryToggleUi(remoteAccess != null ? remoteAccess.Value : storeEnt, StoreUiKey.Key, actor.PlayerSession)) return; UpdateUserInterface(user, storeEnt, component); @@ -114,9 +137,27 @@ public sealed partial class StoreSystem 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); + } + } + private void OnRequestUpdate(EntityUid uid, StoreComponent component, StoreRequestUpdateInterfaceMessage args) { UpdateUserInterface(args.Actor, GetEntity(args.Entity), component); @@ -284,7 +325,8 @@ public sealed partial class StoreSystem $"{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 { diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index 3806842507..0fcabba30f 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -15,11 +15,7 @@ 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!; @@ -95,23 +91,23 @@ public sealed partial class StoreSystem : EntitySystem private void OnAfterInteract(EntityUid uid, CurrencyComponent component, AfterInteractEvent args) { - if (args.Handled || !args.CanReach) + if (args.Handled || !args.CanReach || args.Target is not { } target) return; - if (!TryComp(args.Target, out var store)) + if (!TryGetStore(target, out var store)) return; - var ev = new CurrencyInsertAttemptEvent(args.User, args.Target.Value, args.Used, store); - RaiseLocalEvent(args.Target.Value, ev); + var ev = new CurrencyInsertAttemptEvent(args.User, target, args.Used, store.Value.Comp); + RaiseLocalEvent(target, ev); if (ev.Cancelled) return; - if (!TryAddCurrency((uid, component), (args.Target.Value, store))) + if (!TryAddCurrency((uid, component), (store.Value, store.Value.Comp))) 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); + var msg = Loc.GetString("store-currency-inserted", ("used", args.Used), ("target", target)); + _popup.PopupEntity(msg, target, args.User); } private void OnImplantActivate(EntityUid uid, StoreComponent component, OpenUplinkImplantEvent args) diff --git a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs index 8350c01f7a..bf00f5ebd8 100644 --- a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs +++ b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs @@ -73,7 +73,13 @@ public sealed class AddUplinkCommand : LocalizedEntityCommands } // Finally add uplink - if (!_uplinkSystem.AddUplink(user, 20, uplinkEntity: uplinkEntity, giveDiscounts: isDiscounted)) + var result = _uplinkSystem.AddUplink(user, 20, out var code, uplinkEntity: uplinkEntity, giveDiscounts: isDiscounted); + + if (code != null && result == AddUplinkResult.Pda) + shell.WriteLine(Loc.GetString("add-uplink-command-success-pda", ("code", string.Join("-", code).Replace("sharp", "#")))); + else if (result == AddUplinkResult.Implant) + shell.WriteLine(Loc.GetString("add-uplink-command-success-implant")); + else if (result == AddUplinkResult.Failure) shell.WriteLine(Loc.GetString("add-uplink-command-error-2")); } diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs index e8ed868dfb..a8c1ab3da5 100644 --- a/Content.Server/Traitor/Uplink/UplinkSystem.cs +++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.PDA.Ringer; using Content.Server.Store.Systems; using Content.Server.StoreDiscount.Systems; using Content.Shared.FixedPoint; @@ -9,6 +10,7 @@ using Content.Shared.Mind; using Content.Shared.PDA; using Content.Shared.Store; using Content.Shared.Store.Components; +using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.Server.Traitor.Uplink; @@ -21,7 +23,9 @@ 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"; @@ -31,28 +35,72 @@ public sealed class UplinkSystem : EntitySystem /// /// 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. + /// The entity that will have the store in it. /// 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) + EntityUid? storeEntity = null, + bool giveDiscounts = false, + bool bindToPda = false) { - // Try to find target item if none passed + code = null; + if (TryAddEntityUplink(user, balance, out var generatedCode, uplinkEntity, storeEntity, giveDiscounts, bindToPda)) + { + code = generatedCode; + return AddUplinkResult.Pda; + } + + if (TryImplantUplink(user, balance, giveDiscounts)) + { + return AddUplinkResult.Implant; + } + + 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; + + storeEntity ??= Spawn(TraitorUplinkStore, MapCoordinates.Nullspace); uplinkEntity ??= FindUplinkTarget(user); if (uplinkEntity == null) - return ImplantUplink(user, balance, giveDiscounts); + return false; - EnsureComp(uplinkEntity.Value); + var ev = new GenerateUplinkCodeEvent(); + RaiseLocalEvent(storeEntity.Value, ref ev); - SetUplink(user, uplinkEntity.Value, balance, giveDiscounts); + if (ev.Code == null) + { + QueueDel(storeEntity); + return false; + } - // TODO add BUI. Currently can't be done outside of yaml -_- - // ^ What does this even mean? + code = ev.Code; + + if (bindToPda) + { + var accessComp = EnsureComp(storeEntity.Value); + _ringer.SetBoundUplinkEntity((storeEntity.Value, accessComp), uplinkEntity.Value); + } + + SetUplink(user, storeEntity.Value, uplinkEntity.Value, balance, giveDiscounts); return true; } @@ -60,25 +108,35 @@ public sealed class UplinkSystem : EntitySystem /// /// Configure TC for the uplink /// - private void SetUplink(EntityUid user, EntityUid uplink, FixedPoint2 balance, bool giveDiscounts) + private void SetUplink(EntityUid user, EntityUid store, EntityUid uplink, FixedPoint2 balance, bool giveDiscounts) + { + SetUplink(user, store, balance, giveDiscounts); + var remote = EnsureComp(uplink); + remote.Store = store; + } + + /// + /// Configure TC for the uplink + /// + 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,7 +144,7 @@ public sealed class UplinkSystem : EntitySystem /// /// Implant an uplink as a fallback measure if the traitor had no PDA /// - private bool ImplantUplink(EntityUid user, FixedPoint2 balance, bool giveDiscounts) + public bool TryImplantUplink(EntityUid user, FixedPoint2 balance, bool giveDiscounts) { if (!_proto.Resolve(FallbackUplinkCatalog, out var catalog)) return false; @@ -115,7 +173,7 @@ public sealed class UplinkSystem : EntitySystem /// Finds the entity that can hold an uplink for a user. /// Usually this is a pda in their pda slot, but can also be in their hands. (but not pockets or inside bag, etc.) ///
- public EntityUid? FindUplinkTarget(EntityUid user) + public Entity? FindUplinkTarget(EntityUid user) { // Try to find PDA in inventory if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator)) @@ -124,18 +182,25 @@ public sealed class UplinkSystem : EntitySystem { var pdaUid = containerSlot.ContainedEntity; - if (HasComp(pdaUid) && HasComp(pdaUid)) - return pdaUid; + if (HasComp(pdaUid) && TryComp(pdaUid, out var remote)) + return (pdaUid.Value, remote); } } // Also check hands foreach (var item in _handsSystem.EnumerateHeld(user)) { - if (HasComp(item) && HasComp(item)) - return item; + if (HasComp(item) && TryComp(item, out var remote)) + return (item, remote); } return null; } } + +public enum AddUplinkResult +{ + Pda, + Implant, + Failure, +} diff --git a/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs b/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs index 2dbbfb5efc..dd0de10493 100644 --- a/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs +++ b/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs @@ -3,22 +3,21 @@ using Robust.Shared.GameStates; namespace Content.Shared.PDA.Ringer; /// -/// Opens the store UI when the ringstone is set to the secret code. +/// Makes a PDA able to open store UIs when the ringtone is set to a secret code. /// Traitors are told the code when greeted. /// [RegisterComponent, NetworkedComponent, Access(typeof(SharedRingerSystem))] public sealed partial class RingerUplinkComponent : Component { - /// - /// Notes to set ringtone to in order to lock or unlock the uplink. - /// Set via GenerateUplinkCodeEvent. - /// - [DataField] - public Note[]? Code; - /// /// Whether to show the toggle uplink button in PDA settings. /// [DataField] public bool Unlocked; + + /// + /// The store which the ringer is targetting. + /// + [DataField] + public EntityUid? TargetStore; } diff --git a/Content.Shared/PDA/SharedRingerSystem.cs b/Content.Shared/PDA/SharedRingerSystem.cs index 392cd46e72..3c1e392f13 100644 --- a/Content.Shared/PDA/SharedRingerSystem.cs +++ b/Content.Shared/PDA/SharedRingerSystem.cs @@ -137,6 +137,7 @@ public abstract class SharedRingerSystem : EntitySystem return; ent.Comp.Unlocked = false; + ent.Comp.TargetStore = null; UI.CloseUi(ent.Owner, StoreUiKey.Key); } @@ -249,7 +250,10 @@ public abstract class SharedRingerSystem : EntitySystem // Close store UI if we're locking if (!ent.Comp.Unlocked) + { + ent.Comp.TargetStore = null; UI.CloseUi(ent.Owner, StoreUiKey.Key); + } return true; } diff --git a/Content.Shared/Store/Components/RemoteStoreComponent.cs b/Content.Shared/Store/Components/RemoteStoreComponent.cs new file mode 100644 index 0000000000..3b07e03e09 --- /dev/null +++ b/Content.Shared/Store/Components/RemoteStoreComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Store.Components; + +/// +/// This component manages a store which players can use to purchase different listings +/// through the ui. The currency, listings, and categories are defined in yaml. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class RemoteStoreComponent : Component +{ + /// + /// The store which is currently being targetted for remote opening + /// + [DataField] + public EntityUid? Store; +} diff --git a/Content.Shared/Store/SharedStoreSystem.cs b/Content.Shared/Store/SharedStoreSystem.cs new file mode 100644 index 0000000000..0abd12742e --- /dev/null +++ b/Content.Shared/Store/SharedStoreSystem.cs @@ -0,0 +1,59 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Store.Components; + +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 EntityQuery StoreQuery = default!; + [Dependency] protected readonly EntityQuery RemoteStoreQuery = default!; + + /// + /// 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(Entity 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(Entity entity) + { + if (StoreQuery.TryComp(entity, out var storeComp)) + return (entity, storeComp); + + return GetRemoteStore(entity); + } + + /// + /// 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, false) + && entity.Comp.Store != null + && StoreQuery.TryComp(entity.Comp.Store, out var storeComp)) + return (entity.Comp.Store.Value, storeComp); + + return null; + } +} diff --git a/Content.Shared/Store/StoreUi.cs b/Content.Shared/Store/StoreUi.cs index d8cb9e6ca8..531788df1f 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/Resources/Locale/en-US/administration/commands/add-uplink-command.ftl b/Resources/Locale/en-US/administration/commands/add-uplink-command.ftl index 40d0c5fa1b..a0cee58050 100644 --- a/Resources/Locale/en-US/administration/commands/add-uplink-command.ftl +++ b/Resources/Locale/en-US/administration/commands/add-uplink-command.ftl @@ -5,4 +5,6 @@ add-uplink-command-completion-1 = Username (defaults to self) add-uplink-command-completion-2 = Uplink uid (default to PDA) add-uplink-command-completion-3 = Is uplink discount enabled add-uplink-command-error-1 = Selected player doesn't control any entity -add-uplink-command-error-2 = Failed to add uplink to the player \ No newline at end of file +add-uplink-command-error-2 = Failed to add uplink to the player +add-uplink-command-success-pda = Uplink added to player PDA with code {$code} +add-uplink-command-success-implant = Uplink added to player as an implant diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 79beb70551..ad3b59b0ea 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/Store/presets.yml b/Resources/Prototypes/Store/presets.yml index 13823a40f1..d4c1245c55 100644 --- a/Resources/Prototypes/Store/presets.yml +++ b/Resources/Prototypes/Store/presets.yml @@ -51,3 +51,10 @@ - ChangelingAbilities currencyWhitelist: - ChangelingDNA + +- type: entity + parent: StorePresetUplink + id: StorePresetRemoteUplink + categories: [ HideSpawnMenu ] + components: + - type: RingerAccessUplink From 1a8b1c48b936735635d2472afd48038f57d65f7e Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 4 Apr 2026 20:15:23 +0000 Subject: [PATCH 082/247] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ Resources/Changelog/Changelog.yml | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index dcaae63db7..cf6842025e 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1691,5 +1691,13 @@ 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 Name: Admin Order: 3 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7b35a1062c..769227c0d6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4033,3 +4025,13 @@ 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 From fd9212c73a68ec23f6668ed1cb8d35b434941de8 Mon Sep 17 00:00:00 2001 From: Kyle Tyo Date: Sat, 4 Apr 2026 17:09:24 -0400 Subject: [PATCH 083/247] Remove delta_alt.ogg (#43468) Delete delta_alt.ogg --- Resources/Audio/Misc/delta_alt.ogg | Bin 226574 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Resources/Audio/Misc/delta_alt.ogg diff --git a/Resources/Audio/Misc/delta_alt.ogg b/Resources/Audio/Misc/delta_alt.ogg deleted file mode 100644 index b389910f136b91b55a7aad5975ca23318488e55e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226574 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 z 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

)84o4 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�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

SU0I<`OOb3e4aLM%K+7bMF<)9G%3(^dldH1 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 iy&Plo*UQB@;(lqdgwjzfED_ Date: Sat, 4 Apr 2026 17:29:38 -0400 Subject: [PATCH 084/247] Update DungeonSystem.Rooms to use IRobustRandom instead of Random (#43457) * Use IRobustRandom instead of System.Random * Use IRobustRandom instead of System.Random * Add Robust.Shared.Random directive --- Content.Server/Procedural/DungeonSystem.Rooms.cs | 9 +++++---- Content.Server/Procedural/RoomFillSystem.cs | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Content.Server/Procedural/DungeonSystem.Rooms.cs b/Content.Server/Procedural/DungeonSystem.Rooms.cs index e5b0981b3d..0854fbb172 100644 --- a/Content.Server/Procedural/DungeonSystem.Rooms.cs +++ b/Content.Server/Procedural/DungeonSystem.Rooms.cs @@ -7,6 +7,7 @@ using Content.Shared.Whitelist; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Utility; +using Robust.Shared.Random; namespace Content.Server.Procedural; @@ -19,7 +20,7 @@ public sealed partial class DungeonSystem ///

/// Gets a random dungeon room matching the specified area, whitelist and size. /// - public DungeonRoomPrototype? GetRoomPrototype(Vector2i size, Random random, EntityWhitelist? whitelist = null) + public DungeonRoomPrototype? GetRoomPrototype(Vector2i size, IRobustRandom random, EntityWhitelist? whitelist = null) { return GetRoomPrototype(random, whitelist, minSize: size, maxSize: size); } @@ -27,7 +28,7 @@ public sealed partial class DungeonSystem /// /// Gets a random dungeon room matching the specified area and whitelist and size range /// - public DungeonRoomPrototype? GetRoomPrototype(Random random, + public DungeonRoomPrototype? GetRoomPrototype(IRobustRandom random, EntityWhitelist? whitelist = null, Vector2i? minSize = null, Vector2i? maxSize = null) @@ -77,7 +78,7 @@ public sealed partial class DungeonSystem MapGridComponent grid, Vector2i origin, DungeonRoomPrototype room, - Random random, + IRobustRandom random, HashSet? reservedTiles, bool clearExisting = false, bool rotation = false) @@ -96,7 +97,7 @@ public sealed partial class DungeonSystem SpawnRoom(gridUid, grid, finalTransform, room, reservedTiles, clearExisting); } - public Angle GetRoomRotation(DungeonRoomPrototype room, Random random) + public Angle GetRoomRotation(DungeonRoomPrototype room, IRobustRandom random) { var roomRotation = Angle.Zero; diff --git a/Content.Server/Procedural/RoomFillSystem.cs b/Content.Server/Procedural/RoomFillSystem.cs index f4ccab2367..96adb8dec3 100644 --- a/Content.Server/Procedural/RoomFillSystem.cs +++ b/Content.Server/Procedural/RoomFillSystem.cs @@ -1,4 +1,5 @@ using Robust.Shared.Map.Components; +using Robust.Shared.Random; namespace Content.Server.Procedural; @@ -6,6 +7,7 @@ public sealed class RoomFillSystem : EntitySystem { [Dependency] private readonly DungeonSystem _dungeon = default!; [Dependency] private readonly SharedMapSystem _maps = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -19,8 +21,7 @@ public sealed class RoomFillSystem : EntitySystem if (xform.GridUid != null) { - var random = new Random(); - var room = _dungeon.GetRoomPrototype(random, component.RoomWhitelist, component.MinSize, component.MaxSize); + var room = _dungeon.GetRoomPrototype(_random, component.RoomWhitelist, component.MinSize, component.MaxSize); if (room != null) { @@ -30,7 +31,7 @@ public sealed class RoomFillSystem : EntitySystem mapGrid, _maps.LocalToTile(xform.GridUid.Value, mapGrid, xform.Coordinates) - new Vector2i(room.Size.X/2,room.Size.Y/2), room, - random, + _random, null, clearExisting: component.ClearExisting, rotation: component.Rotation); From 9d9294cdfdce8aeb78f3e409377de4542c9a150e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2026 03:04:39 +0200 Subject: [PATCH 085/247] Update Credits (#43473) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 221a6124d5..64ab3e20ae 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, 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, 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-A-Chair, 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, 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, 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, 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, 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, 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-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, 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, 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, zekins3366, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex From ca95da6daaf595c475a144c82c8e44de31d90c5c Mon Sep 17 00:00:00 2001 From: Naxel <46362288+Naxel11@users.noreply.github.com> Date: Sun, 5 Apr 2026 07:39:00 +0300 Subject: [PATCH 086/247] Add parallelization to RadiationSystem.GridCast (#39173) * add parallelization to RadiationSystem.GridCast * change the preparation of sources receivers data * failed integrationtest fix * Update Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs Co-authored-by: Southbridge <7013162+southbridge-fur@users.noreply.github.com> * Change namespace to file-scoped * Method signature changed * null check changed * null check changed. exactly * Use TryComp instead of Resolve in UpdateSource * replace TryComp with xform in UpdateSource * replace DynamicTree with B2DynamicTree * return comment * Remove magic number * Optimize radiation memory usage * replace ThreadLocal to ArrayPool * fix "false sharing" issue * Revert "fix "false sharing" issue" This reverts commit c0729f024f8e2df9d15f1cc9cc9bc1be4d4be312. * cleanup * inject entquery --------- Co-authored-by: xNaxelx <46362288+xNaxelx@users.noreply.github.com> Co-authored-by: Southbridge <7013162+southbridge-fur@users.noreply.github.com> Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> --- .../Systems/RadiationSystem.GridCast.cs | 294 +++++++++--------- .../Radiation/Systems/RadiationSystem.cs | 111 ++++++- .../Components/RadiationSourceComponent.cs | 5 + 3 files changed, 251 insertions(+), 159 deletions(-) diff --git a/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs b/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs index 796c1e1fce..8bbfa7ad41 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs @@ -1,11 +1,15 @@ +using System.Buffers; +using System.Collections.Concurrent; +using System.Linq; using System.Numerics; using Content.Server.Radiation.Components; using Content.Server.Radiation.Events; using Content.Shared.Radiation.Components; using Content.Shared.Radiation.Systems; -using Robust.Shared.Collections; +using JetBrains.Annotations; using Robust.Shared.Map.Components; -using Robust.Shared.Timing; +using Robust.Shared.Physics; +using Robust.Shared.Threading; using Robust.Shared.Utility; namespace Content.Server.Radiation.Systems; @@ -13,113 +17,62 @@ namespace Content.Server.Radiation.Systems; // main algorithm that fire radiation rays to target public partial class RadiationSystem { - private List> _grids = new(); - private readonly record struct SourceData( float Intensity, + float Slope, + float MaxRange, Entity Entity, Vector2 WorldPosition) { - public EntityUid? GridUid => Entity.Comp2.GridUid; - public float Slope => Entity.Comp1.Slope; + public EntityUid Uid => Entity.Owner; public TransformComponent Transform => Entity.Comp2; } private void UpdateGridcast() { - // should we save debug information into rays? - // if there is no debug sessions connected - just ignore it var debug = _debugSessions.Count > 0; - - var stopwatch = new Stopwatch(); + var stopwatch = new Robust.Shared.Timing.Stopwatch(); stopwatch.Start(); - _sources.Clear(); - _sources.EnsureCapacity(Count()); - - var sources = EntityQueryEnumerator(); - var destinations = EntityQueryEnumerator(); - - while (sources.MoveNext(out var uid, out var source, out var xform)) + var sourcesCount = _sourceDataMap.Count; + if (_activeReceivers.Count == 0 || sourcesCount == 0) { - if (!source.Enabled) - continue; - - var worldPos = _transform.GetWorldPosition(xform); - - // Intensity is scaled by stack size. - var intensity = source.Intensity * _stack.GetCount(uid); - - // Apply rad modifier if the source is enclosed within a radiation blocking container - // Note that this also applies to receivers, and it doesn't bother to check if the container sits between them. - // I.e., a source & receiver in the same blocking container will get double-blocked, when no blocking should be applied. - intensity = GetAdjustedRadiationIntensity(uid, intensity); - - _sources.Add(new(intensity, (uid, source, xform), worldPos)); + RaiseLocalEvent(new RadiationSystemUpdatedEvent()); + return; } - var debugRays = debug ? new List() : null; - var receiversTotalRads = new ValueList<(Entity, float)>(); + var results = new float[_activeReceivers.Count]; + var debugRays = debug ? new ConcurrentBag() : null; - // TODO RADIATION Parallelize - // Would need to give receiversTotalRads a fixed size. - // Also the _grids list needs to be local to a job. (or better yet cached in SourceData) - // And I guess disable parallelization when debugging to make populating the debug List easier. - // Or just make it threadsafe? - while (destinations.MoveNext(out var destUid, out var dest, out var destTrs)) + var job = new RadiationJob { - var destWorld = _transform.GetWorldPosition(destTrs); + System = this, + SourceTree = _sourceTree, + SourceDataMap = _sourceDataMap, + Destinations = _activeReceivers, + Results = results, + DebugRays = debugRays, + Debug = debug + }; - var rads = 0f; - foreach (var source in _sources) + _parallel.ProcessNow(job, _activeReceivers.Count); + + for (var i = 0; i < _activeReceivers.Count; i++) + { + var uid = _activeReceivers[i]; + var rads = results[i]; + + if (_receiverQuery.TryComp(uid, out var receiver)) { - // send ray towards destination entity - if (Irradiate(source, destUid, destTrs, destWorld, debug) is not { } ray) - continue; - - // add rads to total rad exposure - if (ray.ReachedDestination) - rads += ray.Rads; - - if (!debug) - continue; - - debugRays!.Add(new DebugRadiationRay( - ray.MapId, - GetNetEntity(ray.SourceUid), - ray.Source, - GetNetEntity(ray.DestinationUid), - ray.Destination, - ray.Rads, - ray.Blockers ?? new()) - ); + receiver.CurrentRadiation = rads; + if (rads > 0) + IrradiateEntity(uid, rads, GridcastUpdateRate); } - - // Apply modifier if the destination entity is hidden within a radiation blocking container - rads = GetAdjustedRadiationIntensity(destUid, rads); - - receiversTotalRads.Add(((destUid, dest), rads)); } - // update information for debug overlay - var elapsedTime = stopwatch.Elapsed.TotalMilliseconds; - var totalSources = _sources.Count; - var totalReceivers = receiversTotalRads.Count; - UpdateGridcastDebugOverlay(elapsedTime, totalSources, totalReceivers, debugRays); + if (debugRays is not null) + UpdateGridcastDebugOverlay(stopwatch.Elapsed.TotalMilliseconds, sourcesCount, _activeReceivers.Count, debugRays.ToList()); - // send rads to each entity - foreach (var (receiver, rads) in receiversTotalRads) - { - // update radiation value of receiver - // if no radiation rays reached target, that will set it to 0 - receiver.Comp.CurrentRadiation = rads; - - // also send an event with combination of total rad - if (rads > 0) - IrradiateEntity(receiver, rads, GridcastUpdateRate); - } - - // raise broadcast event that radiation system has updated RaiseLocalEvent(new RadiationSystemUpdatedEvent()); } @@ -127,68 +80,28 @@ public partial class RadiationSystem EntityUid destUid, TransformComponent destTrs, Vector2 destWorld, - bool saveVisitedTiles) + bool saveVisitedTiles, + List> gridList) { - // lets first check that source and destination on the same map - if (source.Transform.MapID != destTrs.MapID) - return null; - var mapId = destTrs.MapID; - - // get direction from rad source to destination and its distance - var dir = destWorld - source.WorldPosition; - var dist = dir.Length(); - - // check if receiver is too far away - if (dist > GridcastMaxDistance) - return null; - - // will it even reach destination considering distance penalty + var dist = (destWorld - source.WorldPosition).Length(); var rads = source.Intensity - source.Slope * dist; if (rads < MinIntensity) return null; - // create a new radiation ray from source to destination - // at first we assume that it doesn't hit any radiation blockers - // and has only distance penalty var ray = new RadiationRay(mapId, source.Entity, source.WorldPosition, destUid, destWorld, rads); - // if source and destination on the same grid it's possible that - // between them can be another grid (ie. shuttle in center of donut station) - // however we can do simplification and ignore that case - if (GridcastSimplifiedSameGrid && destTrs.GridUid is { } gridUid && source.GridUid == gridUid) - { - if (!_gridQuery.TryGetComponent(gridUid, out var gridComponent)) - return ray; - return Gridcast((gridUid, gridComponent, Transform(gridUid)), ref ray, saveVisitedTiles, source.Transform, destTrs); - } - - // lets check how many grids are between source and destination - // do a box intersection test between target and destination - // it's not very precise, but really cheap - - // TODO RADIATION - // Consider caching this in SourceData? - // I.e., make the lookup for grids as large as the sources's max distance and store the result in SourceData. - // Avoids having to do a lookup per source*receiver. var box = Box2.FromTwoPoints(source.WorldPosition, destWorld); - _grids.Clear(); - _mapManager.FindGridsIntersecting(mapId, box, ref _grids, true); + gridList.Clear(); + _mapManager.FindGridsIntersecting(mapId, box, ref gridList, true); - // gridcast through each grid and try to hit some radiation blockers - // the ray will be updated with each grid that has some blockers - foreach (var grid in _grids) + foreach (var grid in gridList) { ray = Gridcast((grid.Owner, grid.Comp, Transform(grid)), ref ray, saveVisitedTiles, source.Transform, destTrs); - - // looks like last grid blocked all radiation - // we can return right now if (ray.Rads <= 0) return ray; } - _grids.Clear(); - return ray; } @@ -200,19 +113,12 @@ public partial class RadiationSystem TransformComponent destTrs) { var blockers = saveVisitedTiles ? new List<(Vector2i, float)>() : null; - - // if grid doesn't have resistance map just apply distance penalty var gridUid = grid.Owner; if (!_resistanceQuery.TryGetComponent(gridUid, out var resistance)) return ray; + var resistanceMap = resistance.ResistancePerTile; - // get coordinate of source and destination in grid coordinates - - // TODO Grid overlap. This currently assumes the grid is always parented directly to the map (local matrix == world matrix). - // If ever grids are allowed to overlap, this might no longer be true. In that case, this should precompute and cache - // inverse world matrices. - Vector2 srcLocal = sourceTrs.ParentUid == grid.Owner ? sourceTrs.LocalPosition : Vector2.Transform(ray.Source, grid.Comp2.InvLocalMatrix); @@ -221,28 +127,20 @@ public partial class RadiationSystem ? destTrs.LocalPosition : Vector2.Transform(ray.Destination, grid.Comp2.InvLocalMatrix); - Vector2i sourceGrid = new( - (int)Math.Floor(srcLocal.X / grid.Comp1.TileSize), - (int)Math.Floor(srcLocal.Y / grid.Comp1.TileSize)); + Vector2i sourceGrid = new((int)Math.Floor(srcLocal.X / grid.Comp1.TileSize), (int)Math.Floor(srcLocal.Y / grid.Comp1.TileSize)); + Vector2i destGrid = new((int)Math.Floor(dstLocal.X / grid.Comp1.TileSize), (int)Math.Floor(dstLocal.Y / grid.Comp1.TileSize)); - Vector2i destGrid = new( - (int)Math.Floor(dstLocal.X / grid.Comp1.TileSize), - (int)Math.Floor(dstLocal.Y / grid.Comp1.TileSize)); - - // iterate tiles in grid line from source to destination var line = new GridLineEnumerator(sourceGrid, destGrid); while (line.MoveNext()) { var point = line.Current; if (!resistanceMap.TryGetValue(point, out var resData)) continue; + ray.Rads -= resData; + if (saveVisitedTiles && blockers is not null) + blockers.Add((point, ray.Rads)); - // save data for debug - if (saveVisitedTiles) - blockers!.Add((point, ray.Rads)); - - // no intensity left after blocker if (ray.Rads <= MinIntensity) { ray.Rads = 0; @@ -250,13 +148,11 @@ public partial class RadiationSystem } } - if (!saveVisitedTiles || blockers!.Count <= 0) + if (blockers is null || blockers.Count == 0) return ray; - // save data for debug if needed ray.Blockers ??= new(); ray.Blockers.Add(GetNetEntity(gridUid), blockers); - return ray; } @@ -291,4 +187,92 @@ public partial class RadiationSystem return rads; } + + [UsedImplicitly] + private readonly record struct RadiationJob : IParallelRobustJob + { + public int BatchSize => 5; + public required RadiationSystem System { get; init; } + public required B2DynamicTree SourceTree { get; init; } + public required Dictionary SourceDataMap { get; init; } + public required List Destinations { get; init; } + public required float[] Results { get; init; } + public required ConcurrentBag? DebugRays { get; init; } + public required bool Debug { get; init; } + + public void Execute(int index) + { + var destUid = Destinations[index]; + if (System.Deleted(destUid) || !System.TryComp(destUid, out TransformComponent? destTrs)) + { + Results[index] = 0; + return; + } + + var nearbySourcesArray = ArrayPool.Shared.Rent(256); + + var gridList = new List>(8); + + try + { + var destWorld = System._transform.GetWorldPosition(destTrs); + var rads = 0f; + var destMapId = destTrs.MapID; + + var queryAabb = new Box2(destWorld, destWorld); + + var state = (nearbySourcesArray, 0, SourceTree); + SourceTree.Query(ref state, + static (ref (EntityUid[] arr, int count, B2DynamicTree tree) tuple, + DynamicTree.Proxy proxy) => + { + if (tuple.count >= tuple.arr.Length) + return true; + + var uid = tuple.tree.GetUserData(proxy); + tuple.arr[tuple.count++] = uid; + return true; + }, + in queryAabb); + + var nearbySourcesSpan = nearbySourcesArray.AsSpan(0, state.Item2); + + foreach (var sourceUid in nearbySourcesSpan) + { + if (!SourceDataMap.TryGetValue(sourceUid, out var source) + || source.Transform.MapID != destMapId) + continue; + var delta = source.WorldPosition - destWorld; + if (delta.LengthSquared() > source.MaxRange * source.MaxRange) + continue; + var dist = delta.Length(); + var radsAfterDist = source.Intensity - source.Slope * dist; + if (radsAfterDist < System.MinIntensity) + continue; + if (System.Irradiate(source, destUid, destTrs, destWorld, Debug, gridList) is not { } ray) + continue; + + if (ray.ReachedDestination) + rads += ray.Rads; + + DebugRays?.Add(new DebugRadiationRay( + ray.MapId, + System.GetNetEntity(ray.SourceUid), + ray.Source, + System.GetNetEntity(ray.DestinationUid), + ray.Destination, + ray.Rads, + ray.Blockers ?? new Dictionary>()) + ); + } + + rads = System.GetAdjustedRadiationIntensity(destUid, rads); + Results[index] = rads; + } + finally + { + ArrayPool.Shared.Return(nearbySourcesArray); + } + } + } } diff --git a/Content.Server/Radiation/Systems/RadiationSystem.cs b/Content.Server/Radiation/Systems/RadiationSystem.cs index 4a4d0f8930..39d8d6cd33 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.cs @@ -4,7 +4,9 @@ using Content.Shared.Radiation.Events; using Content.Shared.Stacks; using Robust.Shared.Configuration; using Robust.Shared.Map; -using Robust.Shared.Map.Components; +using Robust.Shared.Physics; +using Robust.Shared.Threading; +using System.Numerics; namespace Content.Server.Radiation.Systems; @@ -15,14 +17,18 @@ public sealed partial class RadiationSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] private readonly SharedMapSystem _maps = default!; + [Dependency] private readonly IParallelManager _parallel = default!; + [Dependency] private readonly EntityQuery _receiverQuery = default!; private EntityQuery _blockerQuery; private EntityQuery _resistanceQuery; - private EntityQuery _gridQuery; private EntityQuery _stackQuery; + private readonly B2DynamicTree _sourceTree = new(); + private readonly Dictionary _sourceDataMap = new(); + private readonly List _activeReceivers = new(); + private float _accumulator; - private List _sources = new(); public override void Initialize() { @@ -32,8 +38,102 @@ public sealed partial class RadiationSystem : EntitySystem _blockerQuery = GetEntityQuery(); _resistanceQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); _stackQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnSourceInit); + SubscribeLocalEvent(OnSourceShutdown); + SubscribeLocalEvent(OnSourceMove); + SubscribeLocalEvent(OnSourceStackChanged); + + SubscribeLocalEvent(OnReceiverInit); + SubscribeLocalEvent(OnReceiverShutdown); + } + + private void OnSourceInit(Entity entity, ref ComponentInit args) + { + UpdateSource(entity); + } + + private void OnSourceShutdown(EntityUid uid, RadiationSourceComponent component, ComponentShutdown args) + { + if (component.Proxy != DynamicTree.Proxy.Free) + { + _sourceTree.DestroyProxy(component.Proxy); + component.Proxy = DynamicTree.Proxy.Free; + } + _sourceDataMap.Remove(uid); + } + + private void OnSourceMove(Entity entity, ref MoveEvent args) + { + if (args.NewPosition.EntityId == args.OldPosition.EntityId && + args.NewPosition.Position.EqualsApprox(args.OldPosition.Position)) + return; + + UpdateSource(entity); + } + + private void OnSourceStackChanged(Entity entity, ref StackCountChangedEvent args) + { + UpdateSource(entity); + } + + private void OnReceiverInit(EntityUid uid, RadiationReceiverComponent component, ComponentInit args) + { + _activeReceivers.Add(uid); + } + + private void OnReceiverShutdown(EntityUid uid, RadiationReceiverComponent component, ComponentShutdown args) + { + _activeReceivers.Remove(uid); + } + + private void UpdateSource(Entity entity) + { + var (uid, component) = entity; + var xform = Transform(uid); + + if (!component.Enabled || Terminating(uid)) + { + if (component.Proxy != DynamicTree.Proxy.Free) + { + _sourceTree.DestroyProxy(component.Proxy); + component.Proxy = DynamicTree.Proxy.Free; + } + _sourceDataMap.Remove(uid); + return; + } + + var worldPos = _transform.GetWorldPosition(xform); + var intensity = component.Intensity * _stack.GetCount(uid); + intensity = GetAdjustedRadiationIntensity(uid, intensity); + + if (intensity <= 0) + { + if (component.Proxy != DynamicTree.Proxy.Free) + { + _sourceTree.DestroyProxy(component.Proxy); + component.Proxy = DynamicTree.Proxy.Free; + } + _sourceDataMap.Remove(uid); + return; + } + + // Avoid division by 0 + var maxRange = component.Slope >= float.Epsilon ? intensity / component.Slope : GridcastMaxDistance; + maxRange = Math.Min(maxRange, GridcastMaxDistance); + + _sourceDataMap[uid] = new SourceData(intensity, component.Slope, maxRange, (uid, component, xform), worldPos); + var aabb = Box2.CenteredAround(worldPos, new Vector2(maxRange * 2, maxRange * 2)); + + if (component.Proxy != DynamicTree.Proxy.Free) + { + _sourceTree.MoveProxy(component.Proxy, in aabb); + } + else + { + component.Proxy = _sourceTree.CreateProxy(in aabb, uint.MaxValue, uid); + } } public override void Update(float frameTime) @@ -59,8 +159,11 @@ public sealed partial class RadiationSystem : EntitySystem { if (!Resolve(entity, ref entity.Comp, false)) return; + if (entity.Comp.Enabled == val) + return; entity.Comp.Enabled = val; + UpdateSource((entity.Owner, entity.Comp)); } /// diff --git a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs index 874024a905..337e9c648e 100644 --- a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs +++ b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs @@ -1,3 +1,5 @@ +using Robust.Shared.Physics; + namespace Content.Shared.Radiation.Components; /// @@ -26,4 +28,7 @@ public sealed partial class RadiationSourceComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public bool Enabled = true; + + [ViewVariables] + public DynamicTree.Proxy Proxy = DynamicTree.Proxy.Free; } From 209c7f169f0aa969d15ec3cf41ca0335f234049d Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 5 Apr 2026 04:54:59 +0000 Subject: [PATCH 087/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 769227c0d6..53ac6ff490 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4035,3 +4028,11 @@ 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 From c5c7549dc23c8ca2882c33f8ffd7a68365e3dd0d Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sat, 4 Apr 2026 22:21:48 -0700 Subject: [PATCH 088/247] Remove ambuzol and ambuzol plus from pill canister random (#43458) * remove ambuzol and ambuzol plus from pill canister random * cleanup * review --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Objects/Specific/Medical/healing.yml | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml index c09bef4a3a..7555e88861 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -834,40 +834,33 @@ storagebase: !type:GroupSelector children: - id: PillDexalin - amount: &Range1to6 !type:RangeNumberSelector - range: 1, 6 + amount: 1, 6 - id: PillDylovene - amount: *Range1to6 + amount: 1, 6 - id: PillHyronalin - amount: *Range1to6 + amount: 1, 6 - id: PillPotassiumIodide - amount: *Range1to6 + amount: 1, 6 - id: PillIron - amount: *Range1to6 + amount: 1, 6 - id: PillCopper - amount: *Range1to6 + amount: 1, 6 - id: PillKelotane - amount: *Range1to6 + amount: 1, 6 - id: PillDermaline - amount: *Range1to6 + amount: 1, 6 - id: PillTricordrazine - amount: *Range1to6 + amount: 1, 6 - id: PillBicaridine - amount: *Range1to6 + amount: 1, 6 - id: PillCharcoal - amount: *Range1to6 - - id: PillAmbuzol - weight: 0.75 - amount: *Range1to6 - - id: PillAmbuzolPlus - weight: 0.75 - amount: *Range1to6 + amount: 1, 6 - id: PillSpaceDrugs weight: 0.75 - amount: *Range1to6 + amount: 1, 6 - id: StrangePill weight: 0.75 - amount: *Range1to6 + amount: 1, 6 # Syringes - type: entity From 9d51bad8be41af9a98f3dbac137604d3feeca8cb Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 5 Apr 2026 05:37:38 +0000 Subject: [PATCH 089/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 53ac6ff490..f493a253d5 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4036,3 +4029,10 @@ 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 From 5b53d2489166063c8657e3699f954d078be36206 Mon Sep 17 00:00:00 2001 From: ThatGuyUSA Date: Sat, 4 Apr 2026 23:10:57 -0700 Subject: [PATCH 090/247] Wire interface syntax correction (#43475) * save station * Revert "Merge branch 'wiz-guardian-deck'" This reverts commit 78fa318583b6c93110c47e3b9e23f7222747f89a, reversing changes made to 4d5dab1098bcfdbce14906d9c77dbc669e295760. * syntax --- Content.Shared/Wires/SharedWiresComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Wires/SharedWiresComponent.cs b/Content.Shared/Wires/SharedWiresComponent.cs index d691e854ea..8fdb3de0b1 100644 --- a/Content.Shared/Wires/SharedWiresComponent.cs +++ b/Content.Shared/Wires/SharedWiresComponent.cs @@ -242,7 +242,7 @@ namespace Content.Shared.Wires WireLetter.γ => "wire-letter-name-gamma", WireLetter.δ => "wire-letter-name-delta", WireLetter.ε => "wire-letter-name-epsilon", - WireLetter.ζ => "wire-letter-name-zeta ", + WireLetter.ζ => "wire-letter-name-zeta", WireLetter.η => "wire-letter-name-eta", WireLetter.θ => "wire-letter-name-theta", WireLetter.ι => "wire-letter-name-iota", From 8280ec0bbb7b20b3b076f541cfce016372a70dc9 Mon Sep 17 00:00:00 2001 From: K-Dynamic <20566341+K-Dynamic@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:02:58 +1200 Subject: [PATCH 091/247] Ports honkmother mitre from /tg/station 13, renames honkmother coat (#35237) * initial assets and attribution * 4 space meta.json * remove all ported assets except clown mitre * change honkmother mitre description * add mitre to loadout and theater vendor * icon outline change Co-authored-by: TiniestShark --------- Co-authored-by: TiniestShark --- .../VendingMachines/Inventories/theater.yml | 1 + .../Entities/Clothing/Head/hats.yml | 11 ++++ .../Entities/Clothing/OuterClothing/coats.yml | 2 +- .../Loadouts/Jobs/Civilian/clown.yml | 5 ++ .../Loadouts/LoadoutGroups/loadout_groups.yml | 1 + .../Hats/mitre_clown.rsi/equipped-HELMET.png | Bin 0 -> 617 bytes .../Head/Hats/mitre_clown.rsi/icon.png | Bin 0 -> 511 bytes .../Head/Hats/mitre_clown.rsi/inhand-left.png | Bin 0 -> 585 bytes .../Hats/mitre_clown.rsi/inhand-right.png | Bin 0 -> 574 bytes .../Head/Hats/mitre_clown.rsi/meta.json | 26 ++++++++++ .../Coats/clownpriest.rsi/inhand-left.png | Bin 692 -> 665 bytes .../Coats/clownpriest.rsi/meta.json | 48 +++++++++--------- 12 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Head/Hats/mitre_clown.rsi/meta.json diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml index 7e7bb37162..4ceaeef80c 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml @@ -40,6 +40,7 @@ ClothingUniformJumpsuitDameDane: 2 ClothingShoesDameDane: 2 ClothingOuterDameDane: 2 + ClothingHeadHatMitreClown: 1 ClothingOuterClownPriest: 1 ClothingMaskSadMime: 1 ClothingMaskScaredMime: 1 diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index 44e4983fd8..af513936da 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/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index 22ad9096f4..a05cf9baa4 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/Loadouts/Jobs/Civilian/clown.yml b/Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml index 3e4823d449..b9c87343b2 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/LoadoutGroups/loadout_groups.yml b/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml index 52106d2d6f..34a26f28ec 100644 --- a/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml @@ -512,6 +512,7 @@ loadouts: - JesterHat - JesterAltHat + - ClownMitre - type: loadoutGroup id: ClownJumpsuit 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 0000000000000000000000000000000000000000..5647e824379ce4a83942e3eec105ddca2681d6d7 GIT binary patch literal 617 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|S$^ zr;B4q#hkaZY`qT$h_u~rSREg2zEI@Pfwf=6MPiOTc=T$S-dFVwi(Tb~Aq7RNOWx>j z;A@J=pV-x1BIqbMr^nJTeATwb$XrpfoME4jBW|5Ukvt<3JXtV5#7P1DV-Cw~XeNHfm*FLqzu zUPp(qy^i~H#?iG^U-k;sbG(W?ka18*!0+78XuHn!<_qlaaHrTa)pOo$&{ptx^}NqA zh^b>u_`?&NC!|Cf9@&Dcsre8Cc6_?c1R5WVa9h>KO=J_PA12Hk(%cE=^Z->0NQ@XtQ z`dhVtxvkubHlEE&vYURT-$6uz?e=}KWS*Fd*54M?=sE9Yu4?WVd;Rer!~ICkyWjt= zu<}v;^!4=9g=%?;+!7&+zt?5HZ&=I6-u+ene9bIgsT{`oy`G0AO)dB<-Gb>>Y~I+z ZymapAxJN3F=K|9XgQu&X%Q~loCIFkL2}l3{ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..abf8dbbcdffa7114d01d0e1666dbb2bb09344d44 GIT binary patch literal 511 zcmVtI1pbg@|0j~sLd$HOLZcZqIh;zh`F88UhEzR$dwOdu!cE#vx*svk{g%UXOF)c)LY zCJCeBUw)6NudCzA^&PbfN`W$w(C&ciJ8Hn~r>(sJz=w~5Dc@laFzJMrKz;E-;b}gL zL~4QNl{eGV*J&hSya``R!fF+8<%wF7+k4nNqBJ^~7`O8G-_wOB*z?X(Nxvu%7q8=& zz0f#ehvW9QhUxd7$S%-KYNiND|x`b#D1Q_domtEjFq_nswpCYAJ0~1M=!S3#eSJzuC0+K#4f_jkj7Hcsl zVVo|>AN4c1zN6Ns|6ngIQ7M(;F=A?M5`gP!!2N&8!Q64n{6?=JskkZH0zVIa#1i$) zZhSWqFxo|tgnFnQ6V*T(6-Rdutb1%jJ^`I#N6;r)pe`LzU=K@BC$--Tmz z3RHn}U^+$qSN$19oNL5tb%3x7tzy!wJSQh7;t_VmurIE?(!>A&002ovPDHLkV1mH3 B?)d-! literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1329de8c115122908f3336022f7c35127d185e46 GIT binary patch literal 585 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0Z1^9%xUSnYR&%j{1Ni+6}_ohvoUOant^XAR}4931+eIMrV0ww?dXZTbv z0aPCM@iGUH;wuUA3kHfJ0E6p+J12lLoCO|{#XtoIK$tP>S|=w^P@=>&q9iy!t)x7$ zD3u`~F*C13&(AeP!Bo#s&*=9a=50VV+fpMu(>y)37(nK;GDtD9GB5&JUO+4jWrKXD z!N?32X9BVf8JQRafOHfPXSTC|#j}8H5ZGVF$nXLf2rwGWQU;*Z1a<}%ph^QHV*|zo z5K}=mvMzv_GzG{80VbfiOkkBkmKH!3RF|QF0Z5k5w)9dGoF|r!uG432`U{C^R_y^gmc6S!3s!oIlSY zpyS6Wm!qFDs?@!W7dBkKed>1Lha8==F69BQ>+WZ7UD9*xkdH#fTEopYy8ETQjW2RW zX-8ZB`Jbh8#-&L_z+r&{Ln9L-%PD`>ItvcTMtMFbzYn%DEmvfJJe(l>~~iGacXaZ>m4zbE{)PKgj)_u6{1-oD!M<0}rlN literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..afb2afbac9ae5a4f3eab779737f2f48e31beb8ef GIT binary patch literal 574 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0Z1^9%xUSnYR&%j{1Ni+6}_ohvoUOant^XAR}4931+eIMrV0ww?dXZTbv z0aPCM@iGUH;wuUA3kHfJ0E6p+J12lLoCO|{#XtoIL6~vJ#O${~L5ULAh?3y^w370~ zqEv=}#LT=BJwMkF1yemkJ)_@yn70AdY)g&sO!M^AVgQ-X${@wa%D@O@c>%FBlnwHo z1|u_AoC(M_WMpCx0Mb!FoY~F-7S95*LEvT?Bf|?|Ai!ueOBsMt6WAG8fGQ1)j13qU zKuiVM$hrVx(i9*Y1ek#4GJ#bFSy}*DP+f)w1|Zqx8vEA$7Ci_G=eeFPjv*25Z>JsP zV>0A$x!&};to8r@*w$5=aociiXI8Rbn(&hQcw~tXhk}7Z!+{^-2a7UOa^7z_?{p+w zO>3W3TZsM3=?&L!pSrzbf&X^vth?{4*Zz}R;J@3!WyVj-+ESnSdEy;Y*7E$nZg-bO z*ny#uk&%UsML=S|=$x0FinHHx9-FRkUAU(rcayq=k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1^2Ka=ye%a1$o zqB(UsPy@r?tHpB`U~wiO+mMloK>$cc0dZzK3s^i0$OZwPaz=(1z<_|!XqGYnr6#a5umDvW z7#SNdE`XQ{vXONG#H1-eHV7~Q&1C|s46?KUvY@&Q4GciC?_TLj96JLPW?=Z@>Eaj? z;r@2oL7`>^9+y@&m!*IHKR0_Nc3P!v*B}plje~h z9fmb`9&&^WX)1VGK67cA31TbGI5b5g_>u5Q$*s%0TN*UZhh&_)&h?(-amSqm&f}L8 zcbYl06uNL+TkEFORT@3<3s6Gf%38ahFV*LJQ~_n~FF3(#amYf6we)GcN2kAvEs)KU ztJ`WH?p43veTLhT1yhB6*p+q!u$aX>Xp$A*k#L+ftnr#`L0$VLSs$jQ);CzZ$_?@q zb~pPjC}E%N@RH9y$DoXTu|pY<^P11B@sey0(@9^)Z2g8K*B+i?t#1Q`g{P~Z%Q~lo FCII*?(L?|M delta 679 zcmbQqx`lOuWIY=L1H-D!!h1l9v%n*=n1O-s2naJy)#j6CU|>q~ba4!+nDchl{%jFP zk>mThyB|DLl55mhB5->5AvL#koI79Z^ku(uuy)+AZI_ddE5{4_#N`E73te|Tc~zo0 zDJX~|&|tRGPLBwqbF)({FcSIy;#J@eqJ(uTa_E8IQuB)CrmnA~I1NZug1Sm*J1+mJVQ zRTZzU=SuUsEvWB#_x9cF(`g4^Mz9%tV=DJuFFxB(J$b(1J*GaRZJE)xpZs2SDpkSv zVqW@<4k7i&TALs2iLgJNYMh(AF!oH&2Qdf9x|GSSBH%fcCkGXBBR+(MOiv4rH*7t?7{L??UwSIQL z{NMVHV~6jjpWmIiM(Kn2ha=mb)_ne*cJKeD_Y9}zNKf~h^IUp0L&UU$zk6@ot$C{u zzpHOq?%T|shqBdwr0k0W*CAbeTzJbk&@#Kg}o4I4(9TUT)Xt zn&gW!q^C(fkUOUrzQxzPW|O^L{X5Cmj1?RwF1YvL%QrJ6?qdhm{*jQnepq;W z&;biXEMSWuzX?nhuLYY#4n+0~%wafJ#iC@vIOi*eO94o@;{i*-HD!T=oC#~)9XdcF x4F=O4&sDX+#1$-FD*!b+6kJti(QfG8ANxjAvcGCdGcYYOc)I$ztaD0e0syihEIt4L diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/meta.json index bacde82361..4e665a18e8 100644 --- a/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Coats/clownpriest.rsi/meta.json @@ -1,26 +1,26 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from Yogstation at https://github.com/yogstation13/Yogstation/commit/18ab2203bc47b7590f2c72b5f7969eafa723f033", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from /tg/station 13 at commit https://github.com/tgstation/tgstation/commit/3dbc8c3c121e3ade9158303c2819b47206b0f93d. inhand-left modified by K-Dynamic (github).", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-OUTERCLOTHING", - "directions": 4 - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] -} \ No newline at end of file + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-OUTERCLOTHING", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} From 0eadb5086783ec79d3ae287bf13380a419f13664 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 5 Apr 2026 10:18:28 +0000 Subject: [PATCH 092/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f493a253d5..05531749e5 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4036,3 +4028,12 @@ 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 From 666032f88a295d543bf2d9eed1fb070b737575dc Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Sun, 5 Apr 2026 03:25:53 -0700 Subject: [PATCH 093/247] Atmospherics GetBulkTileAtmosphere/GasMixturePressures API (#42745) --- .../EntitySystems/AtmosphereSystem.API.cs | 90 +++++++++++++++++++ .../AtmosphereSystem.DeltaPressure.cs | 65 -------------- 2 files changed, 90 insertions(+), 65 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index e68bf7b2ec..8c3681d543 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; @@ -289,6 +290,95 @@ public partial class AtmosphereSystem GetTileMixture(entity, excite)?.AdjustMoles(gas, mols); } + /// + /// Retrieves the pressures of all gas mixtures + /// in the given array of s, and stores the results in the + /// provided span. + /// + /// The tiles span to find the pressures of. + /// The span to store the pressures to - this should be the same length + /// as the tile array. + /// Thrown when the length of the provided spans do not match. + /// Note that for or s that are null, + /// this method will return a value close to zero but not exactly zero. + [PublicAPI] + public static void GetBulkTileAtmospherePressures(Span tiles, Span pressures) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(tiles.Length, pressures.Length); + + var len = tiles.Length; + var arr1 = ArrayPool.Shared.Rent(len); + + try + { + var mixtSpan = arr1.AsSpan(0, len); + for (var i = 0; i < tiles.Length; i++) + { + mixtSpan[i] = tiles[i]?.Air; + } + + GetBulkGasMixturePressures(mixtSpan, pressures); + } + finally + { + ArrayPool.Shared.Return(arr1); + } + } + + /// + /// Gets the pressures of a of s. + /// + /// The to get the pressures of. + /// The to store the pressures to - this should be the same length + /// as the mixtures array. + /// Thrown when the length of the provided spans do not match. + /// Note that for GasMixtures that are null, this method will return a value close to zero but not exactly zero. + [PublicAPI] + public static void GetBulkGasMixturePressures(Span mixtures, Span pressures) + { + ArgumentOutOfRangeException.ThrowIfNotEqual(mixtures.Length, pressures.Length); + + var len = mixtures.Length; + + var arr1 = ArrayPool.Shared.Rent(len); + var arr2 = ArrayPool.Shared.Rent(len); + var arr3 = ArrayPool.Shared.Rent(len); + try + { + var mixtVol = arr1.AsSpan(0, len); + var mixtTemp = arr2.AsSpan(0, len); + var mixtMoles = arr3.AsSpan(0, len); + + for (var i = 0; i < len; i++) + { + if (mixtures[i] is not { } mixture) + { + // To prevent any NaN/Div/0 errors, we just bite the bullet + // and set everything to the lowest possible value. + mixtVol[i] = 1; + mixtTemp[i] = 1; + mixtMoles[i] = float.Epsilon; + continue; + } + + mixtVol[i] = mixture.Volume; + mixtTemp[i] = mixture.Temperature; + mixtMoles[i] = mixture.TotalMoles; + } + + // TODO NumericsHelpers need a method that substitutes NaNs with zeros. AVX-512 has one iirc but for 256/128 we need to do some masking bs + NumericsHelpers.Multiply(mixtMoles, Atmospherics.R); + NumericsHelpers.Multiply(mixtMoles, mixtTemp); + NumericsHelpers.Divide(mixtMoles, mixtVol, pressures); + } + finally + { + ArrayPool.Shared.Return(arr1); + ArrayPool.Shared.Return(arr2); + ArrayPool.Shared.Return(arr3); + } + } + /// /// Triggers a tile's to react. /// diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs index 9457c5689c..59ccbbb8c8 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.DeltaPressure.cs @@ -200,71 +200,6 @@ public sealed partial class AtmosphereSystem } } - /// - /// A DeltaPressure helper method that retrieves the pressures of all gas mixtures - /// in the given array of s, and stores the results in the - /// provided span. - /// - /// The tiles span to find the pressures of. - /// The span to store the pressures to - this should be the same length - /// as the tile array. - /// Thrown when the length of the provided spans do not match. - private static void GetBulkTileAtmospherePressures(Span tiles, Span pressures) - { - // this shit is internal because I don't even trust myself - if (tiles.Length != pressures.Length) - throw new ArgumentException("Length of Tiles and Pressures span must be the same!"); - - var len = pressures.Length; - - // Once again, ArrayPool might return arrays that are longer than the length. - // We really need them to be all the same length, so slice them here. - var arr1 = ArrayPool.Shared.Rent(len); - var arr2 = ArrayPool.Shared.Rent(len); - var arr3 = ArrayPool.Shared.Rent(len); - - var mixtVol = arr1.AsSpan(0, len); - var mixtTemp = arr2.AsSpan(0, len); - var mixtMoles = arr3.AsSpan(0, len); - - try - { - for (var i = 0; i < len; i++) - { - if (tiles[i] is not { Air: { } mixture }) - { - // To prevent any NaN/Div/0 errors, we just bite the bullet - // and set everything to the lowest possible value. - mixtVol[i] = 1; - mixtTemp[i] = 1; - mixtMoles[i] = float.Epsilon; - continue; - } - - mixtVol[i] = mixture.Volume; - mixtTemp[i] = mixture.Temperature; - mixtMoles[i] = mixture.TotalMoles; - } - - /* - Retrieval of single tile pressures requires calling a get method for each tile, - which does a bunch of scalar operations. - - So we go ahead and batch-retrieve the pressures of all tiles - and process them in bulk. - */ - NumericsHelpers.Multiply(mixtMoles, Atmospherics.R); - NumericsHelpers.Multiply(mixtMoles, mixtTemp); - NumericsHelpers.Divide(mixtMoles, mixtVol, pressures); - } - finally - { - ArrayPool.Shared.Return(arr1); - ArrayPool.Shared.Return(arr2); - ArrayPool.Shared.Return(arr3); - } - } - /// /// Packs data into a data struct and enqueues it /// into the queue for From 974eb80ff40e5ef05ca955aaababf0c746fad1fa Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Sun, 5 Apr 2026 05:17:08 -0700 Subject: [PATCH 094/247] Fix LINDA pressure delta nonsense and weighting (#43471) * Fix LINDA equalization * Apply suggestion from @slarticodefast --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../EntitySystems/AtmosphereSystem.LINDA.cs | 65 +++++++++++++++---- Content.Shared/Atmos/Atmospherics.cs | 13 +++- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs index 7ece546e4c..d4a70dfbc0 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -117,8 +117,11 @@ namespace Content.Server.Atmos.EntitySystems private void Archive(TileAtmosphere tile, int fireCount) { if (tile.Air != null) + { + // TODO ATMOS: This is an extremely large hotspot in LINDA, accounting for 1/5th of its time. + // Please make GasMixture a struct or use a FauxGasMixture with an InlineArray to handle copying this sanely. tile.AirArchived = new GasMixture(tile.Air); - + } tile.ArchivedCycle = fireCount; } @@ -197,12 +200,25 @@ namespace Content.Server.Atmos.EntitySystems } /// - /// Shares gas between two tiles. Part of LINDA. + /// Performs a share operation between two tiles, sharing both physical gas and temperature. /// + /// The receiving the share. + /// The sharing its air. + /// The number of s next to the receiver that air can flow to. + /// The pressure difference between the two tiles after sharing. + /// LINDA is an FEA-like solver and this method is basically the core of it. + /// In FEA we divide the problem into infinitesimal parts and try to step towards the desired end state: + /// a steady state where all air is equalized between tiles. + /// To do this we share the tiles air between other tiles over time (as well as the temperature). + /// Note that the timestep is actually a cyclestep, so running the cycles faster leads to a faster equalization. + /// Hilarious, I know. public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int atmosAdjacentTurfs) { - if (tileReceiver.Air is not {} receiver || tileSharer.Air is not {} sharer || - tileReceiver.AirArchived == null || tileSharer.AirArchived == null) + // TODO ATMOS: Method needs to timestep over deltaTime instead of per cycle + // TODO ATMOS: Method needs to account for adjacent turfs in the situation where air is moving from receiver to sharer. + // See https://github.com/tgstation/tgstation/pull/63785 + if (tileReceiver.Air is not { } receiver || tileSharer.Air is not { } sharer || + tileReceiver.AirArchived == null || tileSharer.AirArchived == null) return 0f; var temperatureDelta = tileReceiver.AirArchived.Temperature - tileSharer.AirArchived.Temperature; @@ -221,12 +237,13 @@ namespace Content.Server.Atmos.EntitySystems var movedMoles = 0f; var absMovedMoles = 0f; - for(var i = 0; i < Atmospherics.TotalNumberOfGases; i++) + for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) { var thisValue = receiver.Moles[i]; var sharerValue = sharer.Moles[i]; var delta = (thisValue - sharerValue) / (atmosAdjacentTurfs + 1); - if (!(MathF.Abs(delta) >= Atmospherics.GasMinMoles)) continue; + if (!(MathF.Abs(delta) >= Atmospherics.GasMinMoles)) + continue; if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider) { var gasHeatCapacity = delta * GasMolarHeatCapacities[i]; @@ -240,8 +257,10 @@ namespace Content.Server.Atmos.EntitySystems } } - if (!receiver.Immutable) receiver.Moles[i] -= delta; - if (!sharer.Immutable) sharer.Moles[i] += delta; + if (!receiver.Immutable) + receiver.Moles[i] -= delta; + if (!sharer.Immutable) + sharer.Moles[i] += delta; movedMoles += delta; absMovedMoles += MathF.Abs(delta); } @@ -256,16 +275,21 @@ namespace Content.Server.Atmos.EntitySystems // Transfer of thermal energy (via changed heat capacity) between self and sharer. if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity) { - receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * tileReceiver.AirArchived.Temperature) + (heatCapacitySharerToThis * tileSharer.AirArchived.Temperature)) / newHeatCapacity; + receiver.Temperature = + (oldHeatCapacity * receiver.Temperature - + heatCapacityToSharer * tileReceiver.AirArchived.Temperature + + heatCapacitySharerToThis * tileSharer.AirArchived.Temperature) / newHeatCapacity; } if (!sharer.Immutable && newSharerHeatCapacity > Atmospherics.MinimumHeatCapacity) { - sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * tileSharer.AirArchived.Temperature) + (heatCapacityToSharer * tileReceiver.AirArchived.Temperature)) / newSharerHeatCapacity; + sharer.Temperature = + (oldSharerHeatCapacity * sharer.Temperature - + heatCapacitySharerToThis * tileSharer.AirArchived.Temperature + + heatCapacityToSharer * tileReceiver.AirArchived.Temperature) / newSharerHeatCapacity; } // Thermal energy of the system (self and sharer) is unchanged. - if (MathF.Abs(oldSharerHeatCapacity) > Atmospherics.MinimumHeatCapacity) { if (MathF.Abs(newSharerHeatCapacity / oldSharerHeatCapacity - 1) < 0.1) @@ -275,12 +299,25 @@ namespace Content.Server.Atmos.EntitySystems } } - if (!(temperatureDelta > Atmospherics.MinimumTemperatureToMove) && - !(MathF.Abs(movedMoles) > Atmospherics.MinimumMolesDeltaToMove)) return 0f; + // If we didn't move enough air or if the temperature difference is too small, + // we don't consider there to be a pressure difference. + // TODO ATMOS: This is a very weird early return, please figure out why this exists because this logic seems to be double checked + // in a lot of other places (ex. HighPressureDelta). + if (!(absTemperatureDelta > Atmospherics.MinimumTemperatureToMove) && + !(MathF.Abs(movedMoles) > Atmospherics.MinimumMolesDeltaToMove)) + return 0f; + var moles = receiver.TotalMoles; var theirMoles = sharer.TotalMoles; - return (tileReceiver.AirArchived.Temperature * (moles + movedMoles)) - (tileSharer.AirArchived.Temperature * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume; + /* + To get the pressure delta: + PV = nRT + P = nRT / V + \Delta P = ((n_1 * T_1) - (n_2 * T_2)) * R / V + */ + return (tileReceiver.AirArchived.Temperature * (moles + movedMoles) - + tileSharer.AirArchived.Temperature * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume; } /// diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs index 02d4a7deb8..12af967526 100644 --- a/Content.Shared/Atmos/Atmospherics.cs +++ b/Content.Shared/Atmos/Atmospherics.cs @@ -138,8 +138,19 @@ namespace Content.Shared.Atmos /// public const float MinimumAirToSuspend = (MolesCellStandard * MinimumAirRatioToSuspend); - public const float MinimumTemperatureToMove = (T20C + 100f); + /// + /// The minimum difference in temperature between s + /// (s) required + /// for LINDA to report a pressure difference between them for space wind. + /// In Kelvin. + /// + public const float MinimumTemperatureToMove = 5f; + /// + /// The minimum difference in moles between s + /// (s) required for LINDA to + /// report a pressure difference between them for space wind. + /// public const float MinimumMolesDeltaToMove = (MolesCellStandard * MinimumAirRatioToMove); /// From 9ad745045be6b12d35251c27d92809bfa4e37e09 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sun, 5 Apr 2026 10:01:01 -0400 Subject: [PATCH 095/247] Fix `IdCardConsoleSystem` missing prototype error (#42884) * Fix IdCardConsoleSystem missing prototype error * Resolve -> TryIndex * Poke --- Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs | 2 +- Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs | 2 +- Content.Server/Access/Systems/IdCardConsoleSystem.cs | 4 ++-- Content.Shared/Access/Components/IdCardConsoleComponent.cs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs index 81b1c087d8..31f65dc78d 100644 --- a/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs +++ b/Content.Client/Access/UI/IdCardConsoleBoundUserInterface.cs @@ -65,7 +65,7 @@ namespace Content.Client.Access.UI _window?.UpdateState(castState); } - public void SubmitData(string newFullName, string newJobTitle, List> newAccessList, ProtoId newJobPrototype) + public void SubmitData(string newFullName, string newJobTitle, List> newAccessList, ProtoId? newJobPrototype) { SendMessage(new WriteToTargetIdMessage( newFullName, diff --git a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs index bb44ae2615..2169277f0d 100644 --- a/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs +++ b/Content.Client/Access/UI/IdCardConsoleWindow.xaml.cs @@ -216,7 +216,7 @@ namespace Content.Client.Access.UI JobTitleLineEdit.Text, // Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair _accessButtons.ButtonsList.Where(x => x.Value.Pressed).Select(x => x.Key).ToList(), - jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : string.Empty); + jobProtoDirty ? _jobPrototypeIds[JobPresetOptionButton.SelectedId] : null); } } } diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index 0ea55eee91..1e4a044901 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -135,7 +135,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem string newFullName, string newJobTitle, List> newAccessList, - ProtoId newJobProto, + ProtoId? newJobProto, EntityUid player, IdCardConsoleComponent? component = null) { @@ -158,7 +158,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem _idCard.TryChangeFullName(targetId, newFullName, player: player); _idCard.TryChangeJobTitle(targetId, newJobTitle, player: player); - if (_prototype.Resolve(newJobProto, out var job) + if (_prototype.TryIndex(newJobProto, out var job) && _prototype.Resolve(job.Icon, out var jobIcon)) { _idCard.TryChangeJobIcon(targetId, jobIcon, player: player); diff --git a/Content.Shared/Access/Components/IdCardConsoleComponent.cs b/Content.Shared/Access/Components/IdCardConsoleComponent.cs index 35af5972c3..89c24940ba 100644 --- a/Content.Shared/Access/Components/IdCardConsoleComponent.cs +++ b/Content.Shared/Access/Components/IdCardConsoleComponent.cs @@ -26,9 +26,9 @@ public sealed partial class IdCardConsoleComponent : Component public readonly string FullName; public readonly string JobTitle; public readonly List> AccessList; - public readonly ProtoId JobPrototype; + public readonly ProtoId? JobPrototype; - public WriteToTargetIdMessage(string fullName, string jobTitle, List> accessList, ProtoId jobPrototype) + public WriteToTargetIdMessage(string fullName, string jobTitle, List> accessList, ProtoId? jobPrototype) { FullName = fullName; JobTitle = jobTitle; From 4a88b375df99a884df0ce5053efcfa8d1124a123 Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sun, 5 Apr 2026 17:03:29 +0200 Subject: [PATCH 096/247] Make cameras react to the AI eye nearby (#43466) * init TODO: Make this part of the surveillance camera system Make this be bearable when unpowered(Probably commented out until we make AI lose vision without power) Remove leftover shit Probably clean up the camera RSI * move to surv cameras, unpowered, remove leftovers * silly * review * review * I can see the light * reverse fixtures * review * dependency query * unused oops --- .../SurveillanceCameraMicrophoneSystem.cs | 4 +- .../SurveillanceCameraSystem.Collide.cs | 101 ++++++++++++++++++ .../Systems/SurveillanceCameraSystem.cs | 34 ++++-- .../LightOnCollideColliderComponent.cs | 13 --- .../Components/LightOnCollideComponent.cs | 11 -- .../Light/EntitySystems/LightCollideSystem.cs | 87 --------------- .../CameraActiveOnCollideColliderComponent.cs | 16 +++ .../CameraActiveOnCollideComponent.cs | 22 ++++ .../Components/SurveillanceCameraComponent.cs | 25 +++-- .../SharedSurveillanceCameraSystem.cs | 10 +- Resources/Prototypes/Actions/station_ai.yml | 28 ----- .../Entities/Mobs/Player/silicon.yml | 12 ++- .../WallmountMachines/surveillance_camera.yml | 5 +- 13 files changed, 207 insertions(+), 161 deletions(-) create mode 100644 Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.Collide.cs delete mode 100644 Content.Shared/Light/Components/LightOnCollideColliderComponent.cs delete mode 100644 Content.Shared/Light/Components/LightOnCollideComponent.cs delete mode 100644 Content.Shared/Light/EntitySystems/LightCollideSystem.cs create mode 100644 Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideColliderComponent.cs create mode 100644 Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideComponent.cs diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs index e8c53de9eb..723ddbf0ff 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs @@ -30,7 +30,7 @@ public sealed class SurveillanceCameraMicrophoneSystem : EntitySystem // This function ensures that chat popups appear on camera views that have connected microphones. foreach (var (_, __, camera, xform) in EntityQuery()) { - if (camera.ActiveViewers.Count == 0) + if (camera.ActivePvsViewers.Count == 0) continue; // get range to camera. This way wispers will still appear as obfuscated if they are too far from the camera's microphone @@ -41,7 +41,7 @@ public sealed class SurveillanceCameraMicrophoneSystem : EntitySystem if (range < 0 || range > ev.VoiceRange) continue; - foreach (var viewer in camera.ActiveViewers) + foreach (var viewer in camera.ActivePvsViewers) { // if the player has not already received the chat message, send it to them but don't log it to the chat // window. This is simply so that it appears in camera. diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.Collide.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.Collide.cs new file mode 100644 index 0000000000..4015a65cc9 --- /dev/null +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.Collide.cs @@ -0,0 +1,101 @@ +using Content.Shared.Power.EntitySystems; +using Content.Shared.SurveillanceCamera; +using Content.Shared.SurveillanceCamera.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; + +namespace Content.Server.SurveillanceCamera; + +public partial class SurveillanceCameraSystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] private readonly EntityQuery _cameraQuery = default!; + + public void InitializeCollide() + { + SubscribeLocalEvent(OnPreventCollide); + SubscribeLocalEvent(OnStart); + SubscribeLocalEvent(OnEnd); + + SubscribeLocalEvent(OnCollideShutdown); + SubscribeLocalEvent(OnOverrideState); + } + + private void OnCollideShutdown(Entity ent, ref ComponentShutdown args) + { + // TODO: Check this on the event. + if (TerminatingOrDeleted(ent.Owner)) + return; + + // Regenerate contacts for everything we were colliding with. + var contacts = _physics.GetContacts(ent.Owner); + + while (contacts.MoveNext(out var contact)) + { + if (!contact.IsTouching) + continue; + + var other = contact.OtherEnt(ent.Owner); + + if (_cameraQuery.HasComp(other)) + { + _physics.RegenerateContacts(other); + } + } + } + + // You may be wondering what de fok this is doing here. + // At the moment there's no easy way to do collision whitelists based on components. + private void OnPreventCollide(Entity ent, ref PreventCollideEvent args) + { + if (!_cameraQuery.HasComp(args.OtherEntity)) + { + args.Cancelled = true; + } + } + + private void OnEnd(Entity ent, ref EndCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + if (!_cameraQuery.TryComp(args.OtherEntity, out var cameraCollider)) + return; + + // TODO: Engine bug IsTouching box2d yay. + var contacts = _physics.GetTouchingContacts(args.OtherEntity) - 1; + + if (contacts > 0) + return; + + cameraCollider.Enabled = false; + Dirty(args.OtherEntity, cameraCollider); + UpdateVisuals(args.OtherEntity); + } + + private void OnStart(Entity ent, ref StartCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + if (!_cameraQuery.TryComp(args.OtherEntity, out var cameraCollider)) + return; + + cameraCollider.Enabled = true; + Dirty(args.OtherEntity, cameraCollider); + UpdateVisuals(args.OtherEntity); + } + + private void OnOverrideState(Entity ent, ref SurveillanceCameraGetIsViewedExternallyEvent args) + { + if (ent.Comp.RequiresPower && !_power.IsPowered(ent.Owner)) + return; + + if (!ent.Comp.Enabled) + return; + + args.Viewed = true; + } +} + diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs index cdd032c79c..09c2b39447 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs @@ -13,15 +13,15 @@ using Content.Shared.DeviceNetwork.Components; namespace Content.Server.SurveillanceCamera; -public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem +public sealed partial class SurveillanceCameraSystem : SharedSurveillanceCameraSystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly ViewSubscriberSystem _viewSubscriberSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterface = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly SurveillanceCameraMapSystem _cameraMapSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; // Pings a surveillance camera subnet. All cameras will always respond // with a data message if they are on the same subnet. @@ -61,6 +61,8 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem SubscribeLocalEvent(OnPacketReceived); SubscribeLocalEvent(OnSetName); SubscribeLocalEvent(OnSetNetwork); + + InitializeCollide(); } private void OnPacketReceived(EntityUid uid, SurveillanceCameraComponent component, DeviceNetworkPacketEvent args) @@ -232,7 +234,7 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem var ev = new SurveillanceCameraDeactivateEvent(camera); - RemoveActiveViewers(camera, new(component.ActiveViewers), null, component); + RemoveActiveViewers(camera, new(component.ActivePvsViewers), null, component); component.Active = false; // Send a targetted event to all monitors. @@ -249,6 +251,25 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem UpdateVisuals(camera, component); } + /// + /// Checks whether the camera is being viewed through by anyone at all. + /// + /// The camera to check + /// True if the camera is looked through, otherwise False. + public bool IsGettingViewed(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + if (ent.Comp.ActivePvsViewers.Count > 0 || ent.Comp.ActiveMonitors.Count > 0) + return true; + + var ev = new SurveillanceCameraGetIsViewedExternallyEvent(); + RaiseLocalEvent(ent, ref ev); + + return ev.Viewed; + } + public override void SetActive(EntityUid camera, bool setting, SurveillanceCameraComponent? component = null) { if (!Resolve(camera, ref component)) @@ -284,7 +305,8 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem } _viewSubscriberSystem.AddViewSubscriber(camera, actor.PlayerSession); - component.ActiveViewers.Add(player); + + component.ActivePvsViewers.Add(player); if (monitor != null) { @@ -346,7 +368,7 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem if (Resolve(player, ref actor)) _viewSubscriberSystem.RemoveViewSubscriber(camera, actor.PlayerSession); - component.ActiveViewers.Remove(player); + component.ActivePvsViewers.Remove(player); if (monitor != null) { @@ -391,7 +413,7 @@ public sealed class SurveillanceCameraSystem : SharedSurveillanceCameraSystem key = SurveillanceCameraVisuals.Active; } - if (component.ActiveViewers.Count > 0 || component.ActiveMonitors.Count > 0) + if (IsGettingViewed((uid, component))) { key = SurveillanceCameraVisuals.InUse; } diff --git a/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs b/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs deleted file mode 100644 index 39be05a148..0000000000 --- a/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Light.Components; - -/// -/// Can activate when collided with. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class LightOnCollideColliderComponent : Component -{ - [DataField] - public string FixtureId = "lightTrigger"; -} diff --git a/Content.Shared/Light/Components/LightOnCollideComponent.cs b/Content.Shared/Light/Components/LightOnCollideComponent.cs deleted file mode 100644 index c3b4bd7396..0000000000 --- a/Content.Shared/Light/Components/LightOnCollideComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Light.Components; - -/// -/// Enables / disables pointlight whenever entities are contacting with it -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class LightOnCollideComponent : Component -{ -} diff --git a/Content.Shared/Light/EntitySystems/LightCollideSystem.cs b/Content.Shared/Light/EntitySystems/LightCollideSystem.cs deleted file mode 100644 index 2de7c5591f..0000000000 --- a/Content.Shared/Light/EntitySystems/LightCollideSystem.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Content.Shared.Light.Components; -using Robust.Shared.Physics.Events; -using Robust.Shared.Physics.Systems; - -namespace Content.Shared.Light.EntitySystems; - -public sealed class LightCollideSystem : EntitySystem -{ - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SlimPoweredLightSystem _lights = default!; - - private EntityQuery _lightQuery; - - public override void Initialize() - { - base.Initialize(); - - _lightQuery = GetEntityQuery(); - - SubscribeLocalEvent(OnPreventCollide); - SubscribeLocalEvent(OnStart); - SubscribeLocalEvent(OnEnd); - - SubscribeLocalEvent(OnCollideShutdown); - } - - private void OnCollideShutdown(Entity ent, ref ComponentShutdown args) - { - // TODO: Check this on the event. - if (TerminatingOrDeleted(ent.Owner)) - return; - - // Regenerate contacts for everything we were colliding with. - var contacts = _physics.GetContacts(ent.Owner); - - while (contacts.MoveNext(out var contact)) - { - if (!contact.IsTouching) - continue; - - var other = contact.OtherEnt(ent.Owner); - - if (_lightQuery.HasComp(other)) - { - _physics.RegenerateContacts(other); - } - } - } - - // You may be wondering what de fok this is doing here. - // At the moment there's no easy way to do collision whitelists based on components. - private void OnPreventCollide(Entity ent, ref PreventCollideEvent args) - { - if (!_lightQuery.HasComp(args.OtherEntity)) - { - args.Cancelled = true; - } - } - - private void OnEnd(Entity ent, ref EndCollideEvent args) - { - if (args.OurFixtureId != ent.Comp.FixtureId) - return; - - if (!_lightQuery.HasComp(args.OtherEntity)) - return; - - // TODO: Engine bug IsTouching box2d yay. - var contacts = _physics.GetTouchingContacts(args.OtherEntity) - 1; - - if (contacts > 0) - return; - - _lights.SetEnabled(args.OtherEntity, false); - } - - private void OnStart(Entity ent, ref StartCollideEvent args) - { - if (args.OurFixtureId != ent.Comp.FixtureId) - return; - - if (!_lightQuery.HasComp(args.OtherEntity)) - return; - - _lights.SetEnabled(args.OtherEntity, true); - } -} diff --git a/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideColliderComponent.cs b/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideColliderComponent.cs new file mode 100644 index 0000000000..f92f395eb9 --- /dev/null +++ b/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideColliderComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.SurveillanceCamera.Components; + +/// +/// Can activate when collided with. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class CameraActiveOnCollideColliderComponent : Component +{ + /// + /// The fixture id used for detecting the collision. + /// + [DataField] + public string FixtureId = "lightTrigger"; +} diff --git a/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideComponent.cs b/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideComponent.cs new file mode 100644 index 0000000000..27c4bf7c09 --- /dev/null +++ b/Content.Shared/SurveillanceCamera/Components/CameraActiveOnCollideComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.SurveillanceCamera.Components; + +/// +/// Marks an entity with whenever entities are contacting with it. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class CameraActiveOnCollideComponent : Component +{ + /// + /// Whether this camera is currently being collided with. + /// + [DataField, AutoNetworkedField] + public bool Enabled; + + /// + /// Whether the entity must be powered for this component to work. + /// + [DataField, AutoNetworkedField] + public bool RequiresPower = true; +} diff --git a/Content.Shared/SurveillanceCamera/Components/SurveillanceCameraComponent.cs b/Content.Shared/SurveillanceCamera/Components/SurveillanceCameraComponent.cs index 21381e753f..914ba4a63f 100644 --- a/Content.Shared/SurveillanceCamera/Components/SurveillanceCameraComponent.cs +++ b/Content.Shared/SurveillanceCamera/Components/SurveillanceCameraComponent.cs @@ -8,18 +8,23 @@ namespace Content.Shared.SurveillanceCamera.Components; [Access(typeof(SharedSurveillanceCameraSystem))] public sealed partial class SurveillanceCameraComponent : Component { - // List of active viewers. This is for bookkeeping purposes, - // so that when a camera shuts down, any entity viewing it - // will immediately have their subscription revoked. + /// + /// List of active viewers who have a PVS view subscription on this camera. + /// This is for bookkeeping purposes, + /// so that when a camera shuts down, any entity viewing it + /// will immediately have their subscription revoked. + /// [ViewVariables] - public HashSet ActiveViewers { get; } = new(); + public HashSet ActivePvsViewers { get; } = new(); - // Monitors != Viewers, as viewers are entities that are tied - // to a player session that's viewing from this camera - // - // Monitors are grouped sets of viewers, and may be - // completely different monitor types (e.g., monitor console, - // AI, etc.) + /// + /// Monitors != Viewers, as viewers are entities that are tied + /// to a player session that's viewing from this camera + /// + /// Monitors are grouped sets of viewers, and may be + /// completely different monitor types (e.g., monitor console, + /// AI, etc.) + /// [ViewVariables] public HashSet ActiveMonitors { get; } = new(); diff --git a/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs b/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs index b6743669e9..1be49f8fc8 100644 --- a/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs +++ b/Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs @@ -5,7 +5,7 @@ using Robust.Shared.Serialization; namespace Content.Shared.SurveillanceCamera; -public abstract class SharedSurveillanceCameraSystem : EntitySystem +public abstract partial class SharedSurveillanceCameraSystem : EntitySystem { public override void Initialize() { @@ -68,3 +68,11 @@ public enum SurveillanceCameraVisuals : byte Xray, Emp } + +/// +/// Raised on a camera entity to find whether it is externally viewed by some entity. +/// This does not use the actual viewers or monitors camera has and is simply used to see whether the camera is "technically" +/// being looked through by somebody, such as the Station AI. +/// +[ByRefEvent] +public record struct SurveillanceCameraGetIsViewedExternallyEvent(bool Viewed = false); diff --git a/Resources/Prototypes/Actions/station_ai.yml b/Resources/Prototypes/Actions/station_ai.yml index 4dbaf07aab..9e9bc9f650 100644 --- a/Resources/Prototypes/Actions/station_ai.yml +++ b/Resources/Prototypes/Actions/station_ai.yml @@ -14,34 +14,6 @@ - type: InstantAction event: !type:JumpToCoreEvent -- type: entity - parent: BaseAction - id: ActionSurvCameraLights - name: Toggle camera lights - description: Enable surveillance camera lights near wherever you're viewing. - components: - - type: Action - priority: -5 - itemIconStyle: BigAction - icon: - sprite: Interface/Actions/actions_ai.rsi - state: camera_light - - type: InstantAction - event: !type:RelayedActionComponentChangeEvent - components: - - type: LightOnCollideCollider - - type: FixturesChange - fixtures: - lightTrigger: - shape: - !type:PhysShapeCircle - radius: 0.35 - density: 80 - hard: false - layer: - - GhostImpassable - - - type: entity parent: BaseMentalAction id: ActionAIViewLaws diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index 5e06100098..7a6e51cb48 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: @@ -487,6 +486,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: @@ -506,6 +515,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/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml index 561991cd26..a12bca758d 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/surveillance_camera.yml @@ -11,7 +11,7 @@ light: shape: !type:PhysShapeCircle - radius: 5 + radius: 0.5 hard: false mask: - GhostImpassable @@ -24,7 +24,8 @@ - None layer: - TabletopMachineLayer - - type: LightOnCollide + - type: CameraActiveOnCollide + requiresPower: false # TODO: Change this when AI can no longer see via unpowered cameras - type: PointLight enabled: false radius: 5 From d904647eb37fc763837f47f232c172264e53a6fd Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 5 Apr 2026 15:18:30 +0000 Subject: [PATCH 097/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 05531749e5..9f703312dd 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4037,3 +4030,12 @@ 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 From bed228f37f8172e680104705f80a5bf0ce12f6bd Mon Sep 17 00:00:00 2001 From: salarua Date: Sun, 5 Apr 2026 09:55:36 -0700 Subject: [PATCH 098/247] Fix radio quotes (#43483) Change radio straight quotes to curly quotes --- Resources/Locale/en-US/headset/headset-component.ftl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Locale/en-US/headset/headset-component.ftl b/Resources/Locale/en-US/headset/headset-component.ftl index d61fb8edb2..f9f9c17411 100644 --- a/Resources/Locale/en-US/headset/headset-component.ftl +++ b/Resources/Locale/en-US/headset/headset-component.ftl @@ -1,6 +1,6 @@ # Chat window radio wrap (prefix and postfix) -chat-radio-message-wrap = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}]"{$message}"[/font][/color] -chat-radio-message-wrap-bold = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}][bold]"{$message}"[/bold][/font][/color] +chat-radio-message-wrap = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}]“{$message}”[/font][/color] +chat-radio-message-wrap-bold = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}][bold]“{$message}”[/bold][/font][/color] examine-headset-default-channel = Use {$prefix} for the default channel ([color={$color}]{$channel}[/color]). From fbe66e6557eba1378c35a8f977d544f053fa3599 Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:16:00 +0200 Subject: [PATCH 099/247] Prevent changelings from repeated devourings take 2 (#43463) * init * review * check on ling instead --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../ChangelingTransformBoundUserInterface.cs | 3 +- .../Components/ChangelingDevouredComponent.cs | 18 ++ .../Components/ChangelingIdentityComponent.cs | 7 +- .../Systems/ChangelingDevourSystem.cs | 179 +++++++++++------- .../Systems/ChangelingTransformSystem.cs | 3 +- .../Systems/SharedChangelingIdentitySystem.cs | 56 +++++- .../Locale/en-US/changeling/changeling.ftl | 2 + 7 files changed, 184 insertions(+), 84 deletions(-) create mode 100644 Content.Shared/Changeling/Components/ChangelingDevouredComponent.cs diff --git a/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs b/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs index 64d809c0c5..dc40147220 100644 --- a/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs +++ b/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs @@ -23,7 +23,6 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne _menu.OpenOverMouseScreenPosition(); } - public override void Update() { if (_menu == null) @@ -32,7 +31,7 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne if (!EntMan.TryGetComponent(Owner, out var lingIdentity)) return; - var models = ConvertToButtons(lingIdentity.ConsumedIdentities, lingIdentity?.CurrentIdentity); + var models = ConvertToButtons(lingIdentity.ConsumedIdentities.Keys, lingIdentity?.CurrentIdentity); _menu.SetButtons(models); } diff --git a/Content.Shared/Changeling/Components/ChangelingDevouredComponent.cs b/Content.Shared/Changeling/Components/ChangelingDevouredComponent.cs new file mode 100644 index 0000000000..c235ea071a --- /dev/null +++ b/Content.Shared/Changeling/Components/ChangelingDevouredComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Changeling.Components; + +/// +/// Component used for marking entities devoured by a changeling. +/// Used to prevent granting the identity several times. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ChangelingDevouredComponent : Component +{ + /// + /// HashSet of all changelings that have devoured this entity. + /// + // TODO: This should be using some sort of relation system in the future. + [DataField, AutoNetworkedField] + public HashSet DevouredBy = new(); +} diff --git a/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs b/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs index 8e74f83537..fa667f4bf2 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. diff --git a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs index d08e6315ce..b9bdfa29c7 100644 --- a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs @@ -59,29 +59,6 @@ public sealed class ChangelingDevourSystem : EntitySystem } } - /// - /// 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) @@ -94,33 +71,13 @@ public sealed class ChangelingDevourSystem : EntitySystem args.Handled = true; var target = args.Target; - if (target == ent.Owner) - return; // don't eat yourself - - if (!_mobState.IsDead(target)) - { - _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-not-dead"), args.Performer, args.Performer, PopupType.Medium); + if (!CanDevour(ent.AsNullable(), target)) return; - } - - if (HasComp(target)) - { - _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-rotting"), args.Performer, args.Performer, PopupType.Medium); - return; - } - - if (IsTargetProtected(target, ent)) - { - _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-protected"), ent, ent, PopupType.Medium); - return; - } if (_net.IsServer) { ent.Comp.CurrentDevourSound = _audio.Stop(ent.Comp.CurrentDevourSound); - var pvsSound = _audio.PlayPvs(ent.Comp.DevourWindupNoise, ent); - if (pvsSound != null) - ent.Comp.CurrentDevourSound = pvsSound.Value.Entity; + 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}"); @@ -162,17 +119,12 @@ public sealed class ChangelingDevourSystem : EntitySystem _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; _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} began to devour {ToPrettyString(target):player}'s identity"); @@ -203,37 +155,120 @@ public sealed class ChangelingDevourSystem : EntitySystem if (args.Target is not { } target) return; + // Damage first before the CanDevour check to make sure they don't gib in-between and to kill them again in case they somehow revived. _damageable.ChangeDamage(target, ent.Comp.DevourDamage, true, true, ent.Owner); - if (!_mobState.IsDead(target)) - { - _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-not-dead"), args.User, args.User, PopupType.Medium); - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} unsuccessfully devoured {ToPrettyString(args.Target):player}'s identity"); + 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) - && 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"); - _changelingIdentitySystem.CloneToPausedMap((ent, identityStorage), target); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} successfully devoured {ToPrettyString(target):player}'s identity"); - if (_inventorySystem.TryGetSlotEntity(target, "jumpsuit", out var item) - && TryComp(item, out var butcherable)) - RipClothing(target, (item.Value, butcherable)); + if (_inventorySystem.TryGetSlotEntity(target, "jumpsuit", out var item) + && TryComp(item, out var butcherable)) + RipClothing(target, (item.Value, butcherable)); + + 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)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-cannot-devour"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; } - Dirty(ent); + if (HasDevoured(changeling.Owner, victim)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-already-devoured"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + if (!_mobState.IsDead(victim)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-not-dead"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + if (HasComp(victim)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-rotting"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + if (IsTargetProtected(victim, changeling!)) + { + if (showPopup) + _popupSystem.PopupClient(Loc.GetString("changeling-devour-attempt-failed-protected"), changeling.Owner, changeling.Owner, PopupType.Medium); + return false; + } + + return true; + } + + /// + /// Checks if the target's outerclothing is beyond a DamageCoefficientThreshold to protect them from being devoured. + /// + /// The Targeted entity + /// Changelings Devour Component + /// 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; } // TODO: This should just be an API method in the butcher system diff --git a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs index 94d2cd77a8..8bea4dedc6 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs @@ -139,7 +139,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem if (identity.CurrentIdentity == targetIdentity) return; // don't transform into ourselves - if (!identity.ConsumedIdentities.Contains(targetIdentity.Value)) + if (!identity.ConsumedIdentities.ContainsKey(targetIdentity.Value)) return; // this identity does not belong to this player TransformInto(ent.AsNullable(), targetIdentity.Value); @@ -170,6 +170,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem _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)}\""); + _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. diff --git a/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs b/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs index a3b620d68a..12d8f162a9 100644 --- a/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs +++ b/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs @@ -1,4 +1,5 @@ -using System.Numerics; +using System.Linq; +using System.Numerics; using Content.Shared.Body; using Content.Shared.Changeling.Components; using Content.Shared.Cloning; @@ -34,6 +35,8 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); SubscribeLocalEvent(OnStoredRemove); + + SubscribeLocalEvent(OnDevouredShutdown); } private void OnPlayerAttached(Entity ent, ref PlayerAttachedEvent args) @@ -57,7 +60,32 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem { if (TryComp(ent, out var actor)) CleanupPvsOverride(ent, actor.PlayerSession); + CleanupChangelingNullspaceIdentities(ent); + CleanupDevouredReferences(ent); + } + + // Set all references to this entity to null to prevent PVS errors when networking. + private void OnDevouredShutdown(Entity ent, ref ComponentShutdown args) + { + foreach (var ling in ent.Comp.DevouredBy) + { + if (!TryComp(ling, out var identityComp)) + continue; + + var keysToUpdate = identityComp.ConsumedIdentities + .Where(kvp => kvp.Value == ent.Owner) + .Select(kvp => kvp.Key) + .ToList(); + + if (keysToUpdate.Count == 0) + continue; // No need to dirty. + + foreach (var key in keysToUpdate) + identityComp.ConsumedIdentities[key] = null; + + Dirty(ling, identityComp); + } } private void OnStoredRemove(Entity ent, ref ComponentRemove args) @@ -78,7 +106,23 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem foreach (var consumedIdentity in ent.Comp.ConsumedIdentities) { - QueueDel(consumedIdentity); + QueueDel(consumedIdentity.Key); + } + } + + /// + /// Removes all references to the owning changeling from ChangelingDevouredComponents. + /// + /// The changeling entity + private void CleanupDevouredReferences(Entity ent) + { + foreach (var devouredUid in ent.Comp.ConsumedIdentities.Values) + { + if (!TryComp(devouredUid, out var devouredComp)) + continue; + + if (devouredComp.DevouredBy.Remove(ent.Owner)) + Dirty(devouredUid.Value, devouredComp); } } @@ -132,7 +176,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem if (clone == null) return null; - ent.Comp.ConsumedIdentities.Add(clone.Value); + ent.Comp.ConsumedIdentities.Add(clone.Value, target); Dirty(ent); HandlePvsOverride(ent, clone.Value); @@ -157,12 +201,12 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem /// Cleanup all PVS overrides for the owner of the ChangelingIdentity /// /// The changeling storing the identities. - /// + /// The session you wish to remove the overrides from. private void CleanupPvsOverride(Entity ent, ICommonSession session) { foreach (var identity in ent.Comp.ConsumedIdentities) { - _pvsOverrideSystem.RemoveSessionOverride(identity, session); + _pvsOverrideSystem.RemoveSessionOverride(identity.Key, session); } } @@ -175,7 +219,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem { foreach (var identity in ent.Comp.ConsumedIdentities) { - _pvsOverrideSystem.AddSessionOverride(identity, session); + _pvsOverrideSystem.AddSessionOverride(identity.Key, session); } } diff --git a/Resources/Locale/en-US/changeling/changeling.ftl b/Resources/Locale/en-US/changeling/changeling.ftl index e33e5bc715..873744fa06 100644 --- a/Resources/Locale/en-US/changeling/changeling.ftl +++ b/Resources/Locale/en-US/changeling/changeling.ftl @@ -1,6 +1,8 @@ roles-antag-changeling-name = Changeling roles-antag-changeling-objective = A intelligent predator that assumes the identities of its victims. +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! From 7c2c66571a8832e3c6f66008159f3bf9f5e5af10 Mon Sep 17 00:00:00 2001 From: ruddygreat <53099277+ruddygreat@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:23:52 +0100 Subject: [PATCH 100/247] Add support for unshaded displacement maps (#34876) * first commit. helpful commit messages yay! * incredibly minor cleanup * hoping this appeases the trailing whitespace check * helps if you actually commit the files you change * bring shader in line w/ what pjb's doing in that other pr * comments about why this is bad * this seems to magically work now somehow? --------- Co-authored-by: Ruddygreat --- .../DisplacementMap/DisplacementMapSystem.cs | 19 +++++++++++++++--- .../DisplacementMap/DisplacementData.cs | 3 +++ Resources/Prototypes/Shaders/displacement.yml | 7 +++++++ .../Shaders/displacement_unshaded.swsl | 20 +++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 Resources/Textures/Shaders/displacement_unshaded.swsl diff --git a/Content.Client/DisplacementMap/DisplacementMapSystem.cs b/Content.Client/DisplacementMap/DisplacementMapSystem.cs index 6986e1c868..14075caba3 100644 --- a/Content.Client/DisplacementMap/DisplacementMapSystem.cs +++ b/Content.Client/DisplacementMap/DisplacementMapSystem.cs @@ -2,14 +2,18 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.DisplacementMap; using Robust.Client.GameObjects; using Robust.Client.Graphics; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager; namespace Content.Client.DisplacementMap; public sealed class DisplacementMapSystem : EntitySystem { - [Dependency] private readonly ISerializationManager _serialization = default!; - [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly ISerializationManager _serialization = null!; + [Dependency] private readonly SpriteSystem _sprite = null!; + + //needs to be replaced later: see comment on line 48 + private static readonly ProtoId UnshadedID = "unshaded"; private static string? BuildDisplacementLayerKey(object key) { @@ -40,7 +44,16 @@ public sealed class DisplacementMapSystem : EntitySystem EnsureDisplacementIsNotOnSprite(sprite, key); if (data.ShaderOverride is not null) - sprite.Comp.LayerSetShader(index, data.ShaderOverride); + { + //TODO : this is a kinda janky workaround for the fact that the current rendering pipeline does not have + //proper support for multiple shaders on a given layer (or an ubershader to handle stacking all of the effects well) + //should be replaced by an engine-level solution, but this is an adequate temporary solution. + //what's that phrase about temporary solutions? + sprite.Comp.LayerSetShader(index, + (sprite.Comp[index] is SpriteComponent.Layer layer && layer.ShaderPrototype == UnshadedID) + ? data.ShaderOverrideUnshaded + : data.ShaderOverride); + } //allows you not to write it every time in the YML foreach (var pair in data.SizeMaps) diff --git a/Content.Shared/DisplacementMap/DisplacementData.cs b/Content.Shared/DisplacementMap/DisplacementData.cs index 79f89a1d25..8bc922708f 100644 --- a/Content.Shared/DisplacementMap/DisplacementData.cs +++ b/Content.Shared/DisplacementMap/DisplacementData.cs @@ -13,4 +13,7 @@ public sealed partial class DisplacementData [DataField] public string? ShaderOverride = "DisplacedDraw"; + + [DataField] + public string ShaderOverrideUnshaded = "DisplacedDrawUnshaded"; } diff --git a/Resources/Prototypes/Shaders/displacement.yml b/Resources/Prototypes/Shaders/displacement.yml index 70c9dce6f7..debb68fb1f 100644 --- a/Resources/Prototypes/Shaders/displacement.yml +++ b/Resources/Prototypes/Shaders/displacement.yml @@ -15,3 +15,10 @@ path: "/Textures/Shaders/displacement.swsl" params: displacementSize: 127 + +- type: shader + id: DisplacedDrawUnshaded + kind: source + path: "/Textures/Shaders/displacement_unshaded.swsl" + params: + displacementSize: 127 diff --git a/Resources/Textures/Shaders/displacement_unshaded.swsl b/Resources/Textures/Shaders/displacement_unshaded.swsl new file mode 100644 index 0000000000..cc389b9a9b --- /dev/null +++ b/Resources/Textures/Shaders/displacement_unshaded.swsl @@ -0,0 +1,20 @@ +light_mode unshaded; + +uniform sampler2D displacementMap; +uniform highp float displacementSize; +uniform highp vec4 displacementUV; + +varying highp vec2 displacementUVOut; + +void vertex() { + displacementUVOut = mix(displacementUV.xy, displacementUV.zw, tCoord2); +} + +void fragment() { + highp vec4 displacementSample = texture2D(displacementMap, displacementUVOut); + highp vec2 displacementValue = (displacementSample.xy - vec2(128.0 / 255.0)) / (1.0 - 128.0 / 255.0); + COLOR = zTexture(UV + displacementValue * TEXTURE_PIXEL_SIZE * displacementSize * vec2(1.0, -1.0)); + COLOR.a *= displacementSample.a; +} + + From 32b617e1953a7229a9f68e9b00d13747ca1eb704 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 5 Apr 2026 14:43:29 -0700 Subject: [PATCH 101/247] Web vest resprites (#43486) * resprite time! * fix them being off by a pixel * push!!! --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../equipped-OUTERCLOTHING-vox.png | Bin 5654 -> 0 bytes .../elitevest.rsi/equipped-OUTERCLOTHING.png | Bin 5732 -> 7922 bytes .../Vests/elitevest.rsi/icon.png | Bin 5027 -> 5784 bytes .../Vests/elitevest.rsi/inhand-left.png | Bin 5063 -> 6484 bytes .../Vests/elitevest.rsi/inhand-right.png | Bin 5052 -> 7103 bytes .../Vests/elitevest.rsi/meta.json | 4 ---- .../equipped-OUTERCLOTHING.png | Bin 1009 -> 9668 bytes .../Vests/mercwebvest.rsi/icon.png | Bin 298 -> 5718 bytes .../Vests/mercwebvest.rsi/inhand-left.png | Bin 345 -> 6345 bytes .../Vests/mercwebvest.rsi/inhand-right.png | Bin 374 -> 6451 bytes .../equipped-OUTERCLOTHING-vox.png | Bin 615 -> 0 bytes .../webvest.rsi/equipped-OUTERCLOTHING.png | Bin 1233 -> 7701 bytes .../OuterClothing/Vests/webvest.rsi/icon.png | Bin 375 -> 5403 bytes .../Vests/webvest.rsi/inhand-left.png | Bin 358 -> 6232 bytes .../Vests/webvest.rsi/inhand-right.png | Bin 350 -> 6168 bytes .../OuterClothing/Vests/webvest.rsi/meta.json | 4 ---- 16 files changed, 8 deletions(-) delete mode 100644 Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING-vox.png delete mode 100644 Resources/Textures/Clothing/OuterClothing/Vests/webvest.rsi/equipped-OUTERCLOTHING-vox.png diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING-vox.png b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/equipped-OUTERCLOTHING-vox.png deleted file mode 100644 index c075eb9a7b7a41f54aa9a9310f64410fc2535ade..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5654 zcmeHKd0Z3M79N(e1l*qTM8O&(Dq50ELIO!lWD81!076s{st(D73}iD4VY4i?Doa7J z3RSDE?G=f@2JPfz`2WdZKR*s81GQI{wP(}W;8yi%nQV^S2aNl>DAG#(Q} zQ2Z5HaKz{uylvmj33E+rJGG9-<67khuC;hBD_czLWM>DgZ~vepH%aRkTu8|mgg#ia zWM6B+1dsC;mis@nyy*08{)xHfh)s3@%8N_sU01VmZN1Bm?8f#r-E#y+?sdAjo75=HnC0^YIx>2&A!n^+t|gji+PX z3XdbZ1yk*`@{1#O%d~LCe{8nWoMw}9(qm7Z%B?H@FzayX=+boj zFD6|i#_Z#M887{D#3(l7=Z4Cp+)7<)`C?&aXRcXh%d%x zZ$d8DIYu_LaoEXKS@&I@s8e!GDkO0A^sx<}?%7uBP zV`>G7*YZM<_7Yo(`oKh zn86%2T7s&KF;GbqnB-~M!zST>XaX9A<)cCo4ax-LqKt7!7z);d=I98 z9|pi7YlTXnP)IohJz02tjzelbSgS;c1{s2*vtSyFLK6TK8qA{8h_Ks8IKToXPWUrx z{mO)A8~fZ3Q)?8e1cS)9T!o^mjBmzQDP~xw@OZxm2lxW9 z{+-7_ll`48@c6e*-iY6~biJkPjTm?%{TjjKwa&4#UeI!Td)9O02= z`!23$oA!K6nKIj{$Qf6BY0hcb99n~{ zk150XF2b{|PuQ3S6o&TS?kfl^JT~cLcw6(mc2W9SQMxX@EoFBIFaK&{l?7DN7P|(q z3g5D5ub`Hc_V_&y-_!OV78e&?igC_w0{7s>cG(BLuXU@c8|Ir;w?$sCJg{tEYwxx` zLdGtuhfmp`!9v@IPaPLcu$vq-=g??6-zU!rv)|H$au*4lt?e$gr~<~V-di*-eA2Um zS*5#cCb5|U*OVokDWVty0KW9_uTWmid#cdZtn)u6yo^?a!b8ervDeU CV>YA! 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 350f6a288ba1846b3a2015c3c57ed3a732bedb34..1ca03449f8a4e528fc42774aaab6f182dbe0c643 100644 GIT binary patch delta 4797 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 XUy1GzW$CXd00000NkvXXu0mjf45QhS delta 2557 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 E25CG{v;Y7A delta 1700 zcma)62~ZPf6yBhgkRaf}VU4nYg4Ar-O*RLSa0O6A5D+9m6qoESi2{iv7%nG>%7`c+ zq2Ngi^@tWz3n+u2qNuU9Dz>&BZB?*dc%rCPtaQQgn(1`>Xa0ZQ|GxLX_rCYu{2QQe zyvjWcKu2zzOc#skK~1KbRHYH1E=NOvM7D|qfb7fZ(Ltj-KziGgHU2@gk^UC+&MZUc zEq#1Naiso)ql^qy4zwlrDX*EfslHFjv@39el19l~Oj{M5XC~Ym{diZ@&h{xb8RK>l z4+DC7QI~|!N>11_9kz8<-2-(%*y{#EQ*mNjOUx3FF1HY&>H0i3=XvI2*>kw#L~Yoj zCl{OZk38GIQok~%dJ2tQrvOx5>*IzqO6!!zPOffAh${`>TW-h`2PR*%mfPgN+{yz0 z+DesF8XYc`zU4!)*s^TBWJX6I>v(cN)AqOt_rUJ*TJfdOP*#Map=4oglVf5Q+|tL~ zgiiA2lol>Na-{6|?iD4CW;=5s_&2SaBl12lqj(^K0j2nf9#~rQ%3AdW&GCvgU1h`8 zEb7^a2cLAFyQ+QaE0>UfLVEE}ow1jWvyv}6E8X?yKd;x$etlFEGyjLotv6jNWWfv-y}UEfk-uT}x!n~hptRq{dBAjk zwlTsz!Er5%X_78i_8%Fzrf&{Y<(>*>coiIS{_&-YwN-0itBR0cwN(A;Ps=~s{^jMT z7v{D9;dSS<*-2tmw10WYU9;zXTJ^N{lSa>;I_QNmH)8U&f7Tw_1=wD-s^#x&KI0$d zTENQMmp_cfvKm92TA{2oYlSTr+H(U#ou;yc>;jFx$(tkJ7~iPXUd4so#=yt z55j?s6?CDEMyb}RwMw;S5E}XJHiWm)b?oeP4F^}@Vuc9B6fh!y5Uv1+M82p95|Ow7 z5(pJM7*ima5EdG78_+|9;kW|hLNH14Ak-H@A&f*s5I=>FrU-dLp+JEcciMOvKE6lL9u1d#J2_H>H>JTu`HEX`4~$IMHxtxSh2Nr!lyJLH$n7;UPhBvX1+ek&9OZ zIuyBP?VHk2J_mGQ_Zj+rzF9S3-}2OHmFf1(^YyL=D?2M3F2>rgTE^ksyVvmW5RWZm zJCz&!RZbLjHKEIHqdycNUNM_}-dZf90GpPpQ&^w$hrz zkEbKH9XH5p*P~cPUF4s`Eqxx0!UB*!&Bd}_wF!8%%+<0adS=XpeT^G{f~UEX=JdSg hP2}9KgZag@;Ckn1dG<{tHr^hqnL# 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 520d5c63408782867db122dac0a72230843b3e5a..00083227489aeb307f683e8311b084b10b532295 100644 GIT binary patch delta 3347 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 delta 1737 zcmca&bX8f};Gi%$!t(lFEWqh1817GzNx>Te$(Zc@Hb_{7LWX zW3c-y)o!x#3a?cif59S^Q;K~1OKz0ie*e30Js;;QlV_8%yyjh46)M&HNd4A>2c;hx zqPqU6zUyl-VA(e#&ogZQ@xzJ+^WC)PwVb*wblNoVExV?<;_rHny~+zW&I&M9JmJG4 zoKdVZKkMXk!D(mX1D}=7ozVVGvFN*LadmczfRZ$mjpT)!48MB$dfO&3Pif7Wx$T3A z%NylFMgQts5C1(fiWHep5)sD0z}S-M>>S|f>wSZC)#=(c91z5Z@*<}i0m7M zM-vuW-E?)8srMG`PFT1_G^*5K=NIl-CMueGeNEX15B48j)!e;#9pAbpg&&NM^ei-Y zd365w=F&~Fv3PfUcXj{n@`ml9$z2conY*%H%}jARAs*IxK>Lq`i+vME=a~yZH;>B1 z9KUXBQ?s}Gqt5r@`aZ^;83qTG1&+^LHv5S5kN zEdLzanX`F%y4#nSw0RazI#a46UL}TI6n!N5XvLZpQ{JDo)+xUCXw4O=HO2n_4iyB( zo-CUCOkz@m6IUk3gbNO$rZYM&u9}pj;QX6Y{Qru#{*%3x!bN*zxK}tgX8b<*|3Ue7 zx6Gcq)2$Djn5_0{k?zloDsR;Xe|z?=WZ=2aw9s_<_H%ZA@(nVjTWgqQWSE3gPsNp2 zmT%a;z5aa3uiuQCiA9~OA3juLVBlZ3`8MY=Mix-2n*5MUj042bH`FtjT*7V6S&)%m zl3$#WUjUNgh4aAj^SDcNDsl_-QZiGlk}XUUlMM{bbPbKnQgkgWO)PXR(o)QH&CHXH z4HAEX>R%+w)4wK(xUF z24bIYehSb!29}CI$k{5H8yNtBz9EQ2 zD2c?Y1Q-QY&PAz-C8;hz3^d!o$OIS-x`q}ZhUQk>1_o9}mf8k}Rt5%>3wbStO|1+} ztPD*;fFedlR;I?f24<7@@=9YBf6eQ`o(wiRWpV(YQUuX)0*sd864$a4tK$5$lJdl& zRLAtxJfOcFQ}UBi6#`(1Nƍ#SFvPTs?(D1qc?BsVMRPZr=yQZY5Lu&_upN!B&D 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 delta 1773 zcma)6X;c$e6rLa!j4YwbCa5tYN@2nz8L}awB%nFSB8DYZ3p2?CVj;;%Mp?uL5vhOz z5=0UA)3UhGmbHi|hzMF07dRALSoBmual;Ko?F5fq6&BiulW&<@OmW3ju%mE*p z)h86?3mj`KXP&EicWQRhI<@$k*sW{tq=TWZ9Ag{jo<2MLNy>?|v5*o$RK9V^#g2;J z-Um;i-Ae`efqpO7BX#7?$f$bzQGxE7Cu)4uA(Zp|ejj`@`?R&DTJ!!)#TM6C<&YDW zFx&an>Z!%G&eOLf>KBmQ4p))}J$Jyyi%zBLud6d*% zUKBVx#c^GA@+3z`qiNWMpW+ka|E#Umwc~Hzl3Wl?#?k(MX8^D)R}dnL2w2XSs^aK~ zOod|fgg7+-0F{wu*(76w+Vnbe8&iYf&Y(m3K6B0#b&LvEX=7As17!aBZyYCE;I0G% zU8a!nrCb=1LJT&@V6tT(mj`n}u3W|j*&HbgLZl3Y1L^;=pgM7-Fh?%sA|MOJFMNUs%l7eFGtfMy67!GAUoK(!?RS7flg|L}Tu1W%M{;=z}j% z#i}$b)rb`HqA@1i!HNVdHbSU~!<1TuO6f&o!(W1yv63%Py~I*z0mE#D2MgwTFqsg< z(I2(6*L@88d@djJ1*l|1I@UNNu8_<5K^g_7#1VWv)yFc%{UMlE6}!&h4pl0M2AC5e z5fhjfjYH&vAa{mgo!BA?!GxdUzp{rI4PT(a5F8T{A3+W=VPZTWgDYlm_z=Wr@?0SX zVd!sJ@}_z4A(+pA#e|2+jCn>auZ=Mw~E-mi69_MRS zay%Z4dviAiM;#6vMIQ*UIol;QzG1kvaAc$E^X7*m@Lqe2#O0lq{1mkNP!!UDAF z(%EARn33*?| zD4V>7UlU&B+BVrZXmpd4@m{l%l~lof%VA3YD~VHD`rLh5SY%{K001fFBe!qR3imjH zd1Z-@!J60ZjL3GCNCoomJ%9H6;CUJPM|+M=bJ=&-+0jMeE2gu1??pcHSAyvjw}6$H zr1<)v6YILXQiSr73DC|AUpTAt!HcR}%1$`o*&VX!ockNALr(X0wI&^>_=vkXtm-&d z(b^kf@|X4GC%22Vf%irvl7c}sH>UQ+JVpblE6!on*vHa{wXwl&yI)vWNcHws>4UqI z#i3Z9xT`ue)cTf5rY^Jq>)4t5zS{bBfxr2$%}VUuiM!F2g*&nC<2G$0H|clc599(s Z{KIK?hmdXUSI9mFT9I#{u-qpq^?!vxp@aYc diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/meta.json index 84009efde3..1797234d0d 100644 --- a/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Vests/elitevest.rsi/meta.json @@ -14,10 +14,6 @@ "name": "equipped-OUTERCLOTHING", "directions": 4 }, - { - "name": "equipped-OUTERCLOTHING-vox", - "directions": 4 - }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/equipped-OUTERCLOTHING.png index b9273f213f241089aa01238b26b1e8413024a4c9..4f08ace7129dc4d842857cb68494ecab3b74d4e2 100644 GIT binary patch literal 9668 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 IRI`uxKl!_+jsO4v delta 988 zcmV<210(#zOYsMgBYy+9Nkl4e-7@Z4;7~v36xzpwn2)4FoBZ!4q1uSBl zN=p%cgr(T}57a7HS%{_ATG&V+iPas65jij-G10g3*15Ua-Ea1GmfhPK*v#yFGv9mj z=9}5e&bGck8>uFzr6vy^V-Es^2xzzhVF@%m0Kz61mVmD%aDV=B&zI{${W?X!`qr&e zTs8)2He35Vb$4FgIK^e_n`X1MXPuJ(=rkByQ%}bar%2!)^<|`f|C};Kz&+~oq_#^Z z401I3rh2^#Ga{hAED^xj6v1U2dRr#}*zw@ti<(S+@m6&@Xa1Hu@x8ACW*J!h{wzXL z{cOwWnp^_F#ecs+Pdh8?u1IY09)L}5IQ$%qaeMoxIzH|wJ%<7Iq)l>Oaik$HZAU)h z2DbqdNFv{LK(jVU4MMDe_GDXX4$LS2}Q3vH+!GF=uH35UO4D5D$61gBJh(Jyf-?`5P z0Em+!9G1EON=Rk3As(=CC__QWDXYJ37e@d(QDr&+I4%&uwYGMaCT}?~h{qEt9n9pY z+iYdA001=;l{f3lyti?%aZTbR769J{n2Cp_u;Ic76TTBLhzBfk&~a^Cb-P{`3t&@~ zA%D2LyB~irVLtwJuzAbyjgK?1@c|Qw9CY6{uDV?>iv`d-vCQxTDWuiaE=}HYVi1!Y z@&bwlKv*R`=13|pi9G$1Fu~Ew5_o?7Rg0?I2;-4;R9WyJU{MHi0EEs>3RUU!zW_F&&CQ$f&tDV7MR~KY^gfFN{J8LN z5`KNKB0!?jBDZOL1pEP?aDG`@8G{l40000< KMNUMnLSTYyp38aw diff --git a/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/icon.png b/Resources/Textures/Clothing/OuterClothing/Vests/mercwebvest.rsi/icon.png index aac777b0f9583f08f18781a675ddfe16d34aec68..61811311f757e51405c7d763b8dc991e133d17b3 100644 GIT binary patch literal 5718 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 U5OtU2K>@>DS#C~+jDWcR0PY;qm;e9( delta 272 zcmV+r0q_3SEUE&KBYyz#Nkl1P5F`@~Y4$h6?85xFv$L?yWn<`5gI?V&fCaDs7C;IR#bFV} zVIkf78vuY(bJppqX0%RMg;H~ADFB92b7Rf~!T1Bhm@{o4M1KG;9ghdD*L#_J-U#s8 zlxhd?HvL_VXCR8hLhC&MNpd64y;=m&dXIDv8+X9>2e|HpEIToqO}|EoM6VzS#*JcU z0A0wDe94x2QQmKwKFW39f1hKkc8&x<)PYy=s(Ce+nuD+dU8wgNumBdo0$70F0iNL+ WU18C~^vnPN00{s|MNUMnLSTX+TXyIG 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 8ecff94859b36d3e00423953fec042edad9573a3..0aca74f6b6d516c4c984b45a1531bda36501ca54 100644 GIT binary patch literal 6345 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 a$w94qGS`0ZQy-WGhy!K4O^KENj(-DTiuT_C delta 319 zcmV-F0l@yrG1&r;BYy!PNklP=qODQrs>j3 zQkpJp6fJC|fgPk%qo_x;JoHK-fUvi|Fo z91S4LHg#N<#X-J5shcl~L$CD#000000000002q$Xor5?IY#ay9_6KfOl7wfCCrQ|822{sG|Mdjkc*G%66VYIqK|}ItXE2|n@%4zK~y2s`(();yAOt5$=Lb?CLG4&LC!OlUJdmv z#l#D_IzM%`u^M|J?R7H;rQWkdd-W!tm-k9~i|wb7`Tj~(!T96l{YI97H$w^Y2!Z)q zs19s+3vb`-y!3~ht!E{}YlEc=n;PD4y+#ls-c|c#YFu9Go9i^w2cPvFdc{#2`ONW) z8mWAi72;8Wl{MT@y8$6@pU6^#&ZVSdR!=*hi_8>$n4GEh=hk&6gGR3QTy=kPL{goA z2t3*1^|C5tf^bo`jvN)lVh0XX1x-|hoKHx}oYD$uA^%1y9{y8aHeu9HYv^ zRh=_7rv)-bVjX(o)d$(o!O@ly@T-)Swrz#>JA!PKWyEd7)@S;U%aubDcvOC zY(O{#C4h)UcVtx}*-WA}dV zoCmGFn%u}av(e(3O0%17uE3$#y`Nq>oXWX!@wbCXRJc>|b|Q+lS3}J*8g3b<<+UI8 z>Wmk|eD6*to95iJPEO(nEmECq9~M7#I&A7;${rqX2=5Mj-9fhRv3*=Q+uH{X+O34| zXu1@O?_FoB`WN}i1&<59B_r}VMK3ze9XbMZb?N4m)Q{iwy_(_w%(qnDQ_{gGI5aFZ zG+R!>rUY4KJxqIaKx(ifY7-SA=R6C|6c-f4D^TsHQL9ZKAy(#@I( zBfkV4Rm)<1ZN`x0+lxCMcNL$unvp`~F6GXaS!+JDxJPwqQCVmx*qG(b^uBdeDYo;F z7?$|gN3zZh$HdT`&dKZc8Mk=Fh&C-c&6SDnxPIFzg?cgKNqBx!sh-;YjwUnD^OsNW zDuo`+)I9CjN;Oig5o*uxzShicAFL6t@EOa@;mR%SphkA6EjMKzH(!=NcH-{jyC;Lh z&*h4&@@-dQhU#Fdg=;j2DcuV5dFSuk`tV-yrpim1{31V%lEC`5w5Cb5jLH9Vxi>

L|--g~w{QDW?VTL9+8s5Z9VJuTu@Z_=59 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�}i@y~$y? i#g>$!QlMs+>y>Y}_OspIoGAsX03n+1H_O}Q7XB~c4`5#a delta 348 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 delta 1214 zcmV;v1VQ_iJkbe|BYy;#NklJZ#qlXb)&}2J{waa|h6epnu*1dJ8BpaQ^uv6F;{h z_1fI}d#S6Zqh4D*UCLXaudk1;PEQ^p0cF&;LV9|7$hin8quz^zRR%#xJRYZ9E=S{U zZX6;Zkx0<)?ykyozWO>z@H&|i0Z>vZl?bcd(a}N0Vv(ZJsK}$f6#}B&1oaym8*WW? z>y);DT|uQ%v41A!yuICOY+wc+2%;;Y3t|y=DJfEvQSW6NND}~;3x~rL3WaESd0Du7 zxm>30?QP0rGK4n#8s(Z!-gw?(`5YzU|0q{DV|76p^$_l7HJ*@>CIIt{jEqn|pQoXr zA(6uE3WWj<4-eDo>Z)izL7(U5?(S|eHX8XOjIUPr&wrb2J|@bjhj2epkoc26128d+ zg>cG;9+#1-0+7IHbump-cw8*{m%6&Tgxli`f)|!b+k|#iQSt!iOf+1W|>;oz%Qt3pjA5+TDd#8ct-i$z0Hw{kb$ z-)@H;AAi8A`jrR(;!CftnOj?1N5I4N7oU71z@oDfB-C_FB7x}_@XZWgS7_)U=e+p zpgq7BeE}G(Js<#!=*tA@ixFtK`0|Q%^DsU(Mt>jgzHjWP~e7I-+Tv)KW{(AOZ z8kh1GNG6jsIyy?Z+Eti6PN3fBY71F|GIm|S04%@+Y^oytSsaRbdwXeOVuEHSr;VHM zZi`EjY&L6Ml|i|eZ}Tdk?ZzWkVPIf@c6N5?^Zif8gV`x_etw?T*Vk!oYF5e%lssT< zZGTM|55Q1}`#AsrkXA*AKsR);@xTBqsK>$pql!qM0l0%pJ^p%#)pR;7E-42G2Pqbd zsp$3G7slh38o?P$OH1O4&OX_XM_-J~udiEYmA%a^FYN*S{ryMA`~pbU`O>q#1)X>^`-uuiuv;D+@$9MS}&QmJF(Zy3~ZN}5p*orqL(3LcZdDl5_iq@T_iZhzq~ z2$q1rB@r|QY34bAGXT0EZUg`$u!;^6f6@f-TnLbG=J7Iwpa}p|i8$eTWK*6Sx;P@l z4Ug=6=qLRq$g5g=<_iLNnvEr&*NGfYWS@7i^jUFRTS=jP`&at)>@Svo`SzUec+R)~nVcju_xI{aDvsn4_e@TXVa-`k+G+Gdx$I{eq}j==$*Y+?ha z1Alb6|3Iz@(DA01+aeA-TQsFPV&N$>gQX_Q*)L`vD5911xj%aP%PU~gsZ_kxZI`N{ z$JMyx0MKjO);vp9=8NvM`olAqf7hRCuVXOA%0#vQc*OUU`hyCsCU;%^{!kAw!(~_h zl$M+C1~vruM6e4tF0vZ%NyV)w$Xh!tRmyC0{v$V~rD%zjGcqsrA+xugY;D-ATS`da zNq0Y+*v0Zq7x~Bz&X006`aZhdsABhiJ#WzJ!J|q&x}Y|WU&38rd(cnMsB&AP&7moz z2|r$;wG-Q+vMHuBYz!g42rUwnCMw(t;l(qK4DnmGypJ$|wE^S6D`k46 zmm{?JI@b)NIB6K3^+J1PV2^h?+X5r6yu9&OZ7qJcfh{x3G!n8bdCA=aG&M!qzBsZ? z%fMr0P~^3Gt>DX>^8i@^O~Ax2Sth*@3Fq6WzAny_=Oi0nPH(YnS8SXrGwc>i>G&zLsnYX|Y?m3s1YE zm0@OjVf|PkW<$U%>=NxUrZhd_+J%=b8+Cg$F85WI0 z=91E$36(DoYOzA_-~o9*Q!-N%Ql^k$%n%FnZ^(8+4^o5R;8RP*^_alFSMxy zuB$a3T)^2XT)M}8C^*M@@04x|{`PS*Vkq`tAN{DeZP&tc7t+0M&9vF}GCM*>+8F}K zwF6HF9NGNCRm-;JcF#g>I#be=QR^MtY99`|_|2Q-n3FVP+Mic~a*tGYLK0I0a!aS{WjD zcOKW>{bQ#>`_uLn`87Uu^DWOU4e-d%bkE`}Z{+QmGdOPfW_$B9Zlc-8pG3+Z6Qi%@ z6<0bM9@5oYIVX79qU~{UGBces)3NWh{wRKgvq>f5kFR@qzj8v~bLZ#xwA_l$1|EyC zVm%pxgeZzNE*q7Efk*yt$lc$-hgpG~GJ?q`;1xHqQj~Cjw+Z4{q zvD@$Yw_7Wc@ya~v1Rpw>+&_72{~hmwr?j;b8zxQSxTSBfXxdlnwmCT4)C7MgH*v5o zXC9aE(;|c!*Y~usH534)w7G#ui8F*vus->}SA)QCJbqnI!-n*Ra?J>B&a_phw zCu{Ctg!8zED^4^{B$XR2T2w(#oKV@WwWd({d!FU*{sDHTX|JhoN-T{}>%M7izZd@2 zaRr8Q{YJl$Os2oV9G`sm!QJW|)kDc8H##Pq&6hX4A6h*VJyPOD=+P1C=gWemVgd+D z1qeYUmZ3)r2IDkOB?F-tM2Qt3Q6h;muJ2S04l9D4aUpa+lAp{Si578^JFxI2S{#lN=VZ_-xM+3iQqy7p+vvSVUq%LINRyN|4H(niCXA1qL}Ho73l;kdq*5gOEY=sXsYf*Fj1B~K|HS(R`eW`IFpBc? zW3i=BoH{%%+Zm^xp9M=H5zNwDIyh1pB!of(XmlYA&=A-WU_t^q0FoGF7-G_B0?=_3 z6<4BAf)WT(Q=#Mp5z0emQelV;qdpK7rJz$C00HU;z!W;{K!q4|ghClb;V&1VRSCw9 zj!I1hqf}IofzTKb8K4UU6o5v8n1Fx+!vKRsB~wAzfrQSap@Jcnr&KNm(d870!6<|% zlSF9-)Pl2Id0b~4g+Tgj;l+YVA?kqc0g(ijCMZ4+1&YK-fD%;eNoFwUGz!U)LSoYB zOh@u(P%t7_ptY!GC6fr$;e6B!!$Q+Rr3KZMiV|o%&}>-las*UL<$+RZtTRp>6jtr| zY1j|lP%x+j*`N|ZNl6q37Kz3p(F4g;7J5x*0we~DGzu?;MZ(1Yg;sALtkdwKb3_Vs z{zQ#wcuxf&%ZKlVZ(~K8&4k5jwgn4>hEq_0@d&Jm6XhBnf}%l56oU4Tk%Ikb7yVN) zkRgahgCT${pppR^Lx=z%Os4`c;z)HA!epwD$^4RCAr&eUKsn+Xh3bfEg%+qrE3Dnf zQsKYECqyIabwDKpNHkzXGL2Zokz|SLj`1;DC*ptU;iLhKYB8waunlcpXeT6oYK0?u zshiHf_!?P#G#_D)6`J`oGC#`1xT9k)Z#8 z63|Dbpz!%-=wsGcfsYp(^FjUIRlRo;YB7*;!W0vbZzPfbLHjRzT=NPk7Io9)vZ$&|K%88YQ+I`bA-{n;m)&*^L@W^Hc zdf4ttuus_^+;_cWX5`een}2o(p9wxzsHcs^zc83nzHw7YhP$Px}W8S86bGRg>0t9adpXCa=`p>gIz-)5dT; L7O)SwMXve}P!2Bv delta 349 zcmV-j0iyn!D)$1ABYy!tNklp_66jjAOW_uWTl{#%`G)iJ zxw=$PN@%Etw)UnItq z&2&0N2tm^{&c(s;2#hhedEfUx=J)$g2c$(lNGS=!ki+3X+qPUT7p%3EWr>s$DJ8BK z(WwJjmQhs|i^YPfsz{Oqtu@Q#lGEu#mSryK+yHSLqqPROjo+8m|F27`HkyZ~;6cI%cecyAvUICa)CImr1UDxb4yTAqD)PenG7YHFR#&ABLvDRX( v#TY}^b)ho|N0ntfCqSh2aFM4!zhBvE>zue00000NkvXXu0mjf95tmQ 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 bb043734cdca3653be7d9719eeb498103ab64f07..910c02b87f2d6678f4c44d7af774c583591858af 100644 GIT binary patch literal 6232 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 Date: Sun, 5 Apr 2026 21:59:11 +0000 Subject: [PATCH 102/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9f703312dd..1f2eab8a5b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4039,3 +4032,10 @@ 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 From 38f3eed150fe65e3c6b0f831c1f156998aadd1af Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 5 Apr 2026 15:01:59 -0700 Subject: [PATCH 103/247] Uplink implants connect to the same store as the PDA, and also fix not giving stores to PDAs (#43485) * WHISKEY ECHO WHISKEY * add fallback behavior * add comment * buy from the proper store ent --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.IntegrationTests/Tests/StoreTests.cs | 14 ++-- Content.Server/PDA/PdaSystem.cs | 6 +- Content.Server/PDA/Ringer/RingerSystem.cs | 13 ++-- Content.Server/Store/Systems/StoreSystem.cs | 10 ++- Content.Server/Traitor/Uplink/UplinkSystem.cs | 73 +++++++++++-------- .../PDA/Ringer/RingerUplinkComponent.cs | 6 -- Content.Shared/PDA/SharedRingerSystem.cs | 12 ++- Content.Shared/Store/SharedStoreSystem.cs | 11 ++- .../Objects/Misc/subdermal_implants.yml | 4 +- 9 files changed, 86 insertions(+), 63 deletions(-) diff --git a/Content.IntegrationTests/Tests/StoreTests.cs b/Content.IntegrationTests/Tests/StoreTests.cs index 411f4762dd..5a63dfa563 100644 --- a/Content.IntegrationTests/Tests/StoreTests.cs +++ b/Content.IntegrationTests/Tests/StoreTests.cs @@ -1,6 +1,7 @@ using System.Linq; 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; @@ -22,7 +23,7 @@ public sealed class StoreTests : GameTest - type: entity name: InventoryPdaDummy id: InventoryPdaDummy - parent: [BasePDA, StorePresetUplink] + parent: BasePDA components: - type: Clothing QuickEquip: false @@ -68,6 +69,7 @@ public sealed class StoreTests : GameTest EntityUid pda = default; var uplinkSystem = entManager.System(); + var ringerSystem = entManager.System(); var listingPrototypes = prototypeManager.EnumeratePrototypes() .ToArray(); @@ -89,10 +91,10 @@ public sealed class StoreTests : GameTest mindSystem.TransferTo(mind, human, mind: mind); FixedPoint2 originalBalance = 20; - uplinkSystem.AddUplink(human, originalBalance, out var notes, pda, null, true); + uplinkSystem.AddUplink(human, originalBalance, out var notes, pda, true); - var remote = entManager.GetComponent(pda); - var storeEnt = remote.Store; + 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); @@ -142,7 +144,7 @@ public sealed class StoreTests : GameTest var buyMsg = new StoreBuyListingMessage(discountedListingItem.ID, null){Actor = human}; - server.EntMan.EventBus.RaiseLocalEvent(pda, buyMsg); + 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"); @@ -155,7 +157,7 @@ public sealed class StoreTests : GameTest 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); diff --git a/Content.Server/PDA/PdaSystem.cs b/Content.Server/PDA/PdaSystem.cs index afc5876760..724c9dc491 100644 --- a/Content.Server/PDA/PdaSystem.cs +++ b/Content.Server/PDA/PdaSystem.cs @@ -311,11 +311,11 @@ namespace Content.Server.PDA private bool TryGetUnlockedStore(EntityUid uid, [NotNullWhen(true)] out EntityUid? store) { store = null; - if (!TryComp(uid, out var uplink) || !uplink.Unlocked || uplink.TargetStore == null) + if (!TryComp(uid, out var uplink) || !uplink.Unlocked) return false; - store = uplink.TargetStore; - return true; + store = _store.GetStore(uid); + return store != null; } private void UpdateStationName(EntityUid uid, PdaComponent pda) diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs index 06df92bb68..3efb6cbfdd 100644 --- a/Content.Server/PDA/Ringer/RingerSystem.cs +++ b/Content.Server/PDA/Ringer/RingerSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Store.Systems; using Content.Shared.GameTicking; using Content.Shared.PDA; using Content.Shared.PDA.Ringer; +using Content.Shared.Store.Components; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -95,18 +96,20 @@ public sealed class RingerSystem : SharedRingerSystem } /// - public override bool TryToggleUplink(EntityUid uid, Note[] ringtone, EntityUid? user = null) + public override bool TryToggleUplink(Entity entity, Note[] ringtone, EntityUid? user = null) { - if (!TryComp(uid, out var uplink)) + if (!Resolve(entity, ref entity.Comp)) return false; // On the server, we always check if the code matches - if (!TryMatchRingtoneToStore(ringtone, out var store, uid)) + if (!TryMatchRingtoneToStore(ringtone, out var store, entity)) return false; - uplink.TargetStore = store; + // 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((uid, uplink)); + return ToggleUplinkInternal((entity, entity.Comp)); } ///

diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index 0fcabba30f..63c6d48b0b 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -33,9 +33,10 @@ public sealed partial class StoreSystem : SharedStoreSystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnImplantActivate); SubscribeLocalEvent(OnIntrinsicStoreAction); + SubscribeLocalEvent(OnImplantActivate); + InitializeUi(); InitializeCommand(); InitializeRefund(); @@ -110,9 +111,12 @@ public sealed partial class StoreSystem : SharedStoreSystem _popup.PopupEntity(msg, target, args.User); } - private void OnImplantActivate(EntityUid uid, StoreComponent component, OpenUplinkImplantEvent args) + private void OnImplantActivate(Entity entity, ref OpenUplinkImplantEvent args) { - ToggleUi(args.Performer, uid, component); + if (GetRemoteStore(entity.AsNullable()) is not { } store) + return; + + ToggleUi(args.Performer, store, store.Comp, entity, entity.Comp); } /// diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs index a8c1ab3da5..c5c66e1a40 100644 --- a/Content.Server/Traitor/Uplink/UplinkSystem.cs +++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs @@ -30,6 +30,33 @@ public sealed class UplinkSystem : EntitySystem 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. + SetUplink(args.Implanted, Spawn(TraitorUplinkStore, MapCoordinates.Nullspace), 0, false); + Log.Error($"{ToPrettyString(args.Implanted)} did not have an uplink when they were implanted."); + } + /// /// Adds an uplink to the target /// @@ -37,7 +64,6 @@ public sealed class UplinkSystem : EntitySystem /// 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. - /// The entity that will have the store in it. /// Marker that enables discounts for uplink items. /// Binds the uplink to the specific uplink entity. /// Whether the uplink was added successfully to a PDA, implant or not at all. @@ -46,23 +72,24 @@ public sealed class UplinkSystem : EntitySystem FixedPoint2 balance, out Note[]? code, EntityUid? uplinkEntity = null, - EntityUid? storeEntity = null, bool giveDiscounts = false, bool bindToPda = false) { 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, balance, giveDiscounts)) + if (TryImplantUplink(user, storeEntity, balance, giveDiscounts)) { return AddUplinkResult.Implant; } + Del(storeEntity); return AddUplinkResult.Failure; } @@ -71,20 +98,18 @@ public sealed class UplinkSystem : EntitySystem FixedPoint2 balance, out Note[]? code, EntityUid? uplinkEntity, - EntityUid? storeEntity, + EntityUid storeEntity, bool giveDiscounts = false, bool bindToPda = false) { code = null; - - storeEntity ??= Spawn(TraitorUplinkStore, MapCoordinates.Nullspace); uplinkEntity ??= FindUplinkTarget(user); if (uplinkEntity == null) return false; var ev = new GenerateUplinkCodeEvent(); - RaiseLocalEvent(storeEntity.Value, ref ev); + RaiseLocalEvent(storeEntity, ref ev); if (ev.Code == null) { @@ -96,25 +121,15 @@ public sealed class UplinkSystem : EntitySystem if (bindToPda) { - var accessComp = EnsureComp(storeEntity.Value); - _ringer.SetBoundUplinkEntity((storeEntity.Value, accessComp), uplinkEntity.Value); + var accessComp = EnsureComp(storeEntity); + _ringer.SetBoundUplinkEntity((storeEntity, accessComp), uplinkEntity.Value); } - SetUplink(user, storeEntity.Value, uplinkEntity.Value, balance, giveDiscounts); + SetUplink(user, storeEntity, balance, giveDiscounts); return true; } - /// - /// Configure TC for the uplink - /// - private void SetUplink(EntityUid user, EntityUid store, EntityUid uplink, FixedPoint2 balance, bool giveDiscounts) - { - SetUplink(user, store, balance, giveDiscounts); - var remote = EnsureComp(uplink); - remote.Store = store; - } - /// /// Configure TC for the uplink /// @@ -144,9 +159,9 @@ public sealed class UplinkSystem : EntitySystem /// /// Implant an uplink as a fallback measure if the traitor had no PDA /// - public bool TryImplantUplink(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)) @@ -157,15 +172,15 @@ public sealed class UplinkSystem : EntitySystem else balance = balance - cost; + SetUplink(user, storeEntity, balance, giveDiscounts); var implant = _subdermalImplant.AddImplant(user, FallbackUplinkImplant); - if (!HasComp(implant)) + if (!HasComp(implant)) { Log.Error($"Implant does not have the store component {implant}"); return false; } - SetUplink(user, implant.Value, balance, giveDiscounts); return true; } @@ -173,7 +188,7 @@ public sealed class UplinkSystem : EntitySystem /// Finds the entity that can hold an uplink for a user. /// Usually this is a pda in their pda slot, but can also be in their hands. (but not pockets or inside bag, etc.) /// - public Entity? FindUplinkTarget(EntityUid user) + public EntityUid? FindUplinkTarget(EntityUid user) { // Try to find PDA in inventory if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator)) @@ -182,16 +197,16 @@ public sealed class UplinkSystem : EntitySystem { var pdaUid = containerSlot.ContainedEntity; - if (HasComp(pdaUid) && TryComp(pdaUid, out var remote)) - return (pdaUid.Value, remote); + if (HasComp(pdaUid) && HasComp(pdaUid)) + return pdaUid.Value; } } // Also check hands foreach (var item in _handsSystem.EnumerateHeld(user)) { - if (HasComp(item) && TryComp(item, out var remote)) - return (item, remote); + if (HasComp(item) && HasComp(item)) + return item; } return null; diff --git a/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs b/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs index dd0de10493..327cd608b5 100644 --- a/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs +++ b/Content.Shared/PDA/Ringer/RingerUplinkComponent.cs @@ -14,10 +14,4 @@ public sealed partial class RingerUplinkComponent : Component /// [DataField] public bool Unlocked; - - /// - /// The store which the ringer is targetting. - /// - [DataField] - public EntityUid? TargetStore; } diff --git a/Content.Shared/PDA/SharedRingerSystem.cs b/Content.Shared/PDA/SharedRingerSystem.cs index 3c1e392f13..ee0a7fb0bc 100644 --- a/Content.Shared/PDA/SharedRingerSystem.cs +++ b/Content.Shared/PDA/SharedRingerSystem.cs @@ -27,6 +27,7 @@ public abstract class SharedRingerSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPdaSystem _pda = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] protected readonly SharedStoreSystem Store = default!; [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; @@ -137,7 +138,6 @@ public abstract class SharedRingerSystem : EntitySystem return; ent.Comp.Unlocked = false; - ent.Comp.TargetStore = null; UI.CloseUi(ent.Owner, StoreUiKey.Key); } @@ -146,12 +146,12 @@ public abstract class SharedRingerSystem : EntitySystem /// On the client side, it does nothing since the client cannot know the code in advance. /// On the server side, the code is verified. ///
- /// The entity with the RingerUplinkComponent. + /// The entity with the RingerUplinkComponent. /// The ringtone to check against the uplink code. /// The entity attempting to toggle the uplink. /// True if the uplink state was toggled, false otherwise. [PublicAPI] - public virtual bool TryToggleUplink(EntityUid uid, Note[] ringtone, EntityUid? user = null) + public virtual bool TryToggleUplink(Entity entity, Note[] ringtone, EntityUid? user = null) { return false; } @@ -178,7 +178,7 @@ public abstract class SharedRingerSystem : EntitySystem return; // Try to toggle the uplink first - if (TryToggleUplink(ent, args.Ringtone)) + if (TryToggleUplink(ent.Owner, args.Ringtone)) return; // Don't save the uplink code as the ringtone UpdateRingerRingtone(ent, args.Ringtone); @@ -245,13 +245,11 @@ public abstract class SharedRingerSystem : EntitySystem ent.Comp.Unlocked = !ent.Comp.Unlocked; // Update PDA UI if needed - if (TryComp(ent, out var pda)) - _pda.UpdatePdaUi(ent, pda); + _pda.UpdatePdaUi(ent.Owner); // Close store UI if we're locking if (!ent.Comp.Unlocked) { - ent.Comp.TargetStore = null; UI.CloseUi(ent.Owner, StoreUiKey.Key); } diff --git a/Content.Shared/Store/SharedStoreSystem.cs b/Content.Shared/Store/SharedStoreSystem.cs index 0abd12742e..c281685077 100644 --- a/Content.Shared/Store/SharedStoreSystem.cs +++ b/Content.Shared/Store/SharedStoreSystem.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using Content.Shared.Implants; using Content.Shared.Store.Components; namespace Content.Shared.Store; @@ -49,11 +50,19 @@ public abstract partial class SharedStoreSystem : EntitySystem /// The store entity and component if found. public Entity? GetRemoteStore(Entity entity) { - if (RemoteStoreQuery.Resolve(entity, ref entity.Comp, false) + 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; + } } diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index 8e29fdd88f..8232a82a2b 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -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: From 80257bc9821cddb92e9cfd3b3e46bf3befe79c10 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 5 Apr 2026 22:17:53 +0000 Subject: [PATCH 104/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 1f2eab8a5b..064fdc8e90 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- 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 @@ -4039,3 +4030,10 @@ 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 From 54e56cd1fc9c3a367b5eeb76e8defeae8a03c9de Mon Sep 17 00:00:00 2001 From: AndrewFenriz <78079974+AndrewFenriz@users.noreply.github.com> Date: Mon, 6 Apr 2026 01:15:52 +0300 Subject: [PATCH 105/247] Expanded Tile Recipes for Cutter Machine (#43431) * recipes for tiles * lil fix --- .../Locale/en-US/lathe/lathe-categories.ftl | 4 + .../Entities/Structures/Machines/lathe.yml | 5 + .../Prototypes/Recipes/Lathes/Packs/tiles.yml | 50 +++++ .../Prototypes/Recipes/Lathes/categories.yml | 16 ++ Resources/Prototypes/Recipes/Lathes/tiles.yml | 207 ++++++++++++++++++ .../Prototypes/Research/civilianservices.yml | 1 + 6 files changed, 283 insertions(+) diff --git a/Resources/Locale/en-US/lathe/lathe-categories.ftl b/Resources/Locale/en-US/lathe/lathe-categories.ftl index 209daf1ad3..54841b2101 100644 --- a/Resources/Locale/en-US/lathe/lathe-categories.ftl +++ b/Resources/Locale/en-US/lathe/lathe-categories.ftl @@ -31,8 +31,12 @@ lathe-category-faux-tile = Faux lathe-category-maints-tile = Maints lathe-category-marble = Marble lathe-category-steel-tile = Steel +lathe-category-shuttle-tile = Shuttle lathe-category-white-tile = White lathe-category-wood-tile = Wood +lathe-category-plastic-tile = Plastic +lathe-category-precious-tile = Precious +lathe-category-industrial-tile = Industrial # Science lathe-category-mechs = Mechs diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 31fef56da6..b740396326 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -690,10 +690,15 @@ - FloorSteelTilesStatic - FloorWhiteTilesStatic - FloorMaintsTilesStatic + - FloorIndustrialTilesStatic - FloorWoodTilesStatic - FloorConcreteTilesStatic - CircuitFloorsStatic - FloorMarbleTilesStatic + - FloorShuttleTilesStatic + - PlasticTilesStatic + - CarpetTilesStatic + - PreciousTilesStatic dynamicPacks: - FauxTiles - type: MaterialStorage diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml b/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml index 2df2891b7c..f9147efe4f 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml @@ -15,6 +15,7 @@ - FloorTileItemDarkPavement - FloorTileItemDarkPavementVertical - FloorTileItemDarkOffset + - FloorTileItemDarkSquiggly - type: latheRecipePack id: FloorSteelTilesStatic @@ -58,6 +59,18 @@ - FloorTileItemSteelMaint - FloorTileItemTechmaintDark +- type: latheRecipePack + id: FloorIndustrialTilesStatic + recipes: + - FloorTileItemMining + - FloorTileItemMiningDark + - FloorTileItemMiningLight + - FloorTileItemElevatorShaft + - FloorTileItemRockVault + - FloorTileItemMetalDiamond + - FloorTileItemFreezer + - FloorTileItemShowroom + - type: latheRecipePack id: FloorWoodTilesStatic recipes: @@ -93,6 +106,42 @@ recipes: - FloorTileItemWhiteMarble - FloorTileItemDarkMarble + - FloorTileItemUraniumMarble + - FloorTileItemPlasmaMarble + +- type: latheRecipePack + id: FloorShuttleTilesStatic + recipes: + - FloorTileItemShuttleWhite + - FloorTileItemShuttleBlue + - FloorTileItemShuttleOrange + - FloorTileItemShuttlePurple + - FloorTileItemShuttleRed + - FloorTileItemShuttleGrey + - FloorTileItemShuttleBlack + +- type: latheRecipePack + id: PlasticTilesStatic + recipes: + - FloorTileItemLino + - FloorTileItemBoxing + - FloorTileItemGym + +- type: latheRecipePack + id: CarpetTilesStatic + recipes: + - FloorTileItemArcadeBlue + - FloorTileItemArcadeBlue2 + - FloorTileItemArcadeRed + - FloorTileItemEighties + - FloorTileItemCarpetClown + - FloorTileItemCarpetOffice + +- type: latheRecipePack + id: PreciousTilesStatic + recipes: + - FloorTileItemGold + - FloorTileItemSilver ## Dynamic @@ -107,6 +156,7 @@ - FauxTileAstroIce - FauxTileAstroSnow - FauxTileAstroAsteroidSand + - FauxTileAstroAsteroidSandBorderless - FauxTileDesertAstroSand - FauxTileAstroIronsand - FauxTileAstroIronsandBorderless diff --git a/Resources/Prototypes/Recipes/Lathes/categories.yml b/Resources/Prototypes/Recipes/Lathes/categories.yml index fd25394e67..99c1ac30e5 100644 --- a/Resources/Prototypes/Recipes/Lathes/categories.yml +++ b/Resources/Prototypes/Recipes/Lathes/categories.yml @@ -114,6 +114,22 @@ id: Marble name: lathe-category-marble +- type: latheCategory + id: Shuttle + name: lathe-category-shuttle-tile + +- type: latheCategory + id: Plastic + name: lathe-category-plastic-tile + +- type: latheCategory + id: Precious + name: lathe-category-precious-tile + +- type: latheCategory + id: Industrial + name: lathe-category-industrial-tile + # Science - type: latheCategory id: Mech diff --git a/Resources/Prototypes/Recipes/Lathes/tiles.yml b/Resources/Prototypes/Recipes/Lathes/tiles.yml index e30174a6ae..29aa64cac5 100644 --- a/Resources/Prototypes/Recipes/Lathes/tiles.yml +++ b/Resources/Prototypes/Recipes/Lathes/tiles.yml @@ -42,6 +42,16 @@ - Tiles - Maints +- type: latheRecipe + abstract: true + parent: BaseSteelTileRecipe + id: BaseIndustrialTileRecipe + categories: + - Tiles + - Industrial + materials: + Steel: 50 + - type: latheRecipe abstract: true parent: BaseSteelTileRecipe @@ -92,6 +102,46 @@ Steel: 25 Glass: 25 +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BaseShuttleTileRecipe + categories: + - Tiles + - Shuttle + completetime: 1 + materials: + Plasteel: 25 + +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BasePlasticTileRecipe + categories: + - Tiles + - Plastic + materials: + Plastic: 25 + +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BaseCarpetTileRecipe + categories: + - Tiles + - Carpets + materials: + Cloth: 25 + +- type: latheRecipe + abstract: true + parent: BaseTileRecipe + id: BasePreciousTileRecipe + applyMaterialDiscount: false + categories: + - Tiles + - Precious + ## Recipes # Steel tiles @@ -313,6 +363,47 @@ id: FloorTileItemTechmaintDark result: FloorTileItemTechmaintDark +# Industrial +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemMining + result: FloorTileItemMining + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemMiningDark + result: FloorTileItemMiningDark + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemMiningLight + result: FloorTileItemMiningLight + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemElevatorShaft + result: FloorTileItemElevatorShaft + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemRockVault + result: FloorTileItemRockVault + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemMetalDiamond + result: FloorTileItemMetalDiamond + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemShowroom + result: FloorTileItemShowroom + +- type: latheRecipe + parent: BaseIndustrialTileRecipe + id: FloorTileItemFreezer + result: FloorTileItemFreezer + # Circuit - type: latheRecipe parent: BaseCircuitTileRecipe @@ -477,3 +568,119 @@ parent: BaseMarbleTileRecipe id: FloorTileItemDarkMarble result: FloorTileItemDarkMarble + +- type: latheRecipe + parent: BaseMarbleTileRecipe + id: FloorTileItemUraniumMarble + result: FloorTileItemUraniumMarble + materials: + Steel: 25 + Glass: 25 + Uranium: 25 + +- type: latheRecipe + parent: BaseMarbleTileRecipe + id: FloorTileItemPlasmaMarble + result: FloorTileItemPlasmaMarble + materials: + Steel: 25 + Glass: 25 + Plasma: 25 + +# Shuttle +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleWhite + result: FloorTileItemShuttleWhite + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleBlue + result: FloorTileItemShuttleBlue + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleOrange + result: FloorTileItemShuttleOrange + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttlePurple + result: FloorTileItemShuttlePurple + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleRed + result: FloorTileItemShuttleRed + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleGrey + result: FloorTileItemShuttleGrey + +- type: latheRecipe + parent: BaseShuttleTileRecipe + id: FloorTileItemShuttleBlack + result: FloorTileItemShuttleBlack + +# Precious +- type: latheRecipe + parent: BasePreciousTileRecipe + id: FloorTileItemGold + result: FloorTileItemGold + materials: + Gold: 50 + +- type: latheRecipe + parent: BasePreciousTileRecipe + id: FloorTileItemSilver + result: FloorTileItemSilver + materials: + Silver: 50 + +# Carpets +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemArcadeBlue + result: FloorTileItemArcadeBlue + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemArcadeBlue2 + result: FloorTileItemArcadeBlue2 + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemArcadeRed + result: FloorTileItemArcadeRed + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemEighties + result: FloorTileItemEighties + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemCarpetClown + result: FloorTileItemCarpetClown + +- type: latheRecipe + parent: BaseCarpetTileRecipe + id: FloorTileItemCarpetOffice + result: FloorTileItemCarpetOffice + +# Plastic +- type: latheRecipe + parent: BasePlasticTileRecipe + id: FloorTileItemLino + result: FloorTileItemLino + +- type: latheRecipe + parent: BasePlasticTileRecipe + id: FloorTileItemBoxing + result: FloorTileItemBoxing + +- type: latheRecipe + parent: BasePlasticTileRecipe + id: FloorTileItemGym + result: FloorTileItemGym diff --git a/Resources/Prototypes/Research/civilianservices.yml b/Resources/Prototypes/Research/civilianservices.yml index 622df1ba5e..b90649b438 100644 --- a/Resources/Prototypes/Research/civilianservices.yml +++ b/Resources/Prototypes/Research/civilianservices.yml @@ -103,6 +103,7 @@ - FauxTileAstroIce - FauxTileAstroSnow - FauxTileAstroAsteroidSand + - FauxTileAstroAsteroidSandBorderless - FauxTileDesertAstroSand - FauxTileAstroIronsand - FauxTileAstroIronsandBorderless From 0aa13a7dc2bec4f793e9368798f530c0323b13fa Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 5 Apr 2026 22:34:09 +0000 Subject: [PATCH 106/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 064fdc8e90..f70eb16b2f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4037,3 +4029,10 @@ 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 From a5e9f24f5bf700c371262d8750abd716b3d64d52 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Mon, 6 Apr 2026 19:30:41 +0200 Subject: [PATCH 107/247] Use dependencies for EntityQueries in Systems (Part 1: Client) (#43478) * cleanup * vsc lied to me * private readonly * use proxies --- Content.Client/Audio/AmbientSoundSystem.cs | 8 +- .../Audio/AmbientSoundTreeSystem.cs | 3 +- .../CardboardBox/CardboardBoxSystem.cs | 13 +-- Content.Client/Clickable/ClickableSystem.cs | 14 +-- Content.Client/DoAfter/DoAfterSystem.cs | 4 +- .../IconSmoothing/IconSmoothSystem.cs | 86 +++++++++---------- Content.Client/Interaction/DragDropSystem.cs | 5 +- .../Systems/ClientSpriteMovementSystem.cs | 5 +- .../Movement/Systems/FloorOcclusionSystem.cs | 4 +- Content.Client/Outline/TargetOutlineSystem.cs | 4 +- .../Systems/ChameleonProjectorSystem.cs | 8 +- .../ReplaySpectatorSystem.Movement.cs | 3 +- .../Silicons/Borgs/BorgSystem.Battery.cs | 5 -- Content.Client/Silicons/Borgs/BorgSystem.cs | 3 + Content.Client/Sprite/SpriteFadeSystem.cs | 14 +-- .../Visualizers/StickyVisualizerSystem.cs | 4 +- Content.Client/SubFloor/TrayScannerSystem.cs | 15 ++-- .../UserInterface/BuiPreTickUpdateSystem.cs | 10 +-- Content.Client/Verbs/VerbSystem.cs | 4 +- .../Melee/MeleeWeaponSystem.Effects.cs | 4 +- .../Weapons/Melee/MeleeWeaponSystem.cs | 6 +- Content.Client/Weather/WeatherSystem.cs | 10 +-- .../DoAfter/SharedDoAfterSystem.Update.cs | 26 +++--- Content.Shared/DoAfter/SharedDoAfterSystem.cs | 2 +- 24 files changed, 96 insertions(+), 164 deletions(-) diff --git a/Content.Client/Audio/AmbientSoundSystem.cs b/Content.Client/Audio/AmbientSoundSystem.cs index 9929751b22..92bfc677eb 100644 --- a/Content.Client/Audio/AmbientSoundSystem.cs +++ b/Content.Client/Audio/AmbientSoundSystem.cs @@ -235,8 +235,6 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem ///
private void ProcessNearbyAmbience(TransformComponent playerXform) { - var query = GetEntityQuery(); - var metaQuery = GetEntityQuery(); var mapPos = _xformSystem.GetMapCoordinates(playerXform); // Remove out-of-range ambiences @@ -249,9 +247,9 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem if (comp.Enabled && // Don't keep playing sounds that have changed since. sound.Sound == comp.Sound && - query.TryGetComponent(owner, out var xform) && + TryComp(owner, out TransformComponent? xform) && xform.MapID == playerXform.MapID && - !metaQuery.GetComponent(owner).EntityPaused) + !Paused(owner)) { // TODO: This is just trydistance for coordinates. var distance = (xform.ParentUid == playerXform.ParentUid) @@ -294,7 +292,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem var comp = sourceEntity.Comp; if (_playingSounds.ContainsKey(sourceEntity) || - metaQuery.GetComponent(uid).EntityPaused) + Paused(uid)) continue; var audioParams = _params diff --git a/Content.Client/Audio/AmbientSoundTreeSystem.cs b/Content.Client/Audio/AmbientSoundTreeSystem.cs index 7a9439c9df..2844374452 100644 --- a/Content.Client/Audio/AmbientSoundTreeSystem.cs +++ b/Content.Client/Audio/AmbientSoundTreeSystem.cs @@ -23,8 +23,7 @@ public sealed class AmbientSoundTreeSystem : ComponentTreeSystem()); + entry.Component.TreeUid.Value); return ExtractAabb(in entry, pos, default); } diff --git a/Content.Client/CardboardBox/CardboardBoxSystem.cs b/Content.Client/CardboardBox/CardboardBoxSystem.cs index ecebe16727..a179f14a3b 100644 --- a/Content.Client/CardboardBox/CardboardBoxSystem.cs +++ b/Content.Client/CardboardBox/CardboardBoxSystem.cs @@ -14,15 +14,12 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly SpriteSystem _sprite = default!; - - private EntityQuery _mobStateQuery; + [Dependency] private readonly EntityQuery _mobStateQuery = default!; public override void Initialize() { base.Initialize(); - _mobStateQuery = GetEntityQuery(); - SubscribeNetworkEvent(OnBoxEffect); } @@ -33,11 +30,7 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem if (!TryComp(source, out var box)) return; - var xformQuery = GetEntityQuery(); - - if (!xformQuery.TryGetComponent(source, out var xform)) - return; - + var xform = Transform(source); var sourcePos = _transform.GetMapCoordinates(source, xform); //Any mob that can move should be surprised? @@ -72,7 +65,7 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem var ent = Spawn(box.Effect, mapPos); - if (!xformQuery.TryGetComponent(ent, out var entTransform) || !TryComp(ent, out var sprite)) + if (!TryComp(ent, out TransformComponent? entTransform) || !TryComp(ent, out var sprite)) continue; _sprite.SetOffset((ent, sprite), new Vector2(0, 1)); diff --git a/Content.Client/Clickable/ClickableSystem.cs b/Content.Client/Clickable/ClickableSystem.cs index 8834f023f4..ae3487dc87 100644 --- a/Content.Client/Clickable/ClickableSystem.cs +++ b/Content.Client/Clickable/ClickableSystem.cs @@ -16,17 +16,9 @@ public sealed class ClickableSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transforms = default!; [Dependency] private readonly SpriteSystem _sprites = default!; - private EntityQuery _clickableQuery; - private EntityQuery _xformQuery; - private EntityQuery _fadingSpriteQuery; - - public override void Initialize() - { - base.Initialize(); - _clickableQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - _fadingSpriteQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _clickableQuery = default!; + [Dependency] private readonly EntityQuery _xformQuery = default!; + [Dependency] private readonly EntityQuery _fadingSpriteQuery = default!; ///
/// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding diff --git a/Content.Client/DoAfter/DoAfterSystem.cs b/Content.Client/DoAfter/DoAfterSystem.cs index 5802059537..407cf3e7c1 100644 --- a/Content.Client/DoAfter/DoAfterSystem.cs +++ b/Content.Client/DoAfter/DoAfterSystem.cs @@ -50,9 +50,7 @@ public sealed class DoAfterSystem : SharedDoAfterSystem var time = GameTiming.CurTime; var comp = Comp(playerEntity.Value); - var xformQuery = GetEntityQuery(); - var handsQuery = GetEntityQuery(); - Update(playerEntity.Value, active, comp, time, xformQuery, handsQuery); + Update(playerEntity.Value, active, comp, time); } /// diff --git a/Content.Client/IconSmoothing/IconSmoothSystem.cs b/Content.Client/IconSmoothing/IconSmoothSystem.cs index a1c47ca6be..4b8ec7be79 100644 --- a/Content.Client/IconSmoothing/IconSmoothSystem.cs +++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs @@ -18,6 +18,8 @@ namespace Content.Client.IconSmoothing { [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly EntityQuery _iconSmoothQuery = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; private readonly Queue _dirtyEntities = new(); private readonly Queue _anchorChangedEntities = new(); @@ -106,13 +108,10 @@ namespace Content.Client.IconSmoothing { base.FrameUpdate(frameTime); - var xformQuery = GetEntityQuery(); - var smoothQuery = GetEntityQuery(); - // first process anchor state changes. while (_anchorChangedEntities.TryDequeue(out var uid)) { - if (!xformQuery.TryGetComponent(uid, out var xform)) + if (!TryComp(uid, out TransformComponent? xform)) continue; if (xform.MapID == MapId.Nullspace) @@ -123,7 +122,7 @@ namespace Content.Client.IconSmoothing continue; } - DirtyNeighbours(uid, comp: null, xform, smoothQuery); + DirtyNeighbours(uid, comp: null, xform); } // Next, update actual sprites. @@ -132,19 +131,16 @@ namespace Content.Client.IconSmoothing _generation += 1; - var spriteQuery = GetEntityQuery(); - // Performance: This could be spread over multiple updates, or made parallel. while (_dirtyEntities.TryDequeue(out var uid)) { - CalculateNewSprite(uid, spriteQuery, smoothQuery, xformQuery); + CalculateNewSprite(uid); } } - public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, TransformComponent? transform = null, EntityQuery? smoothQuery = null) + public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, TransformComponent? transform = null) { - smoothQuery ??= GetEntityQuery(); - if (!smoothQuery.Value.Resolve(uid, ref comp) || !comp.Running) + if (!_iconSmoothQuery.Resolve(uid, ref comp) || !comp.Running) return; _dirtyEntities.Enqueue(uid); @@ -206,11 +202,7 @@ namespace Content.Client.IconSmoothing _anchorChangedEntities.Enqueue(uid); } - private void CalculateNewSprite(EntityUid uid, - EntityQuery spriteQuery, - EntityQuery smoothQuery, - EntityQuery xformQuery, - IconSmoothComponent? smooth = null) + private void CalculateNewSprite(EntityUid uid, IconSmoothComponent? smooth = null) { TransformComponent? xform; Entity? gridEntity = null; @@ -218,7 +210,7 @@ namespace Content.Client.IconSmoothing // The generation check prevents updating an entity multiple times per tick. // As it stands now, it's totally possible for something to get queued twice. // Generation on the component is set after an update so we can cull updates that happened this generation. - if (!smoothQuery.Resolve(uid, ref smooth, false) + if (!_iconSmoothQuery.Resolve(uid, ref smooth, false) || smooth.Mode == IconSmoothingMode.NoSprite || smooth.UpdateGeneration == _generation || !smooth.Enabled @@ -226,7 +218,7 @@ namespace Content.Client.IconSmoothing { if (smooth is { Enabled: true } && TryComp(uid, out var edge) && - xformQuery.TryGetComponent(uid, out xform)) + TryComp(uid, out xform)) { var directions = DirectionFlag.None; @@ -237,13 +229,13 @@ namespace Content.Client.IconSmoothing gridEntity = (gridUid, grid); - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)))) directions |= DirectionFlag.North; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)))) directions |= DirectionFlag.South; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)))) directions |= DirectionFlag.East; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)))) directions |= DirectionFlag.West; } @@ -253,10 +245,10 @@ namespace Content.Client.IconSmoothing return; } - xform = xformQuery.GetComponent(uid); + xform = Transform(uid); smooth.UpdateGeneration = _generation; - if (!spriteQuery.TryGetComponent(uid, out var sprite)) + if (!_spriteQuery.TryGetComponent(uid, out var sprite)) { Log.Error($"Encountered a icon-smoothing entity without a sprite: {ToPrettyString(uid)}"); RemCompDeferred(uid, smooth); @@ -281,13 +273,13 @@ namespace Content.Client.IconSmoothing switch (smooth.Mode) { case IconSmoothingMode.Corners: - CalculateNewSpriteCorners(gridEntity, smooth, spriteEnt, xform, smoothQuery); + CalculateNewSpriteCorners(gridEntity, smooth, spriteEnt, xform); break; case IconSmoothingMode.CardinalFlags: - CalculateNewSpriteCardinal(gridEntity, smooth, spriteEnt, xform, smoothQuery); + CalculateNewSpriteCardinal(gridEntity, smooth, spriteEnt, xform); break; case IconSmoothingMode.Diagonal: - CalculateNewSpriteDiagonal(gridEntity, smooth, spriteEnt, xform, smoothQuery); + CalculateNewSpriteDiagonal(gridEntity, smooth, spriteEnt, xform); break; default: throw new ArgumentOutOfRangeException(); @@ -295,7 +287,7 @@ namespace Content.Client.IconSmoothing } private void CalculateNewSpriteDiagonal(Entity? gridEntity, IconSmoothComponent smooth, - Entity sprite, TransformComponent xform, EntityQuery smoothQuery) + Entity sprite, TransformComponent xform) { if (gridEntity == null) { @@ -320,7 +312,7 @@ namespace Content.Client.IconSmoothing for (var i = 0; i < neighbors.Length; i++) { var neighbor = (Vector2i)rotation.RotateVec(neighbors[i]); - matching = matching && MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos + neighbor), smoothQuery); + matching = matching && MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos + neighbor)); } if (matching) @@ -333,7 +325,7 @@ namespace Content.Client.IconSmoothing } } - private void CalculateNewSpriteCardinal(Entity? gridEntity, IconSmoothComponent smooth, Entity sprite, TransformComponent xform, EntityQuery smoothQuery) + private void CalculateNewSpriteCardinal(Entity? gridEntity, IconSmoothComponent smooth, Entity sprite, TransformComponent xform) { var dirs = CardinalConnectDirs.None; @@ -347,13 +339,13 @@ namespace Content.Client.IconSmoothing var grid = gridEntity.Value.Comp; var pos = _mapSystem.TileIndicesFor(gridUid, grid, xform.Coordinates); - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)))) dirs |= CardinalConnectDirs.North; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)))) dirs |= CardinalConnectDirs.South; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)))) dirs |= CardinalConnectDirs.East; - if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery)) + if (MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)))) dirs |= CardinalConnectDirs.West; _sprite.LayerSetRsiState(sprite.AsNullable(), 0, $"{smooth.StateBase}{(int)dirs}"); @@ -372,11 +364,11 @@ namespace Content.Client.IconSmoothing CalculateEdge(sprite, directions, sprite); } - private bool MatchingEntity(IconSmoothComponent smooth, AnchoredEntitiesEnumerator candidates, EntityQuery smoothQuery) + private bool MatchingEntity(IconSmoothComponent smooth, AnchoredEntitiesEnumerator candidates) { while (candidates.MoveNext(out var entity)) { - if (smoothQuery.TryGetComponent(entity, out var other) && + if (_iconSmoothQuery.TryGetComponent(entity, out var other) && other.SmoothKey != null && (other.SmoothKey == smooth.SmoothKey || smooth.AdditionalKeys.Contains(other.SmoothKey)) && other.Enabled) @@ -388,11 +380,11 @@ namespace Content.Client.IconSmoothing return false; } - private void CalculateNewSpriteCorners(Entity? gridEntity, IconSmoothComponent smooth, Entity spriteEnt, TransformComponent xform, EntityQuery smoothQuery) + private void CalculateNewSpriteCorners(Entity? gridEntity, IconSmoothComponent smooth, Entity spriteEnt, TransformComponent xform) { var (cornerNE, cornerNW, cornerSW, cornerSE) = gridEntity == null ? (CornerFill.None, CornerFill.None, CornerFill.None, CornerFill.None) - : CalculateCornerFill(gridEntity.Value, smooth, xform, smoothQuery); + : CalculateCornerFill(gridEntity.Value, smooth, xform); // TODO figure out a better way to set multiple sprite layers. // This will currently re-calculate the sprite bounding box 4 times. @@ -422,20 +414,20 @@ namespace Content.Client.IconSmoothing CalculateEdge(spriteEnt, directions, sprite); } - private (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(Entity gridEntity, IconSmoothComponent smooth, TransformComponent xform, EntityQuery smoothQuery) + private (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(Entity gridEntity, IconSmoothComponent smooth, TransformComponent xform) { var gridUid = gridEntity.Owner; var grid = gridEntity.Comp; var pos = _mapSystem.TileIndicesFor(gridUid, grid, xform.Coordinates); - var n = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North)), smoothQuery); - var ne = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthEast)), smoothQuery); - var e = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East)), smoothQuery); - var se = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthEast)), smoothQuery); - var s = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South)), smoothQuery); - var sw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthWest)), smoothQuery); - var w = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West)), smoothQuery); - var nw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthWest)), smoothQuery); + var n = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.North))); + var ne = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthEast))); + var e = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.East))); + var se = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthEast))); + var s = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.South))); + var sw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.SouthWest))); + var w = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.West))); + var nw = MatchingEntity(smooth, _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, pos.Offset(Direction.NorthWest))); // ReSharper disable InconsistentNaming var cornerNE = CornerFill.None; diff --git a/Content.Client/Interaction/DragDropSystem.cs b/Content.Client/Interaction/DragDropSystem.cs index a1af9f9e20..7ff8dd150f 100644 --- a/Content.Client/Interaction/DragDropSystem.cs +++ b/Content.Client/Interaction/DragDropSystem.cs @@ -48,6 +48,7 @@ public sealed class DragDropSystem : SharedDragDropSystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; // how often to recheck possible targets (prevents calling expensive // check logic each update) @@ -433,11 +434,9 @@ public sealed class DragDropSystem : SharedDragDropSystem var bounds = new Box2(mousePos.Position - expansion, mousePos.Position + expansion); var pvsEntities = _lookup.GetEntitiesIntersecting(mousePos.MapId, bounds); - var spriteQuery = GetEntityQuery(); - foreach (var entity in pvsEntities) { - if (!spriteQuery.TryGetComponent(entity, out var inRangeSprite) || + if (!_spriteQuery.TryGetComponent(entity, out var inRangeSprite) || !inRangeSprite.Visible || entity == _draggedEntity) { diff --git a/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs b/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs index eb60e4fbb6..c806ae5aed 100644 --- a/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs +++ b/Content.Client/Movement/Systems/ClientSpriteMovementSystem.cs @@ -10,15 +10,12 @@ namespace Content.Client.Movement.Systems; public sealed class ClientSpriteMovementSystem : SharedSpriteMovementSystem { [Dependency] private readonly SpriteSystem _sprite = default!; - - private EntityQuery _spriteQuery; + [Dependency] private readonly EntityQuery _spriteQuery = default!; public override void Initialize() { base.Initialize(); - _spriteQuery = GetEntityQuery(); - SubscribeLocalEvent(OnAfterAutoHandleState); } diff --git a/Content.Client/Movement/Systems/FloorOcclusionSystem.cs b/Content.Client/Movement/Systems/FloorOcclusionSystem.cs index 6520d98ac9..3e1084bebb 100644 --- a/Content.Client/Movement/Systems/FloorOcclusionSystem.cs +++ b/Content.Client/Movement/Systems/FloorOcclusionSystem.cs @@ -12,14 +12,12 @@ public sealed class FloorOcclusionSystem : SharedFloorOcclusionSystem [Dependency] private readonly IPrototypeManager _proto = default!; - private EntityQuery _spriteQuery; + [Dependency] private readonly EntityQuery _spriteQuery = default!; public override void Initialize() { base.Initialize(); - _spriteQuery = GetEntityQuery(); - SubscribeLocalEvent(OnOcclusionStartup); SubscribeLocalEvent(OnOcclusionShutdown); SubscribeLocalEvent(OnOcclusionAuto); diff --git a/Content.Client/Outline/TargetOutlineSystem.cs b/Content.Client/Outline/TargetOutlineSystem.cs index 73d45a17a9..23752fbb9b 100644 --- a/Content.Client/Outline/TargetOutlineSystem.cs +++ b/Content.Client/Outline/TargetOutlineSystem.cs @@ -27,6 +27,7 @@ public sealed class TargetOutlineSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; private bool _enabled = false; @@ -130,11 +131,10 @@ public sealed class TargetOutlineSystem : EntitySystem var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition).Position; var bounds = new Box2(mousePos - LookupVector, mousePos + LookupVector); var pvsEntities = _lookup.GetEntitiesIntersecting(_eyeManager.CurrentEye.Position.MapId, bounds, LookupFlags.Approximate | LookupFlags.Static); - var spriteQuery = GetEntityQuery(); foreach (var entity in pvsEntities) { - if (!spriteQuery.TryGetComponent(entity, out var sprite) || !sprite.Visible) + if (!_spriteQuery.TryGetComponent(entity, out var sprite) || !sprite.Visible) continue; // Check the predicate diff --git a/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs b/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs index 1319a3d74e..f0cc9b2f59 100644 --- a/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs +++ b/Content.Client/Polymorph/Systems/ChameleonProjectorSystem.cs @@ -4,7 +4,6 @@ using Content.Shared.Chemistry.Components; using Content.Shared.Polymorph.Components; using Content.Shared.Polymorph.Systems; using Robust.Client.GameObjects; -using Robust.Shared.Player; namespace Content.Client.Polymorph.Systems; @@ -13,16 +12,13 @@ public sealed class ChameleonProjectorSystem : SharedChameleonProjectorSystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SpriteSystem _sprite = default!; - private EntityQuery _appearanceQuery; - private EntityQuery _spriteQuery; + [Dependency] private readonly EntityQuery _appearanceQuery = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; public override void Initialize() { base.Initialize(); - _appearanceQuery = GetEntityQuery(); - _spriteQuery = GetEntityQuery(); - SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent(OnStartup); diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs index e7d01713e5..3408a2af26 100644 --- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs +++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Movement.cs @@ -74,8 +74,7 @@ public sealed partial class ReplaySpectatorSystem if ((Direction & DirectionFlag.East) != 0) effectiveDir &= ~DirectionFlag.West; - var query = GetEntityQuery(); - var xform = query.GetComponent(player); + var xform = Transform(player); var pos = _transform.GetWorldPosition(xform); if (!xform.ParentUid.IsValid()) diff --git a/Content.Client/Silicons/Borgs/BorgSystem.Battery.cs b/Content.Client/Silicons/Borgs/BorgSystem.Battery.cs index 52398921d4..6b946f9cf8 100644 --- a/Content.Client/Silicons/Borgs/BorgSystem.Battery.cs +++ b/Content.Client/Silicons/Borgs/BorgSystem.Battery.cs @@ -13,16 +13,11 @@ public sealed partial class BorgSystem // Don't put this on the component because we only need to track the time for a single entity // and we don't want to TryComp it every single tick. private TimeSpan _nextAlertUpdate = TimeSpan.Zero; - private EntityQuery _chassisQuery; - private EntityQuery _slotQuery; public void InitializeBattery() { SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); - - _chassisQuery = GetEntityQuery(); - _slotQuery = GetEntityQuery(); } private void OnPlayerAttached(Entity ent, ref LocalPlayerAttachedEvent args) diff --git a/Content.Client/Silicons/Borgs/BorgSystem.cs b/Content.Client/Silicons/Borgs/BorgSystem.cs index 517027d082..09e6e387f7 100644 --- a/Content.Client/Silicons/Borgs/BorgSystem.cs +++ b/Content.Client/Silicons/Borgs/BorgSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Mobs; using Content.Shared.Power.EntitySystems; using Content.Shared.PowerCell; +using Content.Shared.PowerCell.Components; using Content.Shared.Silicons.Borgs; using Content.Shared.Silicons.Borgs.Components; using Robust.Client.GameObjects; @@ -22,6 +23,8 @@ public sealed partial class BorgSystem : SharedBorgSystem [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly EntityQuery _chassisQuery = default!; + [Dependency] private readonly EntityQuery _slotQuery = default!; public override void Initialize() { diff --git a/Content.Client/Sprite/SpriteFadeSystem.cs b/Content.Client/Sprite/SpriteFadeSystem.cs index b347411040..bdc49bd25f 100644 --- a/Content.Client/Sprite/SpriteFadeSystem.cs +++ b/Content.Client/Sprite/SpriteFadeSystem.cs @@ -27,16 +27,15 @@ public sealed class SpriteFadeSystem : EntitySystem [Dependency] private readonly IInputManager _inputManager = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SpriteSystem _sprite = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; + [Dependency] private readonly EntityQuery _fadeQuery = default!; + [Dependency] private readonly EntityQuery _fadingQuery = default!; + [Dependency] private readonly EntityQuery _fixturesQuery = default!; private List<(MapCoordinates Point, bool ExcludeBoundingBox)> _points = new(); private readonly HashSet _comps = new(); - private EntityQuery _spriteQuery; - private EntityQuery _fadeQuery; - private EntityQuery _fadingQuery; - private EntityQuery _fixturesQuery; - private const float TargetAlpha = 0.4f; private const float ChangeRate = 1f; @@ -44,11 +43,6 @@ public sealed class SpriteFadeSystem : EntitySystem { base.Initialize(); - _spriteQuery = GetEntityQuery(); - _fadeQuery = GetEntityQuery(); - _fadingQuery = GetEntityQuery(); - _fixturesQuery = GetEntityQuery(); - SubscribeLocalEvent(OnFadingShutdown); } diff --git a/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs b/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs index 85c6b8e066..4f58919d78 100644 --- a/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs +++ b/Content.Client/Sticky/Visualizers/StickyVisualizerSystem.cs @@ -5,14 +5,12 @@ namespace Content.Client.Sticky.Visualizers; public sealed class StickyVisualizerSystem : VisualizerSystem { - private EntityQuery _spriteQuery; + [Dependency] private readonly EntityQuery _spriteQuery = default!; public override void Initialize() { base.Initialize(); - _spriteQuery = GetEntityQuery(); - SubscribeLocalEvent(OnInit); } diff --git a/Content.Client/SubFloor/TrayScannerSystem.cs b/Content.Client/SubFloor/TrayScannerSystem.cs index 4c67890f6a..a23d67a98b 100644 --- a/Content.Client/SubFloor/TrayScannerSystem.cs +++ b/Content.Client/SubFloor/TrayScannerSystem.cs @@ -20,6 +20,8 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly TrayScanRevealSystem _trayScanReveal = default!; + [Dependency] private readonly EntityQuery _trayScannerQuery = default!; + [Dependency] private readonly EntityQuery _subFloorHideQuery = default!; private const string TRayAnimationKey = "trays"; private const double AnimationLength = 0.3; @@ -35,16 +37,14 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem // TODO: Multiple viewports or w/e var player = _player.LocalEntity; - var xformQuery = GetEntityQuery(); - if (!xformQuery.TryGetComponent(player, out var playerXform)) + if (!TryComp(player, out TransformComponent? playerXform)) return; - var playerPos = _transform.GetWorldPosition(playerXform, xformQuery); + var playerPos = _transform.GetWorldPosition(playerXform); var playerMap = playerXform.MapID; var range = 0f; HashSet> inRange; - var scannerQuery = GetEntityQuery(); // TODO: Should probably sub to player attached changes / inventory changes but inventory's // API is extremely skrungly. If this ever shows up on dottrace ping me and laugh. @@ -57,7 +57,7 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem { foreach (var ent in slot.ContainedEntities) { - if (!scannerQuery.TryGetComponent(ent, out var sneakScanner) || !sneakScanner.Enabled) + if (!_trayScannerQuery.TryGetComponent(ent, out var sneakScanner) || !sneakScanner.Enabled) continue; canSee = true; @@ -71,7 +71,7 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem if (!_hands.TryGetHeldItem(player.Value, hand, out var heldEntity)) continue; - if (!scannerQuery.TryGetComponent(heldEntity, out var heldScanner) || !heldScanner.Enabled) + if (!_trayScannerQuery.TryGetComponent(heldEntity, out var heldScanner) || !heldScanner.Enabled) continue; range = MathF.Max(heldScanner.Range, range); @@ -93,13 +93,12 @@ public sealed class TrayScannerSystem : SharedTrayScannerSystem } var revealedQuery = AllEntityQuery(); - var subfloorQuery = GetEntityQuery(); while (revealedQuery.MoveNext(out var uid, out _, out var sprite)) { // Revealing // Add buffer range to avoid flickers. - if (subfloorQuery.TryGetComponent(uid, out var subfloor) && + if (_subFloorHideQuery.TryGetComponent(uid, out var subfloor) && inRange.Contains((uid, subfloor))) { // Due to the fact client is predicting this server states will reset it constantly diff --git a/Content.Client/UserInterface/BuiPreTickUpdateSystem.cs b/Content.Client/UserInterface/BuiPreTickUpdateSystem.cs index 330cb51dcc..a25d89543d 100644 --- a/Content.Client/UserInterface/BuiPreTickUpdateSystem.cs +++ b/Content.Client/UserInterface/BuiPreTickUpdateSystem.cs @@ -33,15 +33,7 @@ public sealed class BuiPreTickUpdateSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = null!; [Dependency] private readonly UserInterfaceSystem _uiSystem = null!; [Dependency] private readonly IGameTiming _gameTiming = null!; - - private EntityQuery _userQuery; - - public override void Initialize() - { - base.Initialize(); - - _userQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _userQuery = default!; public void RunUpdates() { diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 77b8531888..e0bf99d1cb 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -34,6 +34,7 @@ namespace Content.Client.Verbs [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly EntityQuery _spriteQuery = default!; private float _lookupSize; @@ -159,10 +160,9 @@ namespace Content.Client.Verbs if (container == null && (visibility & MenuVisibility.InContainer) == 0) return entities.Count != 0; - var spriteQuery = GetEntityQuery(); for (var i = entities.Count - 1; i >= 0; i--) { - if (!spriteQuery.TryGetComponent(entities[i], out var spriteComponent) || !spriteComponent.Visible) + if (!_spriteQuery.TryGetComponent(entities[i], out var spriteComponent) || !spriteComponent.Visible) entities.RemoveSwap(i); } diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs index 8971508969..c792910fcf 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs @@ -32,7 +32,7 @@ public sealed partial class MeleeWeaponSystem if (localPos == Vector2.Zero || animation == null) return; - if (!_xformQuery.TryGetComponent(user, out var userXform) || userXform.MapID == MapId.Nullspace) + if (!TryComp(user, out TransformComponent? userXform) || userXform.MapID == MapId.Nullspace) return; var animationUid = Spawn(animation, userXform.Coordinates); @@ -64,7 +64,7 @@ public sealed partial class MeleeWeaponSystem } _sprite.SetRotation((animationUid, sprite), localPos.ToWorldAngle()); - var xform = _xformQuery.GetComponent(animationUid); + var xform = Transform(animationUid); TrackUserComponent track; switch (arcComponent.Animation) diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs index 420e18748f..5ecbaf63b8 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.cs @@ -35,14 +35,12 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem [Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; - private EntityQuery _xformQuery; - private const string MeleeLungeKey = "melee-lunge"; public override void Initialize() { base.Initialize(); - _xformQuery = GetEntityQuery(); + SubscribeNetworkEvent(OnMeleeLunge); UpdatesOutsidePrediction = true; } @@ -177,7 +175,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem private void ClientHeavyAttack(EntityUid user, EntityCoordinates coordinates, EntityUid meleeUid, MeleeWeaponComponent component) { // Only run on first prediction to avoid the potential raycast entities changing. - if (!_xformQuery.TryGetComponent(user, out var userXform) || + if (!TryComp(user, out TransformComponent? userXform) || !Timing.IsFirstTimePredicted) { return; diff --git a/Content.Client/Weather/WeatherSystem.cs b/Content.Client/Weather/WeatherSystem.cs index 4b63c05991..a7d8343fb2 100644 --- a/Content.Client/Weather/WeatherSystem.cs +++ b/Content.Client/Weather/WeatherSystem.cs @@ -20,19 +20,15 @@ public sealed class WeatherSystem : SharedWeatherSystem [Dependency] private readonly MapSystem _mapSystem = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - private EntityQuery _audioQuery; - private EntityQuery _gridQuery; - private EntityQuery _roofQuery; + [Dependency] private readonly EntityQuery _audioQuery = default!; + [Dependency] private readonly EntityQuery _gridQuery = default!; + [Dependency] private readonly EntityQuery _roofQuery = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentShutdown); - - _audioQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - _roofQuery = GetEntityQuery(); } private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index 14c2ae67d3..0b5902b5e3 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -17,6 +17,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem [Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] EntityQuery _handsQuery = default!; private DoAfter[] _doAfters = Array.Empty(); @@ -34,7 +35,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem try { - Update(uid, active, comp, time, xformQuery, handsQuery); + Update(uid, active, comp, time); } // ReSharper disable once RedundantCatchClause #if EXCEPTION_TOLERANCE @@ -87,9 +88,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem EntityUid uid, ActiveDoAfterComponent active, DoAfterComponent comp, - TimeSpan time, - EntityQuery xformQuery, - EntityQuery handsQuery) + TimeSpan time) { var dirty = false; @@ -122,7 +121,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem continue; } - if (ShouldCancel(doAfter, xformQuery, handsQuery)) + if (ShouldCancel(doAfter)) { InternalCancel(doAfter, comp); dirty = true; @@ -196,27 +195,24 @@ public abstract partial class SharedDoAfterSystem : EntitySystem } } - private bool ShouldCancel(DoAfter doAfter, - EntityQuery xformQuery, - EntityQuery handsQuery) + private bool ShouldCancel(DoAfter doAfter) { var args = doAfter.Args; - //re-using xformQuery for Exists() checks. - if (args.Used is { } used && !xformQuery.HasComponent(used)) + if (args.Used is { } used && !Exists(used)) return true; - if (args.EventTarget is { Valid: true } eventTarget && !xformQuery.HasComponent(eventTarget)) + if (args.EventTarget is { Valid: true } eventTarget && !Exists(eventTarget)) return true; - if (!xformQuery.TryGetComponent(args.User, out var userXform)) + if (!TryComp(args.User, out TransformComponent? userXform)) return true; TransformComponent? targetXform = null; - if (args.Target is { } target && !xformQuery.TryGetComponent(target, out targetXform)) + if (args.Target is { } target && !TryComp(target, out targetXform)) return true; - if (args.Used is { } @using && !xformQuery.HasComp(@using)) + if (args.Used is { } @using && !Exists(@using)) return true; // TODO: Re-use existing xform query for these calculations. @@ -265,7 +261,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem // This does not mean their hand needs to be empty. if (args.NeedHand) { - if (!handsQuery.TryGetComponent(args.User, out var hands) || hands.Count == 0) + if (!_handsQuery.TryGetComponent(args.User, out var hands) || hands.Count == 0) return true; // If an item was in the user's hand to begin with, diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs index 0b72692ea0..8841d8dd26 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -251,7 +251,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem doAfter.NetInitialItem = GetNetEntity(doAfter.InitialItem); // Initial checks - if (ShouldCancel(doAfter, GetEntityQuery(), GetEntityQuery())) + if (ShouldCancel(doAfter)) return false; if (args.AttemptFrequency == AttemptFrequency.StartAndEnd && !TryAttemptEvent(doAfter)) From 8187ade22cf4f6b1df103d52fc6fa8aeb8280690 Mon Sep 17 00:00:00 2001 From: ProPeperos <61984624+ProPeperos@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:20:27 +0200 Subject: [PATCH 108/247] Elk Evac Shuttle - 2 Fixes (#43481) Fix to elk evac shuttle --- Resources/Maps/Shuttles/emergency_delta.yml | 219 +++++--------------- 1 file changed, 51 insertions(+), 168 deletions(-) diff --git a/Resources/Maps/Shuttles/emergency_delta.yml b/Resources/Maps/Shuttles/emergency_delta.yml index c2d2628fb9..1797ce0ecb 100644 --- a/Resources/Maps/Shuttles/emergency_delta.yml +++ b/Resources/Maps/Shuttles/emergency_delta.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Grid - engineVersion: 266.0.0 + engineVersion: 275.2.0 forkId: "" forkVersion: "" - time: 09/17/2025 04:14:52 - entityCount: 957 + time: 04/05/2026 13:24:37 + entityCount: 959 maps: [] grids: - 1 @@ -516,6 +516,9 @@ entities: - type: GasTileOverlay - type: RadiationGridResistance - type: ImplicitRoof + - type: TileHistory + chunkHistory: {} + - type: ExplosionAirtightGrid - proto: AirCanister entities: - uid: 326 @@ -523,10 +526,10 @@ entities: - type: Transform pos: -13.5,-3.5 parent: 1 - - uid: 445 + - uid: 643 components: - type: Transform - pos: -1.5,-18.5 + pos: 0.5,-20.5 parent: 1 - proto: AirlockBrigGlassLocked entities: @@ -735,6 +738,14 @@ entities: parent: 1 - type: Fixtures fixtures: {} + - uid: 795 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,0.5 + parent: 1 + - type: Fixtures + fixtures: {} - proto: AtmosDeviceFanDirectional entities: - uid: 71 @@ -1714,6 +1725,11 @@ entities: - type: Transform pos: -2.5,-22.5 parent: 1 + - uid: 802 + components: + - type: Transform + pos: -9.5,0.5 + parent: 1 - proto: CableHV entities: - uid: 33 @@ -2223,6 +2239,16 @@ entities: - type: Transform pos: -4.5,-17.5 parent: 1 + - uid: 958 + components: + - type: Transform + pos: -9.5,0.5 + parent: 1 + - uid: 959 + components: + - type: Transform + pos: -8.5,0.5 + parent: 1 - proto: CableTerminal entities: - uid: 277 @@ -3134,11 +3160,11 @@ entities: parent: 1 - proto: GasOutletInjector entities: - - uid: 801 + - uid: 687 components: - type: Transform rot: -1.5707963267948966 rad - pos: 1.5,-18.5 + pos: 1.5,-20.5 parent: 1 - proto: GasPassiveVent entities: @@ -3146,10 +3172,16 @@ entities: components: - type: Transform rot: -1.5707963267948966 rad - pos: 1.5,-20.5 + pos: 1.5,-19.5 parent: 1 - proto: GasPipeBend entities: + - uid: 680 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-20.5 + parent: 1 - uid: 856 components: - type: Transform @@ -3181,7 +3213,7 @@ entities: components: - type: Transform rot: -1.5707963267948966 rad - pos: 0.5,-20.5 + pos: -0.5,-19.5 parent: 1 - uid: 514 components: @@ -3189,45 +3221,28 @@ entities: rot: 3.141592653589793 rad pos: -2.5,-16.5 parent: 1 - - uid: 643 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-20.5 - parent: 1 - uid: 644 components: - type: Transform rot: -1.5707963267948966 rad - pos: -0.5,-20.5 + pos: -1.5,-19.5 parent: 1 - uid: 675 components: - type: Transform pos: -2.5,-17.5 parent: 1 - - uid: 680 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-18.5 - parent: 1 - uid: 699 components: - type: Transform - pos: -2.5,-19.5 + rot: -1.5707963267948966 rad + pos: 0.5,-19.5 parent: 1 - uid: 707 components: - type: Transform pos: -2.5,-18.5 parent: 1 - - uid: 802 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-18.5 - parent: 1 - uid: 840 components: - type: Transform @@ -3627,11 +3642,11 @@ entities: parent: 1 - proto: GasPipeTJunction entities: - - uid: 795 + - uid: 445 components: - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-20.5 + rot: 1.5707963267948966 rad + pos: -2.5,-19.5 parent: 1 - uid: 841 components: @@ -3741,11 +3756,11 @@ entities: parent: 1 - proto: GasPort entities: - - uid: 687 + - uid: 460 components: - type: Transform rot: 1.5707963267948966 rad - pos: -1.5,-18.5 + pos: 0.5,-20.5 parent: 1 - proto: GasVentPump entities: @@ -4270,32 +4285,24 @@ entities: rot: -1.5707963267948966 rad pos: 1.5,-18.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 119 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-20.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 130 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-19.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 293 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-21.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - proto: PosterLegitEnlist entities: - uid: 788 @@ -4657,393 +4664,281 @@ entities: - type: Transform pos: 2.5,1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 7 components: - type: Transform pos: 2.5,2.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 15 components: - type: Transform pos: -3.5,1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 18 components: - type: Transform pos: 0.5,3.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 19 components: - type: Transform pos: -0.5,3.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 81 components: - type: Transform pos: -0.5,-1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 82 components: - type: Transform pos: -1.5,-2.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 101 components: - type: Transform pos: -12.5,3.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 142 components: - type: Transform pos: -4.5,-1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 170 components: - type: Transform pos: -1.5,6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 171 components: - type: Transform pos: -10.5,6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 173 components: - type: Transform pos: -10.5,7.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 174 components: - type: Transform pos: -1.5,7.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 182 components: - type: Transform pos: -8.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 183 components: - type: Transform pos: -3.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 184 components: - type: Transform pos: -4.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 185 components: - type: Transform pos: -5.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 186 components: - type: Transform pos: -6.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 187 components: - type: Transform pos: -7.5,9.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 261 components: - type: Transform pos: -14.5,-3.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 262 components: - type: Transform pos: -14.5,-4.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 263 components: - type: Transform pos: -14.5,-5.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 264 components: - type: Transform pos: -14.5,-6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 265 components: - type: Transform pos: -14.5,-7.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 267 components: - type: Transform pos: -1.5,-22.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 274 components: - type: Transform pos: -6.5,-1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 327 components: - type: Transform pos: -2.5,-22.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 330 components: - type: Transform pos: -9.5,-11.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 331 components: - type: Transform pos: -9.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 338 components: - type: Transform pos: -11.5,-17.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 339 components: - type: Transform pos: -12.5,-17.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 342 components: - type: Transform pos: -14.5,-12.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 343 components: - type: Transform pos: -14.5,-13.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 344 components: - type: Transform pos: -14.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 345 components: - type: Transform pos: -14.5,-15.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 430 components: - type: Transform pos: -8.5,-17.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 432 components: - type: Transform pos: -6.5,-17.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 469 components: - type: Transform pos: 2.5,-4.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 470 components: - type: Transform pos: 2.5,-5.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 471 components: - type: Transform pos: 2.5,-6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 478 components: - type: Transform pos: 2.5,-13.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 479 components: - type: Transform pos: 2.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 513 components: - type: Transform pos: -1.5,-8.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 515 components: - type: Transform pos: -1.5,-4.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 530 components: - type: Transform pos: -5.5,-10.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 533 components: - type: Transform pos: -1.5,-10.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 535 components: - type: Transform pos: -1.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 557 components: - type: Transform pos: -5.5,-4.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 712 components: - type: Transform pos: -8.5,-1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 760 components: - type: Transform pos: -9.5,-7.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 764 components: - type: Transform pos: -9.5,-6.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 817 components: - type: Transform pos: -5.5,-8.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 819 components: - type: Transform pos: -5.5,-14.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 820 components: - type: Transform pos: -5.5,-16.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 821 components: - type: Transform pos: -1.5,-16.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 837 components: - type: Transform pos: -5.5,-2.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - proto: SignBridge entities: - uid: 11 @@ -5064,10 +4959,10 @@ entities: fixtures: {} - proto: SignEVA entities: - - uid: 460 + - uid: 801 components: - type: Transform - pos: -9.5,0.5 + pos: -9.5,2.5 parent: 1 - type: Fixtures fixtures: {} @@ -5952,8 +5847,6 @@ entities: rot: 1.5707963267948966 rad pos: -1.5,-20.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - proto: WindoorSecureSecurityLocked entities: - uid: 37 @@ -5962,8 +5855,6 @@ entities: rot: 3.141592653589793 rad pos: -2.5,1.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - proto: WindowReinforcedDirectional entities: - uid: 660 @@ -5972,30 +5863,22 @@ entities: rot: 1.5707963267948966 rad pos: -1.5,-19.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 682 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,-18.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 746 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,-21.5 parent: 1 - - type: DeltaPressure - gridUid: 1 - uid: 755 components: - type: Transform rot: -1.5707963267948966 rad pos: -1.5,2.5 parent: 1 - - type: DeltaPressure - gridUid: 1 ... From 59663fc9b1519c334da40ddfef7afdfb2a6ec30a Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 6 Apr 2026 18:35:57 +0000 Subject: [PATCH 109/247] Automatic changelog update --- Resources/Changelog/Maps.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Maps.yml b/Resources/Changelog/Maps.yml index b971abf643..df01e9ce16 100644 --- a/Resources/Changelog/Maps.yml +++ b/Resources/Changelog/Maps.yml @@ -1078,4 +1078,13 @@ id: 132 time: '2026-03-24T21:43:08.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/43046 +- author: ProPeperos + changes: + - message: On Fland Evac shuttle (Delta) Added missing APC in the upper left room + type: Tweak + - message: On Fland Evac shuttle (Delta) Moved air canister connector + type: Tweak + id: 133 + time: '2026-04-06T18:34:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/43481 Order: 2 From a4594108d5e5f5abd3e5fbb06d89a1054d770217 Mon Sep 17 00:00:00 2001 From: psykana <36602558+psykana@users.noreply.github.com> Date: Mon, 6 Apr 2026 22:26:08 +0100 Subject: [PATCH 110/247] Add nuke disk's location to nukeops greentext (#39767) * Add nuke disk's location to nukeops greentext * robust --- .../GameTicking/Rules/NukeopsRuleSystem.cs | 154 +++++++++++++++++- .../game-presets/preset-nukeops.ftl | 13 +- 2 files changed, 163 insertions(+), 4 deletions(-) diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 0dc906738f..086aeceaef 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -3,31 +3,45 @@ using Content.Server.Communications; using Content.Server.GameTicking.Rules.Components; using Content.Server.Nuke; using Content.Server.NukeOps; +using Content.Server.Pinpointer; using Content.Server.Popups; using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Shuttles.Events; using Content.Server.Shuttles.Systems; using Content.Server.Station.Components; +using Content.Server.StationRecords.Systems; using Content.Server.Store.Systems; +using Content.Shared.Access.Systems; using Content.Shared.GameTicking.Components; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.NPC.Components; using Content.Shared.NPC.Systems; using Content.Shared.Nuke; using Content.Shared.NukeOps; +using Content.Shared.Roles; using Content.Shared.Roles.Components; +using Content.Shared.Roles.Jobs; +using Content.Shared.Station; +using Content.Shared.Station.Components; +using Content.Shared.StationRecords; using Content.Shared.Store; +using Content.Shared.Store.Components; using Content.Shared.Tag; using Content.Shared.Zombies; +using Robust.Server.Player; +using Robust.Shared.Containers; using Robust.Shared.Map; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; +using System.Data; using System.Linq; -using Content.Shared.Station.Components; -using Content.Shared.Store.Components; -using Robust.Shared.Prototypes; +using System.Text; namespace Content.Server.GameTicking.Rules; @@ -35,9 +49,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!; @@ -105,6 +128,63 @@ public sealed class NukeopsRuleSystem : GameRuleSystem args.AddLine(text); } + // Print disk location if nuke didn't explode and is not armed + List diskWinConditions = [WinCondition.NukeDiskOnCentCom, WinCondition.NukeDiskNotOnCentCom]; + if (component.WinConditions.Any(diskWinConditions.Contains)) + { + var diskQuery = AllEntityQuery(); + while (diskQuery.MoveNext(out var diskUid, out _, out var transform)) + { + StringBuilder text = new StringBuilder(Loc.GetString("nukeops-disk-location-title")); + + List containers = new List(); + bool carriedByMob = false; + + var tempParent = diskUid; + while (_containers.TryGetContainingContainer((tempParent, null), out var container) && !carriedByMob) + { + if (HasComp(container.Owner)) + { + carriedByMob = true; + } + var containermeta = MetaData(container.Owner); + containers.Add(containermeta.EntityName); + tempParent = container.Owner; + } + + string location = FormattedMessage.RemoveMarkupOrThrow(_navMap.GetNearestBeaconString((diskUid, transform))); + + if (carriedByMob) + { + GetDiskCarrierData(tempParent, out var name, out var job, out var username); + text.Append(Loc.GetString("nukeops-disk-carried-by", + ("name", name), + ("job", job), + ("user", username), + ("location", location))); + } + else + { + if (containers.Count > 0) + { + string hierarchy = string.Empty; + for (var i = 0; i < containers.Count; i++) + { + hierarchy = (Loc.GetString( + "storage-hierarchy-list", + ("item", containers[i]), + ("existing-text", hierarchy), + ("items-left", containers.Count - i - 1))); + } + text.Append(hierarchy); + } + text.Append(" "); + text.Append(location); + } + args.AddLine(text.ToString()); + } + } + args.AddLine(Loc.GetString("nukeops-list-start")); var antags = _antag.GetAntagIdentifiers(uid); @@ -550,6 +630,74 @@ public sealed class NukeopsRuleSystem : GameRuleSystem return null; } + + private void GetDiskCarrierData(EntityUid carrier, + out string name, + out string job, + out string username) + { + name = Name(carrier); + job = Loc.GetString("job-name-unknown"); + username = "unknown"; // magic word in Fluent selector + + Entity? mind = null; + + if (_mind.TryGetMind(carrier, out _, out var mindComp)) + { + mind = (carrier, mindComp); + } + else + { + var allMinds = EntityQueryEnumerator(); + while (allMinds.MoveNext(out _, out mindComp)) + { + if (mindComp.CharacterName != name) + continue; + + mind = (carrier, mindComp); + break; + } + } + + if (mind is not null) + { + NetUserId? userId = mind.Value.Comp.UserId; + if (userId is not null && _player.TryGetPlayerData(userId.Value, out var sessionData)) + username = sessionData.UserName; + + // Role/job is the trickiest since it can be unknown in some cases + // For example, after "make ghost role" verb + var roles = _roles.MindGetAllRoleInfo(mind.Value.Owner); + if (roles.Count > 0) + { + job = Loc.GetString(roles.First().Name); + return; + } + + if (_jobs.MindTryGetJobName(mind, out var jobName)) + { + job = jobName; + return; + } + } + + // Try station records + var xform = Transform(carrier); + var station = _station.GetStationInMap(xform.MapID); + if (station != null && _records.GetRecordByName(station.Value, name) is { } id) + { + var key = new StationRecordKey(id, station.Value); + if (_records.TryGetRecord(key, out var record)) + { + job = record.JobTitle; + return; + } + } + + // Fallback to ID + if (_idCard.TryFindIdCard(carrier, out var idCard)) + job = idCard.Comp.LocalizedJobTitle ?? job; + } } /// diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl index 1343aaec7e..da1794badb 100644 --- a/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-nukeops.ftl @@ -1,4 +1,4 @@ -nukeops-title = Nuclear Operatives +nukeops-title = Nuclear Operatives nukeops-description = Nuclear operatives have targeted the station. Try to keep them from arming and detonating the nuke by protecting the nuke disk! nukeops-welcome = @@ -24,6 +24,17 @@ nukeops-cond-allnukiesdead = All nuclear operatives have died. nukeops-cond-somenukiesalive = Some nuclear operatives died. nukeops-cond-allnukiesalive = No nuclear operatives died. +nukeops-disk-location-title = Final location of Disk: +nukeops-disk-carried-by = {" "}carried by [color=White]{$name}[/color], [color=orange]{$job}[/color], {$location} { $user -> + [unknown] { "" } + *[other] ([color=gray]{$user}[/color]) +} + +storage-hierarchy-list = { $items-left -> + [0] { $existing-text } { $item }, + *[other] { $existing-text } { $item }, in +} + nukeops-list-start = The nuclear operatives were: nukeops-list-name = - [color=White]{$name}[/color] nukeops-list-name-user = - [color=White]{$name}[/color] ([color=gray]{$user}[/color]) From ee3bf6fe08af33f94607a35026d92b75309ce8c9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 6 Apr 2026 21:42:25 +0000 Subject: [PATCH 111/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f70eb16b2f..4a704decc0 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,17 +1,4 @@ Entries: -- 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 @@ -4036,3 +4023,11 @@ 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 From 6f83236c1d11102f2dd819df016453045079a434 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Mon, 6 Apr 2026 15:08:35 -0700 Subject: [PATCH 112/247] KILL THE AI [New Traitor Objective] (#43462) * KILL THE AI * cleanup and review * fix :( * Make it about as common as "KillRandomHead" * me when I forget to fucking push * remove AI dependency * move to AI system * whtiespace * asfsaf --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Cloning/RandomCloneSpawnerSystem.cs | 5 +- .../GameTicking/GameTicker.Spawning.cs | 10 +-- .../Rules/ParadoxCloneRuleSystem.cs | 11 +-- .../GameTicking/Rules/SurvivorRuleSystem.cs | 12 ++-- .../GameTicking/Rules/XenoborgsRuleSystem.cs | 10 +-- .../Mind/Filters/TargetObjectiveMindFilter.cs | 2 +- .../Components/PickRandomPersonComponent.cs | 2 +- .../Systems/PickObjectiveTargetSystem.cs | 11 +-- Content.Shared/Magic/SharedMagicSystem.cs | 4 +- Content.Shared/Mind/Filters/AliveAiPool.cs | 16 +++++ .../Mind/Filters/AliveHumansPool.cs | 10 +-- .../Mind/Filters/AntagonistMindFilter.cs | 2 +- Content.Shared/Mind/Filters/BodyMindFilter.cs | 2 +- .../Mind/Filters/HasRoleMindFilter.cs | 2 +- Content.Shared/Mind/Filters/JobMindFilter.cs | 2 +- Content.Shared/Mind/Filters/MindFilter.cs | 10 +-- .../Filters/{IMindPool.cs => MindPool.cs} | 10 +-- .../Mind/Filters/ObjectiveMindFilter.cs | 2 +- Content.Shared/Mind/SharedMindSystem.cs | 57 ++------------- .../Objectives/Systems/TargetSystem.cs | 66 ++++++++++++++++++ .../StationAi/SharedStationAiSystem.cs | 23 ++++++ .../objectives/conditions/kill-person.ftl | 1 + .../Prototypes/Objectives/objectiveGroups.yml | 1 + Resources/Prototypes/Objectives/traitor.yml | 25 +++++++ .../Mobs/Silicon/station_ai.rsi/broken.png | Bin 0 -> 8017 bytes .../Mobs/Silicon/station_ai.rsi/meta.json | 3 + 26 files changed, 200 insertions(+), 99 deletions(-) create mode 100644 Content.Shared/Mind/Filters/AliveAiPool.cs rename Content.Shared/Mind/Filters/{IMindPool.cs => MindPool.cs} (51%) create mode 100644 Content.Shared/Objectives/Systems/TargetSystem.cs create mode 100644 Resources/Textures/Mobs/Silicon/station_ai.rsi/broken.png diff --git a/Content.Server/Cloning/RandomCloneSpawnerSystem.cs b/Content.Server/Cloning/RandomCloneSpawnerSystem.cs index a645a10890..a18ccd2e71 100644 --- a/Content.Server/Cloning/RandomCloneSpawnerSystem.cs +++ b/Content.Server/Cloning/RandomCloneSpawnerSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Cloning.Components; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; +using Content.Shared.Objectives.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -15,7 +16,7 @@ public sealed class RandomCloneSpawnerSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly TargetSystem _target = default!; public override void Initialize() { @@ -34,7 +35,7 @@ public sealed class RandomCloneSpawnerSystem : EntitySystem return; } - var allHumans = _mind.GetAliveHumans(); + var allHumans = _target.GetAliveHumans(); if (allHumans.Count == 0) return; diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 5f99123db3..4b00dc3143 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -347,17 +347,17 @@ namespace Content.Server.GameTicking DebugTools.AssertNotNull(data); - var newMind = _mind.CreateMind(data!.UserId, character.Name); - _mind.SetUserId(newMind, data.UserId); - jobPrototype = _prototypeManager.Index(jobId); - _playTimeTrackings.PlayerRolesChanged(player); - var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, jobId, character); DebugTools.AssertNotNull(mobMaybe); mob = mobMaybe!.Value; + var newMind = _mind.CreateMind(data.UserId, Name(mob)); + _mind.SetUserId(newMind, data.UserId); + + _playTimeTrackings.PlayerRolesChanged(player); + _mind.TransferTo(newMind, mob); _roles.MindAddJobRole(newMind, silent: silent, jobPrototype: jobId); diff --git a/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs b/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs index ab8864caaa..7eb076d0d5 100644 --- a/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs @@ -7,17 +7,20 @@ using Content.Shared.GameTicking.Components; using Content.Shared.Gibbing.Components; using Content.Shared.Medical.SuitSensor; using Content.Shared.Mind; +using Content.Shared.Objectives.Systems; +using Content.Shared.Random.Helpers; using Robust.Shared.Random; namespace Content.Server.GameTicking.Rules; public sealed class ParadoxCloneRuleSystem : GameRuleSystem { - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly CloningSystem _cloning = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SuitSensorSystem _sensor = default!; + [Dependency] private readonly TargetSystem _target = default!; public override void Initialize() { @@ -32,7 +35,7 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem { - [Dependency] private readonly RoleSystem _role = default!; - [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; - [Dependency] private readonly TransformSystem _xform = default!; [Dependency] private readonly EmergencyShuttleSystem _eShuttle = default!; - [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly RoleSystem _role = default!; + [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly TargetSystem _target = default!; + [Dependency] private readonly TransformSystem _xform = default!; private static readonly ProtoId InvalidForSurvivorAntagTag = "InvalidForSurvivorAntag"; @@ -38,7 +38,7 @@ public sealed class SurvivorRuleSystem : GameRuleSystem { base.Started(uid, component, gameRule, args); - var allAliveHumanMinds = _mind.GetAliveHumans(); + var allAliveHumanMinds = _target.GetAliveHumans(); foreach (var humanMind in allAliveHumanMinds) { diff --git a/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs index 0b6a700d11..932774e9a1 100644 --- a/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/XenoborgsRuleSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Destructible; using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; +using Content.Shared.Objectives.Systems; using Content.Shared.Xenoborgs.Components; using Robust.Shared.Timing; @@ -14,13 +15,14 @@ namespace Content.Server.GameTicking.Rules; public sealed class XenoborgsRuleSystem : GameRuleSystem { + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly ChatSystem _chatSystem = default!; [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!; + [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly TargetSystem _target = default!; private static readonly Color AnnouncmentColor = Color.Gold; @@ -53,7 +55,7 @@ public sealed class XenoborgsRuleSystem : GameRuleSystem base.AppendRoundEndText(uid, component, gameRule, ref args); var numXenoborgs = GetNumberXenoborgs(); - var numHumans = _mindSystem.GetAliveHumans().Count; + var numHumans = _target.GetAliveHumans().Count; if (numXenoborgs < 5) args.AddLine(Loc.GetString("xenoborgs-crewmajor")); @@ -96,7 +98,7 @@ public sealed class XenoborgsRuleSystem : GameRuleSystem private void CheckRoundEnd(XenoborgsRuleComponent xenoborgsRuleComponent) { var numXenoborgs = GetNumberXenoborgs(); - var numHumans = _mindSystem.GetAliveHumans().Count; + var numHumans = _target.GetAliveHumans().Count; xenoborgsRuleComponent.MaxNumberXenoborgs = Math.Max(xenoborgsRuleComponent.MaxNumberXenoborgs, numXenoborgs); diff --git a/Content.Server/Mind/Filters/TargetObjectiveMindFilter.cs b/Content.Server/Mind/Filters/TargetObjectiveMindFilter.cs index 6fc031d7c1..975f4c19bd 100644 --- a/Content.Server/Mind/Filters/TargetObjectiveMindFilter.cs +++ b/Content.Server/Mind/Filters/TargetObjectiveMindFilter.cs @@ -20,7 +20,7 @@ public sealed partial class TargetObjectiveMindFilter : MindFilter [DataField] public EntityWhitelist? Blacklist; - protected override bool ShouldRemove(Entity mind, EntityUid? excluded, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? excluded, IEntityManager entMan) { // ignore this filter if there is no user to check if (!entMan.TryGetComponent(excluded, out var excludedMind)) diff --git a/Content.Server/Objectives/Components/PickRandomPersonComponent.cs b/Content.Server/Objectives/Components/PickRandomPersonComponent.cs index 2c864a80d4..16e539f320 100644 --- a/Content.Server/Objectives/Components/PickRandomPersonComponent.cs +++ b/Content.Server/Objectives/Components/PickRandomPersonComponent.cs @@ -16,7 +16,7 @@ public sealed partial class PickRandomPersonComponent : Component /// A pool to pick potential targets from. /// [DataField] - public IMindPool Pool = new AliveHumansPool(); + public MindPool Pool = new AliveHumansPool(); /// /// Filters to apply to . diff --git a/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs index b3075b2274..5b48b25ad4 100644 --- a/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs +++ b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs @@ -5,6 +5,7 @@ using Content.Server.GameTicking.Rules; using Content.Server.Revolutionary.Components; using Robust.Shared.Random; using System.Linq; +using Content.Shared.Objectives.Systems; namespace Content.Server.Objectives.Systems; @@ -14,8 +15,8 @@ namespace Content.Server.Objectives.Systems; /// public sealed class PickObjectiveTargetSystem : EntitySystem { - [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly TargetObjectiveSystem _objective = default!; + [Dependency] private readonly TargetSystem _target = default!; public override void Initialize() { @@ -51,7 +52,7 @@ public sealed class PickObjectiveTargetSystem : EntitySystem return; } - _target.SetTarget(ent.Owner, targetComp.Target.Value); + _objective.SetTarget(ent.Owner, targetComp.Target.Value); } private void OnRandomPersonAssigned(Entity ent, ref ObjectiveAssignedEvent args) @@ -68,12 +69,12 @@ public sealed class PickObjectiveTargetSystem : EntitySystem return; // couldn't find a target :( - if (_mind.PickFromPool(ent.Comp.Pool, ent.Comp.Filters, args.MindId) is not {} picked) + if (_target.PickFromPool(ent.Comp.Pool, ent.Comp.Filters, args.MindId) is not {} picked) { args.Cancelled = true; return; } - _target.SetTarget(ent, picked, target); + _objective.SetTarget(ent, picked, target); } } diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 82cae19ec1..62d3dbdbd4 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Magic.Components; using Content.Shared.Magic.Events; using Content.Shared.Maps; using Content.Shared.Mind; +using Content.Shared.Objectives.Systems; using Content.Shared.Physics; using Content.Shared.Popups; using Content.Shared.Speech.Muting; @@ -67,6 +68,7 @@ public abstract class SharedMagicSystem : EntitySystem [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly SharedChargesSystem _charges = default!; [Dependency] private readonly ExamineSystemShared _examine= default!; + [Dependency] private readonly TargetSystem _target = default!; private static readonly ProtoId InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell"; @@ -472,7 +474,7 @@ public abstract class SharedMagicSystem : EntitySystem ev.Handled = true; - var allHumans = _mind.GetAliveHumans(); + var allHumans = _target.GetAliveHumans(); foreach (var human in allHumans) { diff --git a/Content.Shared/Mind/Filters/AliveAiPool.cs b/Content.Shared/Mind/Filters/AliveAiPool.cs new file mode 100644 index 0000000000..88fd557bbf --- /dev/null +++ b/Content.Shared/Mind/Filters/AliveAiPool.cs @@ -0,0 +1,16 @@ +using Content.Shared.Objectives.Systems; +using Content.Shared.Silicons.StationAi; + +namespace Content.Shared.Mind.Filters; + +/// +/// A mind pool that uses . +/// +public sealed partial class AliveAiPool : MindPool +{ + public override void FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, TargetSystem targetSys) + { + var aiSys = entMan.System(); + aiSys.AddAliveAis(minds, exclude); + } +} diff --git a/Content.Shared/Mind/Filters/AliveHumansPool.cs b/Content.Shared/Mind/Filters/AliveHumansPool.cs index c8e5c55bae..1dfa2d2379 100644 --- a/Content.Shared/Mind/Filters/AliveHumansPool.cs +++ b/Content.Shared/Mind/Filters/AliveHumansPool.cs @@ -1,12 +1,14 @@ +using Content.Shared.Objectives.Systems; + namespace Content.Shared.Mind.Filters; /// -/// A mind pool that uses . +/// A mind pool that uses . /// -public sealed partial class AliveHumansPool : IMindPool +public sealed partial class AliveHumansPool : MindPool { - void IMindPool.FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + public override void FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, TargetSystem targetSystem) { - mindSys.AddAliveHumans(minds, exclude); + targetSystem.AddAliveHumans(minds, exclude); } } diff --git a/Content.Shared/Mind/Filters/AntagonistMindFilter.cs b/Content.Shared/Mind/Filters/AntagonistMindFilter.cs index d805138ac3..ff192698f5 100644 --- a/Content.Shared/Mind/Filters/AntagonistMindFilter.cs +++ b/Content.Shared/Mind/Filters/AntagonistMindFilter.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Mind.Filters; /// public sealed partial class AntagonistMindFilter : MindFilter { - protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan) { var roleSys = entMan.System(); return !roleSys.MindIsAntagonist(mind); diff --git a/Content.Shared/Mind/Filters/BodyMindFilter.cs b/Content.Shared/Mind/Filters/BodyMindFilter.cs index 334539634f..0e471b0187 100644 --- a/Content.Shared/Mind/Filters/BodyMindFilter.cs +++ b/Content.Shared/Mind/Filters/BodyMindFilter.cs @@ -10,7 +10,7 @@ public sealed partial class BodyMindFilter : MindFilter [DataField(required: true)] public EntityWhitelist Whitelist = new(); - protected override bool ShouldRemove(Entity ent, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity ent, EntityUid? exclude, IEntityManager entMan) { if (ent.Comp.OwnedEntity is not {} mob) return true; diff --git a/Content.Shared/Mind/Filters/HasRoleMindFilter.cs b/Content.Shared/Mind/Filters/HasRoleMindFilter.cs index e8098d72a2..8e76e5a47e 100644 --- a/Content.Shared/Mind/Filters/HasRoleMindFilter.cs +++ b/Content.Shared/Mind/Filters/HasRoleMindFilter.cs @@ -14,7 +14,7 @@ public sealed partial class HasRoleMindFilter : MindFilter [DataField(required: true)] public EntityWhitelist Whitelist; - protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan) { var roleSys = entMan.System(); return !roleSys.MindHasRole(mind, Whitelist); diff --git a/Content.Shared/Mind/Filters/JobMindFilter.cs b/Content.Shared/Mind/Filters/JobMindFilter.cs index a6565e4d33..d3bec4d8d6 100644 --- a/Content.Shared/Mind/Filters/JobMindFilter.cs +++ b/Content.Shared/Mind/Filters/JobMindFilter.cs @@ -13,7 +13,7 @@ public sealed partial class JobMindFilter : MindFilter [DataField(required: true)] public ProtoId Job; - protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan) { var jobSys = entMan.System(); return jobSys.MindHasJobWithId(mind, Job); diff --git a/Content.Shared/Mind/Filters/MindFilter.cs b/Content.Shared/Mind/Filters/MindFilter.cs index c2daf3e361..47c8c4178b 100644 --- a/Content.Shared/Mind/Filters/MindFilter.cs +++ b/Content.Shared/Mind/Filters/MindFilter.cs @@ -3,25 +3,25 @@ using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Shared.Mind.Filters; /// -/// A mind filter that can be used to filter out minds from a . +/// A mind filter that can be used to filter out minds from a . /// [ImplicitDataDefinitionForInheritors] public abstract partial class MindFilter { /// /// The actual filter function, this has to return false for minds that get removed from the pool. - /// An excluded mind will be the same one passed to . + /// An excluded mind will be the same one passed to . /// /// The mind to check /// The same mind passed to FindMinds - protected abstract bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys); + protected abstract bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan); /// /// The high-level filter function to be used by the mind system. /// - public bool Filter(Entity mind, EntityUid? exclude, EntityManager entMan, SharedMindSystem mindSys) + public bool Filter(Entity mind, EntityUid? exclude, EntityManager entMan) { - return ShouldRemove(mind, exclude, entMan, mindSys) ^ Inverted; + return ShouldRemove(mind, exclude, entMan) ^ Inverted; } /// diff --git a/Content.Shared/Mind/Filters/IMindPool.cs b/Content.Shared/Mind/Filters/MindPool.cs similarity index 51% rename from Content.Shared/Mind/Filters/IMindPool.cs rename to Content.Shared/Mind/Filters/MindPool.cs index 263d15d812..0886e0716d 100644 --- a/Content.Shared/Mind/Filters/IMindPool.cs +++ b/Content.Shared/Mind/Filters/MindPool.cs @@ -1,13 +1,13 @@ -using Robust.Shared.Serialization.Manager.Attributes; +using Content.Shared.Objectives.Systems; namespace Content.Shared.Mind.Filters; /// /// A mind pool that can find minds to use for objectives etc. -/// Further filtered by . +/// Further filtered by . /// [ImplicitDataDefinitionForInheritors] -public partial interface IMindPool +public abstract partial class MindPool { /// /// Add minds for this pool to a hashset. @@ -15,5 +15,7 @@ public partial interface IMindPool /// /// The hashset to add to /// A mind entity that must not be returned - void FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys); + /// entity Manager for further control + /// targeting system which explicitly searches for targets. + public abstract void FindMinds(HashSet> minds, EntityUid? exclude, IEntityManager entMan, TargetSystem targetSys); } diff --git a/Content.Shared/Mind/Filters/ObjectiveMindFilter.cs b/Content.Shared/Mind/Filters/ObjectiveMindFilter.cs index 5b64fc36c6..275f49872d 100644 --- a/Content.Shared/Mind/Filters/ObjectiveMindFilter.cs +++ b/Content.Shared/Mind/Filters/ObjectiveMindFilter.cs @@ -10,7 +10,7 @@ public sealed partial class ObjectiveMindFilter : MindFilter [DataField(required: true)] public EntityWhitelist Blacklist = new(); - protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan, SharedMindSystem mindSys) + protected override bool ShouldRemove(Entity mind, EntityUid? exclude, IEntityManager entMan) { var whitelistSys = entMan.System(); foreach (var obj in mind.Comp.Objectives) diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index a21642c4ec..8571c35aa2 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Systems; using Content.Shared.Players; +using Content.Shared.Silicons.StationAi; using Content.Shared.Speech; using Content.Shared.Whitelist; using Robust.Shared.Containers; @@ -32,20 +33,17 @@ namespace Content.Shared.Mind; public abstract partial class SharedMindSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedObjectivesSystem _objectives = default!; - [Dependency] private readonly SharedPlayerSystem _player = default!; - [Dependency] private readonly MetaDataSystem _metadata = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedPlayerSystem _player = default!; [ViewVariables] protected readonly Dictionary UserMinds = new(); - private HashSet> _pickingMinds = new(); - private readonly EntProtoId _mindProto = "MindBase"; public override void Initialize() @@ -595,57 +593,12 @@ public abstract partial class SharedMindSystem : EntitySystem return TryGetMind(userId, out _, out var mind) ? mind.CharacterName : null; } - /// - /// Returns a list of every living humanoid player's minds, except for a single one which is exluded. - /// A new hashset is allocated for every call, consider using instead. - /// - public HashSet> GetAliveHumans(EntityUid? exclude = null) - { - var allHumans = new HashSet>(); - AddAliveHumans(allHumans, exclude); - return allHumans; - } - - /// - /// Adds to a hashset every living humanoid player's minds, except for a single one which is exluded. - /// - public void AddAliveHumans(HashSet> allHumans, EntityUid? exclude = null) - { - // HumanoidProfileComponent is used to prevent mice, pAIs, etc from being chosen - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var mobState)) - { - // the player needs to have a mind and not be the excluded one + - // the player has to be alive - if (!TryGetMind(uid, out var mind, out var mindComp) || mind == exclude || !_mobState.IsAlive(uid, mobState)) - continue; - - allHumans.Add((mind, mindComp)); - } - } - - /// - /// Picks a random mind from a pool after applying a list of filters. - /// Returns null if no valid mind could be found. - /// - public Entity? PickFromPool(IMindPool pool, List filters, EntityUid? exclude = null) - { - _pickingMinds.Clear(); - pool.FindMinds(_pickingMinds, exclude, EntityManager, this); - FilterMinds(_pickingMinds, filters, exclude); - - if (_pickingMinds.Count == 0) - return null; - - return _random.Pick(_pickingMinds); - } - /// /// Filters minds from a hashset using a single . /// public void FilterMinds(HashSet> minds, MindFilter filter, EntityUid? exclude = null) { - minds.RemoveWhere(mind => filter.Filter(mind, exclude, EntityManager, this)); + minds.RemoveWhere(mind => filter.Filter(mind, exclude, EntityManager)); } /// diff --git a/Content.Shared/Objectives/Systems/TargetSystem.cs b/Content.Shared/Objectives/Systems/TargetSystem.cs new file mode 100644 index 0000000000..215d87d62f --- /dev/null +++ b/Content.Shared/Objectives/Systems/TargetSystem.cs @@ -0,0 +1,66 @@ +using Content.Shared.Humanoid; +using Content.Shared.Mind; +using Content.Shared.Mind.Filters; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Robust.Shared.Random; + +namespace Content.Shared.Objectives.Systems; + +/// +/// This system stores enumerators to find valid Targets, typically searching for minds. +/// Typically used in conjunction with a or an Objective. +/// +public sealed class TargetSystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + + private HashSet> _pickingMinds = new(); + + /// + /// Returns a list of every living humanoid player's minds, except for a single one which is exluded. + /// A new hashset is allocated for every call, consider using instead. + /// + public HashSet> GetAliveHumans(EntityUid? exclude = null) + { + var allHumans = new HashSet>(); + AddAliveHumans(allHumans, exclude); + return allHumans; + } + + /// + /// Adds to a hashset every living humanoid player's minds, except for a single one which is exluded. + /// + public void AddAliveHumans(HashSet> allHumans, EntityUid? exclude = null) + { + // HumanoidProfileComponent is used to prevent mice, pAIs, etc from being chosen + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var mobState)) + { + // the player needs to have a mind and not be the excluded one + + // the player has to be alive + if (!_mind.TryGetMind(uid, out var mind, out var mindComp) || mind == exclude || !_mobState.IsAlive(uid, mobState)) + continue; + + allHumans.Add((mind, mindComp)); + } + } + + /// + /// Picks a random mind from a pool after applying a list of filters. + /// Returns null if no valid mind could be found. + /// + public Entity? PickFromPool(MindPool pool, List filters, EntityUid? exclude = null) + { + _pickingMinds.Clear(); + pool.FindMinds(_pickingMinds, exclude, EntityManager, this); + _mind.FilterMinds(_pickingMinds, filters, exclude); + + if (_pickingMinds.Count == 0) + return null; + + return _random.Pick(_pickingMinds); + } +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs index fde4952f86..cd801511ca 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs @@ -623,6 +623,29 @@ public abstract partial class SharedStationAiSystem : EntitySystem return _blocker.CanComplexInteract(entity.Owner); } + + /// + /// Gets all alive AI minds and adds them to the inputted hashset, excluding one optional mind + /// + /// Hashset of alive AI minds + /// Optional mind to exclude + public void AddAliveAis(HashSet> aliveAis, EntityUid? exclude = null) + { + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out _, out var aiHolder)) + { + // the player needs to have a mind and not be the excluded one + + // the player has to be alive + if (!TryGetHeld((uid, aiHolder), out var held) || _mobState.IsDead(held.Value)) + continue; + + if (!_mind.TryGetMind(held.Value, out var mind, out var mindComp) || mind == exclude) + continue; + + aliveAis.Add((mind, mindComp)); + } + } } public sealed partial class JumpToCoreEvent : InstantActionEvent diff --git a/Resources/Locale/en-US/objectives/conditions/kill-person.ftl b/Resources/Locale/en-US/objectives/conditions/kill-person.ftl index aad31d26f9..c5ea6f47fc 100644 --- a/Resources/Locale/en-US/objectives/conditions/kill-person.ftl +++ b/Resources/Locale/en-US/objectives/conditions/kill-person.ftl @@ -1,3 +1,4 @@ objective-condition-kill-person-title = Kill or maroon {$targetName}, {CAPITALIZE($job)} objective-condition-kill-maroon-title = Kill and maroon {$targetName}, {CAPITALIZE($job)} +objective-condition-kill-station-ai = Destroy {$targetName}, {CAPITALIZE($job)} and ensure they remain out of commission. objective-condition-maroon-person-title = Prevent {$targetName}, {CAPITALIZE($job)} from reaching CentComm. diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index bbdd6fa236..0480a5c9ad 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -29,6 +29,7 @@ id: TraitorObjectiveGroupKill weights: KillRandomPersonObjective: 1 + KillStationAiObjective: 0.25 KillRandomHeadObjective: 0.25 - type: weightedRandom diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index 9ffc0146a2..f8cc42a222 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 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 0000000000000000000000000000000000000000..02fc551ed7cab687da49589bbc75373441b8effd GIT binary patch literal 8017 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 Qn-hT1H91zG?Qr3L02Up4Z2$lO literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json b/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json index 55bd389b63..5c389a8da4 100644 --- a/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json +++ b/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json @@ -253,6 +253,9 @@ }, { "name": "frame_4" + }, + { + "name": "broken" } ] } From a07bd251b478520f8add32c0c47a9b5fc83fca1c Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 6 Apr 2026 22:25:02 +0000 Subject: [PATCH 113/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4a704decc0..2552eaea39 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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 @@ -4031,3 +4023,13 @@ 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 From 6789f1ee88e1045926700fba5d337304502e2fdf Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Date: Mon, 6 Apr 2026 19:21:29 -0700 Subject: [PATCH 114/247] shared... --- .../Implants/SubdermalImplantSystem.cs | 41 +--- Content.Server/PDA/Ringer/RingerSystem.cs | 3 +- .../Store/Systems/StoreSystem.Listings.cs | 172 ---------------- .../Store/Systems/StoreSystem.Refund.cs | 3 + .../Store/Systems/StoreSystem.Ui.cs | 113 +---------- Content.Server/Store/Systems/StoreSystem.cs | 144 +------------ Content.Server/Traitor/Uplink/UplinkSystem.cs | 3 +- .../SharedSubdermalImplantSystem.Relays.cs | 29 ++- .../Store/Components/CurrencyComponent.cs | 3 +- .../Store/SharedStoreSystem.Listings.cs | 184 +++++++++++++++++ Content.Shared/Store/SharedStoreSystem.UI.cs | 105 ++++++++++ Content.Shared/Store/SharedStoreSystem.cs | 191 +++++++++++++++++- 12 files changed, 522 insertions(+), 469 deletions(-) rename {Content.Server => Content.Shared}/Store/Components/CurrencyComponent.cs (95%) create mode 100644 Content.Shared/Store/SharedStoreSystem.Listings.cs create mode 100644 Content.Shared/Store/SharedStoreSystem.UI.cs diff --git a/Content.Server/Implants/SubdermalImplantSystem.cs b/Content.Server/Implants/SubdermalImplantSystem.cs index 582b9cb2ac..08fe5b54cf 100644 --- a/Content.Server/Implants/SubdermalImplantSystem.cs +++ b/Content.Server/Implants/SubdermalImplantSystem.cs @@ -1,44 +1,5 @@ -using Content.Server.Store.Components; -using Content.Server.Store.Systems; using Content.Shared.Implants; -using Content.Shared.Interaction; -using Content.Shared.Popups; -using Content.Shared.Store.Components; namespace Content.Server.Implants; -public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem -{ - [Dependency] private readonly StoreSystem _store = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent>(OnStoreRelay); - } - - // TODO: This shouldn't be in the SubdermalImplantSystem - private void OnStoreRelay(EntityUid uid, StoreComponent store, ImplantRelayEvent implantRelay) - { - var args = implantRelay.Event; - - if (args.Handled) - return; - - // can only insert into yourself to prevent uplink checking with renault - if (args.Target != args.User) - return; - - if (!TryComp(args.Used, out var currency)) - return; - - // same as store code, but message is only shown to yourself - if (!_store.TryAddCurrency((args.Used, currency), (uid, store))) - return; - - args.Handled = true; - var msg = Loc.GetString("store-currency-inserted-implant", ("used", args.Used)); - _popup.PopupEntity(msg, args.User, args.User); - } -} +public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem; diff --git a/Content.Server/PDA/Ringer/RingerSystem.cs b/Content.Server/PDA/Ringer/RingerSystem.cs index 3efb6cbfdd..a86b44d389 100644 --- a/Content.Server/PDA/Ringer/RingerSystem.cs +++ b/Content.Server/PDA/Ringer/RingerSystem.cs @@ -1,10 +1,9 @@ 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; diff --git a/Content.Server/Store/Systems/StoreSystem.Listings.cs b/Content.Server/Store/Systems/StoreSystem.Listings.cs index 412114ce03..79e0683738 100644 --- a/Content.Server/Store/Systems/StoreSystem.Listings.cs +++ b/Content.Server/Store/Systems/StoreSystem.Listings.cs @@ -8,177 +8,5 @@ namespace Content.Server.Store.Systems; public sealed partial class StoreSystem { - /// - /// Refreshes all listings on a store. - /// Do not use if you don't know what you're doing. - /// - /// The store to refresh - public void RefreshAllListings(StoreComponent component) - { - var previousState = component.FullListingsCatalog; - var newState = GetAllListings(); - // if we refresh list with existing cost modifiers - they will be removed, - // need to restore them - if (previousState.Count != 0) - { - foreach (var previousStateListingItem in previousState) - { - if (!previousStateListingItem.IsCostModified - || !TryGetListing(newState, previousStateListingItem.ID, out var found)) - { - continue; - } - foreach (var (modifierSourceId, costModifier) in previousStateListingItem.CostModifiersBySourceId) - { - found.AddCostModifier(modifierSourceId, costModifier); - } - } - } - - component.FullListingsCatalog = newState; - } - - /// - /// Gets all listings from a prototype. - /// - /// All the listings - public HashSet GetAllListings() - { - var clones = new HashSet(); - foreach (var prototype in _proto.EnumeratePrototypes()) - { - clones.Add(new ListingDataWithCostModifiers(prototype)); - } - - return clones; - } - - /// - /// Adds a listing from an Id to a store - /// - /// The store to add the listing to - /// The id of the listing - /// Whether or not the listing was added successfully - public bool TryAddListing(StoreComponent component, string listingId) - { - if (!_proto.TryIndex(listingId, out var proto)) - { - Log.Error("Attempted to add invalid listing."); - return false; - } - - return TryAddListing(component, proto); - } - - /// - /// Adds a listing to a store - /// - /// The store to add the listing to - /// The listing - /// Whether or not the listing was add successfully - public bool TryAddListing(StoreComponent component, ListingPrototype listing) - { - return component.FullListingsCatalog.Add(new ListingDataWithCostModifiers(listing)); - } - - /// - /// Gets the available listings for a store - /// - /// Either the account owner, user, or an inanimate object (e.g., surplus bundle) - /// - /// The store the listings are coming from. - /// The available listings. - public IEnumerable GetAvailableListings(EntityUid buyer, EntityUid store, StoreComponent component) - { - return GetAvailableListings(buyer, component.FullListingsCatalog, component.Categories, store); - } - - /// - /// Gets the available listings for a user given an overall set of listings and categories to filter by. - /// - /// Either the account owner, user, or an inanimate object (e.g., surplus bundle) - /// All of the listings that are available. If null, will just get all listings from the prototypes. - /// What categories to filter by. - /// The physial entity of the store. Can be null. - /// The available listings. - public IEnumerable GetAvailableListings( - EntityUid buyer, - IReadOnlyCollection? listings, - HashSet> categories, - EntityUid? storeEntity = null - ) - { - listings ??= GetAllListings(); - - foreach (var listing in listings) - { - if (!ListingHasCategory(listing, categories)) - continue; - - if (listing.Conditions != null) - { - var args = new ListingConditionArgs(GetBuyerMind(buyer), storeEntity, listing, EntityManager); - var conditionsMet = true; - - foreach (var condition in listing.Conditions) - { - if (!condition.Condition(args)) - { - conditionsMet = false; - break; - } - } - - if (!conditionsMet) - continue; - } - - yield return listing; - } - } - - /// - /// Returns the entity's mind entity, if it has one, to be used for listing conditions. - /// If it doesn't have one, or is a mind entity already, it returns itself. - /// - /// The buying entity. - public EntityUid GetBuyerMind(EntityUid buyer) - { - if (!HasComp(buyer) && _mind.TryGetMind(buyer, out var buyerMind, out var _)) - return buyerMind; - - return buyer; - } - - /// - /// Checks if a listing appears in a list of given categories - /// - /// The listing itself. - /// The categories to check through. - /// If the listing was present in one of the categories. - public bool ListingHasCategory(ListingData listing, HashSet> categories) - { - foreach (var cat in categories) - { - if (listing.Categories.Contains(cat)) - return true; - } - return false; - } - - private bool TryGetListing(IReadOnlyCollection collection, string listingId, [MaybeNullWhen(false)] out ListingDataWithCostModifiers found) - { - foreach(var current in collection) - { - if (current.ID == listingId) - { - found = current; - return true; - } - } - - found = null!; - return false; - } } diff --git a/Content.Server/Store/Systems/StoreSystem.Refund.cs b/Content.Server/Store/Systems/StoreSystem.Refund.cs index cb92a42c10..f4ecd87040 100644 --- a/Content.Server/Store/Systems/StoreSystem.Refund.cs +++ b/Content.Server/Store/Systems/StoreSystem.Refund.cs @@ -4,11 +4,14 @@ using Content.Shared.Interaction.Events; using Content.Shared.Store.Components; using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Containers; +using Robust.Shared.Timing; namespace Content.Server.Store.Systems; public sealed partial class StoreSystem { + [Dependency] private readonly IGameTiming _timing = default!; + private void InitializeRefund() { SubscribeLocalEvent(OnStoreTerminating); diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index ccf7c3f36c..7a87f8b162 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -7,32 +7,25 @@ using Content.Shared.Actions; using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Hands.EntitySystems; -using Content.Shared.Mind; using Content.Shared.Mindshield.Components; using Content.Shared.NPC.Systems; -using Content.Shared.PDA.Ringer; using Content.Shared.Store; using Content.Shared.Store.Components; using Content.Shared.UserInterface; -using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; namespace Content.Server.Store.Systems; public sealed partial class StoreSystem { [Dependency] private readonly IAdminLogManager _admin = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly ActionUpgradeSystem _actionUpgrade = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly StackSystem _stack = default!; - [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly StackSystem _stack = default!; private void InitializeUi() { @@ -66,98 +59,6 @@ public sealed partial class StoreSystem RaiseLocalEvent(entity.Comp.Store.Value, ev); } - /// - /// 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); - } - } - private void OnRequestUpdate(EntityUid uid, StoreComponent component, StoreRequestUpdateInterfaceMessage args) { UpdateUserInterface(args.Actor, GetEntity(args.Entity), component); @@ -246,7 +147,7 @@ public sealed partial class StoreSystem EntityUid? actionId; // I guess we just allow duplicate actions? // Allow duplicate actions and just have a single list buy for the buy-once ones. - if (listing.ApplyToMob || !_mind.TryGetMind(buyer, out var mind, out _)) + if (listing.ApplyToMob || !Mind.TryGetMind(buyer, out var mind, out _)) actionId = _actions.AddAction(buyer, listing.ProductAction); else actionId = _actionContainer.AddAction(mind, listing.ProductAction); @@ -322,7 +223,7 @@ public sealed partial class StoreSystem _admin.Add(LogType.StorePurchase, logImpact, - $"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _proto)}\" from {ToPrettyString(uid)}{logExtraInfo}."); + $"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, Proto)}\" from {ToPrettyString(uid)}{logExtraInfo}."); listing.PurchaseAmount++; //track how many times something has been purchased if (msg.SoundSource != null && GetEntity(msg.SoundSource) != null) @@ -355,7 +256,7 @@ public sealed partial class StoreSystem return; //make sure a malicious client didn't send us random shit - if (!_proto.TryIndex(msg.Currency, out var proto)) + if (!Proto.TryIndex(msg.Currency, out var proto)) return; //we need an actually valid entity to spawn. This check has been done earlier, but just in case. diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index 63c6d48b0b..b8e92f10bf 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -1,39 +1,23 @@ -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; public sealed partial class StoreSystem : SharedStoreSystem { - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStoreOpenAttempt); - SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(BeforeActivatableUiOpen); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnIntrinsicStoreAction); SubscribeLocalEvent(OnImplantActivate); @@ -48,8 +32,8 @@ public sealed partial class StoreSystem : SharedStoreSystem 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 (!_uiSystem.HasUi(uid, StoreUiKey.Key)) - _uiSystem.SetUi(uid, StoreUiKey.Key, new InterfaceData("StoreBoundUserInterface")); + if (!UI.HasUi(uid, StoreUiKey.Key)) + UI.SetUi(uid, StoreUiKey.Key, new InterfaceData("StoreBoundUserInterface")); } private void OnStartup(EntityUid uid, StoreComponent component, ComponentStartup args) @@ -75,7 +59,7 @@ public sealed partial class StoreSystem : SharedStoreSystem 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; @@ -85,32 +69,11 @@ public sealed partial class StoreSystem : SharedStoreSystem 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) - { - 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", target)); - _popup.PopupEntity(msg, target, args.User); - } - private void OnImplantActivate(Entity entity, ref OpenUplinkImplantEvent args) { if (GetRemoteStore(entity.AsNullable()) is not { } store) @@ -118,103 +81,4 @@ public sealed partial class StoreSystem : SharedStoreSystem ToggleUi(args.Performer, store, store.Comp, entity, entity.Comp); } - - /// - /// 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; - } } diff --git a/Content.Server/Traitor/Uplink/UplinkSystem.cs b/Content.Server/Traitor/Uplink/UplinkSystem.cs index c5c66e1a40..1daee849dd 100644 --- a/Content.Server/Traitor/Uplink/UplinkSystem.cs +++ b/Content.Server/Traitor/Uplink/UplinkSystem.cs @@ -53,7 +53,8 @@ public sealed class UplinkSystem : EntitySystem } // If we didn't have an uplink, make an empty one. - SetUplink(args.Implanted, Spawn(TraitorUplinkStore, MapCoordinates.Nullspace), 0, false); + 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."); } diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.Relays.cs index 24b76e15c5..59e2d1baa1 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; namespace Content.Shared.Implants; @@ -12,11 +12,14 @@ public abstract partial class SharedSubdermalImplantSystem public void InitializeRelay() { 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); } /// @@ -36,6 +39,26 @@ public abstract partial class SharedSubdermalImplantSystem RaiseLocalEvent(implant, relayEv); } } + + /// + /// Relays events from the implanted to the implant. + /// + private void RefRelayToImplantEvent(Entity entity, ref T args) where T : notnull + { + if (!_container.TryGetContainer(entity, ImplanterComponent.ImplantSlotId, out var implantContainer)) + return; + + var relayEv = new ImplantRelayEvent(args, entity); + foreach (var implant in implantContainer.ContainedEntities) + { + if (args is HandledEntityEventArgs { Handled: true }) + return; + + RaiseLocalEvent(implant, relayEv); + } + + args = relayEv.Event; + } } /// @@ -43,7 +66,7 @@ public abstract partial class SharedSubdermalImplantSystem /// public sealed class ImplantRelayEvent where T : notnull { - public readonly T Event; + public T Event; public readonly EntityUid ImplantedEntity; diff --git a/Content.Server/Store/Components/CurrencyComponent.cs b/Content.Shared/Store/Components/CurrencyComponent.cs similarity index 95% rename from Content.Server/Store/Components/CurrencyComponent.cs rename to Content.Shared/Store/Components/CurrencyComponent.cs index eb4ca1c0e3..a4c5b3c027 100644 --- a/Content.Server/Store/Components/CurrencyComponent.cs +++ b/Content.Shared/Store/Components/CurrencyComponent.cs @@ -1,8 +1,7 @@ using Content.Shared.FixedPoint; -using Content.Shared.Store; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -namespace Content.Server.Store.Components; +namespace Content.Shared.Store.Components; /// /// Identifies a component that can be inserted into a store diff --git a/Content.Shared/Store/SharedStoreSystem.Listings.cs b/Content.Shared/Store/SharedStoreSystem.Listings.cs new file mode 100644 index 0000000000..fa0f29bb4c --- /dev/null +++ b/Content.Shared/Store/SharedStoreSystem.Listings.cs @@ -0,0 +1,184 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Mind; +using Content.Shared.Store.Components; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Store; + + +public abstract partial class SharedStoreSystem +{ + /// + /// Refreshes all listings on a store. + /// Do not use if you don't know what you're doing. + /// + /// The store to refresh + public void RefreshAllListings(StoreComponent component) + { + var previousState = component.FullListingsCatalog; + var newState = GetAllListings(); + // if we refresh list with existing cost modifiers - they will be removed, + // need to restore them + if (previousState.Count != 0) + { + foreach (var previousStateListingItem in previousState) + { + if (!previousStateListingItem.IsCostModified + || !TryGetListing(newState, previousStateListingItem.ID, out var found)) + { + continue; + } + + foreach (var (modifierSourceId, costModifier) in previousStateListingItem.CostModifiersBySourceId) + { + found.AddCostModifier(modifierSourceId, costModifier); + } + } + } + + component.FullListingsCatalog = newState; + } + + /// + /// Gets all listings from a prototype. + /// + /// All the listings + public HashSet GetAllListings() + { + var clones = new HashSet(); + foreach (var prototype in Proto.EnumeratePrototypes()) + { + clones.Add(new ListingDataWithCostModifiers(prototype)); + } + + return clones; + } + + /// + /// Adds a listing from an Id to a store + /// + /// The store to add the listing to + /// The id of the listing + /// Whether or not the listing was added successfully + public bool TryAddListing(StoreComponent component, string listingId) + { + if (!Proto.TryIndex(listingId, out var proto)) + { + Log.Error("Attempted to add invalid listing."); + return false; + } + + return TryAddListing(component, proto); + } + + /// + /// Adds a listing to a store + /// + /// The store to add the listing to + /// The listing + /// Whether or not the listing was add successfully + public bool TryAddListing(StoreComponent component, ListingPrototype listing) + { + return component.FullListingsCatalog.Add(new ListingDataWithCostModifiers(listing)); + } + + /// + /// Gets the available listings for a store + /// + /// Either the account owner, user, or an inanimate object (e.g., surplus bundle) + /// + /// The store the listings are coming from. + /// The available listings. + public IEnumerable GetAvailableListings(EntityUid buyer, EntityUid store, StoreComponent component) + { + return GetAvailableListings(buyer, component.FullListingsCatalog, component.Categories, store); + } + + /// + /// Gets the available listings for a user given an overall set of listings and categories to filter by. + /// + /// Either the account owner, user, or an inanimate object (e.g., surplus bundle) + /// All of the listings that are available. If null, will just get all listings from the prototypes. + /// What categories to filter by. + /// The physial entity of the store. Can be null. + /// The available listings. + public IEnumerable GetAvailableListings( + EntityUid buyer, + IReadOnlyCollection? listings, + HashSet> categories, + EntityUid? storeEntity = null + ) + { + listings ??= GetAllListings(); + + foreach (var listing in listings) + { + if (!ListingHasCategory(listing, categories)) + continue; + + if (listing.Conditions != null) + { + var args = new ListingConditionArgs(GetBuyerMind(buyer), storeEntity, listing, EntityManager); + var conditionsMet = true; + + foreach (var condition in listing.Conditions) + { + if (!condition.Condition(args)) + { + conditionsMet = false; + break; + } + } + + if (!conditionsMet) + continue; + } + + yield return listing; + } + } + + /// + /// Returns the entity's mind entity, if it has one, to be used for listing conditions. + /// If it doesn't have one, or is a mind entity already, it returns itself. + /// + /// The buying entity. + public EntityUid GetBuyerMind(EntityUid buyer) + { + if (!HasComp(buyer) && Mind.TryGetMind(buyer, out var buyerMind, out _)) + return buyerMind; + + return buyer; + } + + /// + /// Checks if a listing appears in a list of given categories + /// + /// The listing itself. + /// The categories to check through. + /// If the listing was present in one of the categories. + public bool ListingHasCategory(ListingData listing, HashSet> categories) + { + foreach (var cat in categories) + { + if (listing.Categories.Contains(cat)) + return true; + } + return false; + } + + private bool TryGetListing(IReadOnlyCollection collection, string listingId, [MaybeNullWhen(false)] out ListingDataWithCostModifiers found) + { + foreach(var current in collection) + { + if (current.ID == listingId) + { + found = current; + return true; + } + } + + found = null!; + return false; + } +} diff --git a/Content.Shared/Store/SharedStoreSystem.UI.cs b/Content.Shared/Store/SharedStoreSystem.UI.cs new file mode 100644 index 0000000000..2d4b166c27 --- /dev/null +++ b/Content.Shared/Store/SharedStoreSystem.UI.cs @@ -0,0 +1,105 @@ +using System.Linq; +using Content.Shared.FixedPoint; +using Content.Shared.PDA.Ringer; +using Content.Shared.Store.Components; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Store; + +/// +/// This handles... +/// +public abstract partial class SharedStoreSystem +{ + /// + /// Toggles the store Ui open and closed + /// + /// the person doing the toggling + /// the store being toggled + /// + /// The entity remotely accessing the store, if any. + /// The remote access component, if any. + public void ToggleUi(EntityUid user, EntityUid storeEnt, StoreComponent? component = null, EntityUid? remoteAccess = null, RemoteStoreComponent? remoteComponent = null) + { + if (!Resolve(storeEnt, ref component)) + return; + + if (remoteAccess != null && !Resolve(remoteAccess.Value, ref remoteComponent) && remoteComponent!.Store != storeEnt) + return; + + if (!TryComp(user, out var actor)) + return; + + if (!UI.TryToggleUi(remoteAccess != null ? remoteAccess.Value : storeEnt, StoreUiKey.Key, actor.PlayerSession)) + return; + + UpdateUserInterface(user, storeEnt, component); + } + + /// + /// Closes the store UI for everyone, if it's open + /// + public void CloseUi(EntityUid uid, StoreComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + UI.CloseUi(uid, StoreUiKey.Key); + } + + /// + /// Updates the user interface for a store and refreshes the listings + /// + /// The person who if opening the store ui. Listings are filtered based on this. + /// The store entity itself + /// The store component being refreshed. + public void UpdateUserInterface(EntityUid? user, EntityUid store, StoreComponent? component = null) + { + if (!Resolve(store, ref component)) + return; + + //this is the person who will be passed into logic for all listing filtering. + if (user != null) //if we have no "buyer" for this update, then don't update the listings + { + component.LastAvailableListings = GetAvailableListings(component.AccountOwner ?? user.Value, store, component).ToHashSet(); + } + + //dictionary for all currencies, including 0 values for currencies on the whitelist + Dictionary, FixedPoint2> allCurrency = new(); + foreach (var supported in component.CurrencyWhitelist) + { + allCurrency.Add(supported, FixedPoint2.Zero); + + if (component.Balance.TryGetValue(supported, out var value)) + allCurrency[supported] = value; + } + + // TODO: if multiple users are supposed to be able to interact with a single BUI & see different + // stores/listings, this needs to use session specific BUI states. + + // only tell operatives to lock their uplink if it can be locked + var showFooter = HasComp(store); + + var state = new StoreUpdateState(component.LastAvailableListings, allCurrency, showFooter, component.RefundAllowed); + UpdateRemoteStores(store, state); + UI.SetUiState(store, StoreUiKey.Key, state); + } + + /// + /// Updates any remote store connections to a specific store. + /// + /// The store being updated. + /// The state being applied. + public void UpdateRemoteStores(EntityUid store, StoreUpdateState state) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var remote, out var ui)) + { + if (remote.Store != store) + continue; + + UI.SetUiState((uid, ui), StoreUiKey.Key, state); + } + } +} diff --git a/Content.Shared/Store/SharedStoreSystem.cs b/Content.Shared/Store/SharedStoreSystem.cs index c281685077..6e3e5f3e42 100644 --- a/Content.Shared/Store/SharedStoreSystem.cs +++ b/Content.Shared/Store/SharedStoreSystem.cs @@ -1,6 +1,14 @@ 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; @@ -10,9 +18,75 @@ namespace Content.Shared.Store; /// 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; + + 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, @@ -21,7 +95,7 @@ public abstract partial class SharedStoreSystem : EntitySystem /// Entity we're checking for an attached store on /// Store entity we're returning. /// True if a store was found. - public bool TryGetStore(Entity entity, [NotNullWhen(true)] out Entity? store) + public bool TryGetStore(EntityUid entity, [NotNullWhen(true)] out Entity? store) { store = GetStore(entity); return store != null; @@ -34,12 +108,14 @@ public abstract partial class SharedStoreSystem : EntitySystem /// /// Entity we're checking for an attached store on /// The store entity and component if found. - public Entity? GetStore(Entity entity) + public Entity? GetStore(EntityUid entity) { if (StoreQuery.TryComp(entity, out var storeComp)) return (entity, storeComp); - return GetRemoteStore(entity); + var ev = new GetStoreEvent(); + RaiseLocalEvent(entity, ref ev); + return ev.Store; } /// @@ -65,4 +141,113 @@ public abstract partial class SharedStoreSystem : EntitySystem 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; + } +} + From e0af5216b6165773d0f017c7caaef0584ea3f35f Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Date: Mon, 6 Apr 2026 19:33:27 -0700 Subject: [PATCH 115/247] comment and delete empty system --- Content.Server/Store/Systems/StoreSystem.Listings.cs | 12 ------------ Content.Shared/Store/SharedStoreSystem.cs | 1 + 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 Content.Server/Store/Systems/StoreSystem.Listings.cs diff --git a/Content.Server/Store/Systems/StoreSystem.Listings.cs b/Content.Server/Store/Systems/StoreSystem.Listings.cs deleted file mode 100644 index 79e0683738..0000000000 --- a/Content.Server/Store/Systems/StoreSystem.Listings.cs +++ /dev/null @@ -1,12 +0,0 @@ -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; - -public sealed partial class StoreSystem -{ - -} diff --git a/Content.Shared/Store/SharedStoreSystem.cs b/Content.Shared/Store/SharedStoreSystem.cs index 6e3e5f3e42..315d70d52a 100644 --- a/Content.Shared/Store/SharedStoreSystem.cs +++ b/Content.Shared/Store/SharedStoreSystem.cs @@ -58,6 +58,7 @@ public abstract partial class SharedStoreSystem : EntitySystem { var ev = args.Event; + // Only allow insertion if the person implanted is doing the action. if (ev.User == ev.Target) ev.TargetOverride = implant; else From 438b5c8ddadd73b825f42126af116c181a6666b1 Mon Sep 17 00:00:00 2001 From: Zekins <136648667+Zekins3366@users.noreply.github.com> Date: Tue, 7 Apr 2026 06:13:05 +0300 Subject: [PATCH 116/247] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=98=D0=BC=D0=BF=D0=BE=D1=80?= =?UTF-8?q?=D1=82=D0=B0=20(#3556)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Preferences/HumanoidCharacterProfile.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index b4ea4da565..f295039f0f 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -860,7 +860,17 @@ namespace Content.Shared.Preferences 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; From 8522930dca3e51b4a927a11b3058144402fb7083 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:13:16 -0700 Subject: [PATCH 117/247] fix double purchase bug --- .../Prototypes/Entities/Objects/Misc/subdermal_implants.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml index 8232a82a2b..f114ece546 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml @@ -158,7 +158,7 @@ - Syndicate - type: entity - parent: [ BaseSubdermalImplant, StorePresetUplink ] + parent: BaseSubdermalImplant id: UplinkImplant name: uplink implant description: This implant lets the user access a hidden Syndicate uplink at will. From 95c2be9fe2f55be488c9cec8fe1e219d8603dc2c Mon Sep 17 00:00:00 2001 From: Pok <113675512+Pok27@users.noreply.github.com> Date: Tue, 7 Apr 2026 06:16:01 +0300 Subject: [PATCH 118/247] =?UTF-8?q?[Wiki]=20=D0=A1=D0=BF=D0=BE=D1=81=D0=BE?= =?UTF-8?q?=D0=B1=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20(#3553)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EntityScreenshotGenerator.cs | 150 +++++++++++++++++- .../EntityScreenshotRenderService.cs | 54 +++++-- .../Corvax/GuideGenerator/EntityEntry.cs | 117 -------------- .../GuideGenerator/EntityJsonGenerator.cs | 44 ++++- .../EntityNameDuplicatesJsonGenerator.cs | 40 ++--- .../EntityParentJsonGenerator.cs | 101 ++++++++++++ .../GuideGenerator/PrototypeJsonGenerator.cs | 7 +- .../Corvax/GuideGenerator/TextTools.cs | 31 ++++ .../Corvax/GuideGenerator/YAMLEntry.cs | 2 +- Content.Server/Entry/EntryPoint.cs | 3 +- 10 files changed, 392 insertions(+), 157 deletions(-) delete mode 100644 Content.Server/Corvax/GuideGenerator/EntityEntry.cs create mode 100644 Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs diff --git a/Content.Client/Corvax/ExportSprites/EntityScreenshotGenerator.cs b/Content.Client/Corvax/ExportSprites/EntityScreenshotGenerator.cs index b64a962604..de427a1e43 100644 --- a/Content.Client/Corvax/ExportSprites/EntityScreenshotGenerator.cs +++ b/Content.Client/Corvax/ExportSprites/EntityScreenshotGenerator.cs @@ -1,17 +1,23 @@ using System.Linq; using System.Threading.Tasks; +using Content.Client.Gameplay; using Content.Shared.CCVar; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Corvax.GuideGenerator; -using Content.Client.Gameplay; +using Content.Shared.Prototypes; using Robust.Client; +using Robust.Client.GameObjects; using Robust.Client.State; using Robust.Client.Timing; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; using Robust.Shared.Map; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown; +using Robust.Shared.Serialization.Markdown.Mapping; +using Robust.Shared.Serialization.Markdown.Sequence; using Robust.Shared.Utility; namespace Content.Client.Corvax.ExportSprites; @@ -29,6 +35,7 @@ public sealed class EntityScreenshotGenerator [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IResourceManager _resourceManager = default!; + [Dependency] private readonly ISerializationManager _serialization = default!; [Dependency] private readonly IStateManager _stateManager = default!; private ISawmill _sawmill = default!; @@ -120,7 +127,7 @@ public sealed class EntityScreenshotGenerator var prototypes = _prototypeManager.EnumeratePrototypes() .Where(proto => !proto.Abstract && - proto.Components.ContainsKey("Sprite") && + HasExportableSprite(proto) && EntityProjectHelper.MatchesAllowedIds(proto.ID, allowedIds)) .OrderBy(proto => proto.ID) .ToList(); @@ -136,11 +143,18 @@ public sealed class EntityScreenshotGenerator try { - entity = _entityManager.SpawnEntity(proto.ID, new EntityCoordinates(previewGrid.Owner, default)); + if (proto.HasComponent(_entityManager.ComponentFactory)) + { + entity = _entityManager.SpawnEntity(proto.ID, new EntityCoordinates(previewGrid.Owner, default)); - await WaitForEntityAppearanceAsync(entity); - ApplyPrototypeAppearance(entity, proto); - await WaitForEntityAppearanceAsync(entity, 1); + await WaitForEntityAppearanceAsync(entity); + ApplyPrototypeAppearance(entity, proto); + await WaitForEntityAppearanceAsync(entity, 1); + } + else + { + entity = SpawnIconEntity(proto); + } await _renderService.Export(entity, Direction.South, outputDir / $"{proto.ID}.png"); exported++; @@ -241,4 +255,128 @@ public sealed class EntityScreenshotGenerator if (solution.GetPrimaryReagentId() is { } reagent) appearanceSystem.SetData(entity, SolutionContainerVisuals.BaseOverride, reagent.ToString(), appearance); } + + private bool HasExportableSprite(EntityPrototype prototype) + { + if (prototype.HasComponent(_entityManager.ComponentFactory)) + return true; + + return TryGetPrototypeIcon(prototype, out _); + } + + private EntityUid SpawnIconEntity(EntityPrototype prototype) + { + if (!TryGetPrototypeIcon(prototype, out var icon) || icon == null) + throw new InvalidOperationException($"Prototype {prototype.ID} has no exportable icon."); + + var entity = _entityManager.SpawnEntity(null, MapCoordinates.Nullspace); + var sprite = _entityManager.EnsureComponent(entity); + var spriteSystem = _entitySystemManager.GetEntitySystem(); + + spriteSystem.AddBlankLayer((entity, sprite), 0); + if (icon is SpriteSpecifier.EntityPrototype entityIcon) + spriteSystem.LayerSetTexture((entity, sprite), 0, spriteSystem.Frame0(new SpriteSpecifier.EntityPrototype(entityIcon.EntityPrototypeId))); + else + spriteSystem.LayerSetSprite((entity, sprite), 0, icon); + sprite.LayerSetShader(0, "unshaded"); + spriteSystem.LayerSetVisible((entity, sprite), 0, true); + + return entity; + } + + private bool TryGetPrototypeIcon(EntityPrototype prototype, out SpriteSpecifier? icon) + { + icon = null; + + foreach (var (_, entry) in prototype.Components) + { + if (TryExtractSpriteSpecifier(entry.Component.GetType(), entry.Mapping, out icon)) + return true; + } + + return false; + } + + private bool TryExtractSpriteSpecifier(Type? expectedType, DataNode? node, out SpriteSpecifier? icon) + { + icon = null; + + if (node == null) + return false; + + if (expectedType != null && + typeof(SpriteSpecifier).IsAssignableFrom(expectedType) && + TryParseSpriteSpecifier(node, out icon)) + { + return true; + } + + if (node is MappingDataNode mapping) + { + foreach (var (key, child) in mapping.Children) + { + Type? childType = null; + + if (expectedType != null && + _serialization.TryGetVariableType(expectedType, key, out var resolvedType)) + { + childType = resolvedType; + } + + if (TryExtractSpriteSpecifier(childType, child, out icon)) + return true; + } + + return false; + } + + if (node is SequenceDataNode sequence) + { + var elementType = GetSequenceElementType(expectedType); + foreach (var child in sequence.Sequence) + { + if (TryExtractSpriteSpecifier(elementType, child, out icon)) + return true; + } + } + + return false; + } + + private static Type? GetSequenceElementType(Type? type) + { + if (type == null) + return null; + + if (type.IsArray) + return type.GetElementType(); + + var genericArguments = type.GenericTypeArguments; + if (genericArguments.Length == 1) + return genericArguments[0]; + + return null; + } + + private bool TryParseSpriteSpecifier(DataNode node, out SpriteSpecifier? icon) + { + icon = null; + + try + { + icon = _serialization.Read(node, notNullableOverride: true); + if (icon == SpriteSpecifier.Invalid) + { + icon = null; + return false; + } + + return true; + } + catch + { + icon = null; + return false; + } + } } diff --git a/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs b/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs index 848279b9db..cd05548469 100644 --- a/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs +++ b/Content.Client/Corvax/ExportSprites/EntityScreenshotRenderService.cs @@ -3,8 +3,8 @@ using System.Threading; using System.Threading.Tasks; using Robust.Client.GameObjects; using Robust.Client.Graphics; -using Robust.Client.Utility; using Robust.Client.UserInterface; +using Robust.Client.Utility; using Robust.Shared.ContentPack; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -27,6 +27,7 @@ public sealed class EntityScreenshotRenderService private EntityScreenshotRenderControl? _control; private bool _initialized; private readonly Dictionary<(ResPath Path, string State), Image> _rsiStateImageCache = new(); + private readonly Dictionary> _textureImageCache = new(); private ISawmill _sawmill = default!; public void Initialize() @@ -48,6 +49,13 @@ public sealed class EntityScreenshotRenderService _rsiStateImageCache.Clear(); + foreach (var image in _textureImageCache.Values) + { + image.Dispose(); + } + + _textureImageCache.Clear(); + if (_control == null) return; @@ -360,7 +368,7 @@ public sealed class EntityScreenshotRenderService private static int ToDelayMilliseconds(float seconds) { - return Math.Max(1, (int)MathF.Round(seconds * 1000f)); + return Math.Max(1, (int) MathF.Round(seconds * 1000f)); } private void WriteAnimationMetadata(ResPath animationDir, IReadOnlyList animationFrames) @@ -388,7 +396,9 @@ public sealed class EntityScreenshotRenderService return false; // Keep the old render-target path for uncommon transformed sprites. - if (spriteComp.Scale != Vector2.One || spriteComp.Rotation != Angle.Zero) + if (spriteComp.Scale != Vector2.One || + spriteComp.Rotation != Angle.Zero || + spriteComp.EnableDirectionOverride) return false; var size = renderBounds.Size; @@ -407,7 +417,7 @@ public sealed class EntityScreenshotRenderService return false; if (!TryGetLayerImage(spriteLayer, direction, out var sourceImage, out var sourceRect)) - continue; + return false; var drawColor = spriteComp.Color * spriteLayer.Color; var drawOffset = ToPixelOffset(spriteComp.Offset + spriteLayer.Offset) - renderBounds.Min; @@ -429,7 +439,7 @@ public sealed class EntityScreenshotRenderService private static void BlitImage( Image sourceImage, - Rectangle sourceRect, + PixelRect sourceRect, Color modulation, Span destination, Vector2i destinationSize, @@ -519,14 +529,17 @@ public sealed class EntityScreenshotRenderService SpriteComponent.Layer layer, Direction direction, out Image image, - out Rectangle sourceRect) + out PixelRect sourceRect) { image = default!; sourceRect = default; - // Raw texture layers need a separate cache path. Use render target fallback for them. if (layer.Texture != null) - return false; + { + image = GetTextureImage(layer.Texture); + sourceRect = new PixelRect(0, 0, image.Width, image.Height); + return true; + } var rsi = layer.ActualRsi; var stateId = ((ISpriteLayer) layer).RsiState; @@ -569,10 +582,33 @@ public sealed class EntityScreenshotRenderService var target = (int) rsiDirection * framesPerDirection + frame; var targetY = target / statesX; var targetX = target % statesX; - sourceRect = new Rectangle(targetX * frameWidth, targetY * frameHeight, frameWidth, frameHeight); + sourceRect = new PixelRect(targetX * frameWidth, targetY * frameHeight, frameWidth, frameHeight); return true; } + private Image GetTextureImage(Texture texture) + { + if (_textureImageCache.TryGetValue(texture, out var cached)) + return cached; + + var image = new Image(texture.Width, texture.Height); + var pixels = image.GetPixelSpan(); + + for (var y = 0; y < texture.Height; y++) + { + for (var x = 0; x < texture.Width; x++) + { + var color = texture.GetPixel(x, y); + pixels[y * texture.Width + x] = new Rgba32(color.RByte, color.GByte, color.BByte, color.AByte); + } + } + + _textureImageCache[texture] = image; + return image; + } + + private readonly record struct PixelRect(int Left, int Top, int Width, int Height); + private sealed class EntityScreenshotRenderControl : Control { private static readonly Color ExportBackgroundColor = new(128, 128, 128, 0); diff --git a/Content.Server/Corvax/GuideGenerator/EntityEntry.cs b/Content.Server/Corvax/GuideGenerator/EntityEntry.cs deleted file mode 100644 index 9ddd167328..0000000000 --- a/Content.Server/Corvax/GuideGenerator/EntityEntry.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System.Linq; -using System.Text.Json.Serialization; -using Content.Shared.Labels.Components; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Markdown.Mapping; -using Robust.Shared.Serialization.Markdown.Sequence; -using Robust.Shared.Serialization.Markdown.Value; - -namespace Content.Server.Corvax.GuideGenerator; - -public sealed class EntityEntry -{ - private static string[]? GetRootParents(EntityPrototype proto, IPrototypeManager prototypeManager) - { - if (proto.Parents is not { Length: > 0 }) - return null; - - var roots = new HashSet(StringComparer.Ordinal); - var visited = new HashSet(StringComparer.Ordinal); - - static IEnumerable GetParentIds(MappingDataNode mapping) - { - if (mapping.TryGet("parent", out ValueDataNode? parentValue)) - { - if (!string.IsNullOrWhiteSpace(parentValue.Value)) - yield return parentValue.Value; - - yield break; - } - - if (!mapping.TryGet("parent", out SequenceDataNode? parentSequence)) - yield break; - - foreach (var parentNode in parentSequence) - { - if (parentNode is not ValueDataNode valueNode) - continue; - - if (!string.IsNullOrWhiteSpace(valueNode.Value)) - yield return valueNode.Value; - } - } - - void Visit(string id) - { - if (!visited.Add(id)) - return; - - if (!YAMLEntry.TryGetRawMapping(prototypeManager, typeof(EntityPrototype), id, out var mapping) || - mapping == null) - { - roots.Add(id); - return; - } - - var parents = GetParentIds(mapping).ToArray(); - if (parents.Length == 0) - { - roots.Add(id); - return; - } - - foreach (var parent in parents) - { - Visit(parent); - } - } - - foreach (var parent in proto.Parents) - { - Visit(parent); - } - - return roots.Count > 0 ? roots.OrderBy(x => x, StringComparer.Ordinal).ToArray() : null; - } - - [JsonPropertyName("id")] - public string Id { get; } - - [JsonPropertyName("name")] - public string Name { get; } - - [JsonPropertyName("desc")] - public string Description { get; } - - [JsonPropertyName("suffix")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Suffix { get; } - - [JsonPropertyName("label")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? Label { get; } - - [JsonPropertyName("parents")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string[]? Parents { get; } - - public EntityEntry(EntityPrototype proto) - { - var prototypeManager = IoCManager.Resolve(); - var loc = IoCManager.Resolve(); - - Id = proto.ID; - Name = TextTools.CapitalizeString(TextTools.GetDisplayName(proto, prototypeManager, loc)); - Description = proto.Description; - Suffix = string.IsNullOrWhiteSpace(proto.EditorSuffix) ? null : proto.EditorSuffix; - Parents = GetRootParents(proto, prototypeManager); - - Label = proto.Components.Values - .Select(x => x.Component) - .OfType() - .Select(lc => lc.CurrentLabel) - .Where(label => !string.IsNullOrEmpty(label)) - .Select(label => Loc.GetString(label!)) - .FirstOrDefault(); - } -} diff --git a/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs index 24c21cd229..d7562222d3 100644 --- a/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/EntityJsonGenerator.cs @@ -1,12 +1,53 @@ using System.IO; using System.Linq; using System.Text.Json; +using System.Text.Encodings.Web; +using System.Text.Json.Serialization; +using Content.Shared.Labels.Components; using Robust.Shared.Prototypes; namespace Content.Server.Corvax.GuideGenerator; public sealed class EntityJsonGenerator { + [JsonPropertyName("id")] + public string Id { get; } + + [JsonPropertyName("name")] + public string Name { get; } + + [JsonPropertyName("desc")] + public string Description { get; } + + [JsonPropertyName("suffix")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Suffix { get; } + + [JsonPropertyName("label")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Label { get; } + + public EntityJsonGenerator(EntityPrototype proto) + { + var prototypeManager = IoCManager.Resolve(); + var loc = IoCManager.Resolve(); + + Id = proto.ID; + Name = TextTools.CapitalizeString(TextTools.GetDisplayName(proto, prototypeManager, loc)); + Description = proto.Description; + + if (!string.IsNullOrWhiteSpace(proto.EditorSuffix)) + Suffix = TextTools.GetEditorSuffix(proto.EditorSuffix, EntityNameDuplicatesJsonGenerator.IgnoredSuffixTokens, TextTools.NormalizeSuffixToken); + + Label = proto.Components.Values + .Select(x => x.Component) + .OfType() + .Select(lc => lc.CurrentLabel) + .Where(label => !string.IsNullOrEmpty(label)) + .Select(label => Loc.GetString(label!)) + .FirstOrDefault(); + } + public static void PublishJson(StreamWriter file) { var prototype = IoCManager.Resolve(); @@ -14,12 +55,13 @@ public sealed class EntityJsonGenerator prototype .EnumeratePrototypes() .Where(x => !x.Abstract) - .Select(x => new EntityEntry(x)) + .Select(x => new EntityJsonGenerator(x)) .ToDictionary(x => x.Id, x => x); var serializeOptions = new JsonSerializerOptions { WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; file.Write(JsonSerializer.Serialize(prototypes, serializeOptions)); diff --git a/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs index 117936e32b..cabbfff030 100644 --- a/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/EntityNameDuplicatesJsonGenerator.cs @@ -2,19 +2,28 @@ using System.IO; using System.Linq; using System.Text.Encodings.Web; using System.Text.Json; +using Content.Shared.Actions.Components; using Content.Shared.Corvax.GuideGenerator; using Content.Shared.Labels.Components; -using Robust.Shared.Physics; using Robust.Shared.Prototypes; namespace Content.Server.Corvax.GuideGenerator; public static class EntityNameDuplicatesJsonGenerator { + private const string AbilitySuffix = "(способность)"; + + public static readonly string[] AllowedNameComponents = + [ + "Fixtures", + "Physics", + "Action" + ]; + // Suffix parts that should be ignored when building display names. - private static readonly HashSet IgnoredSuffixTokens = new(StringComparer.OrdinalIgnoreCase) + public static readonly HashSet IgnoredSuffixTokens = new(StringComparer.OrdinalIgnoreCase) { - "DO NOT MAP", + "do not map", "не маппить", }; @@ -31,10 +40,11 @@ public static class EntityNameDuplicatesJsonGenerator public static bool MatchesEntityNameFilter(EntityPrototype proto, IReadOnlySet allowedIds) { - var compFactory = IoCManager.Resolve(); + var hasAllowedComponent = AllowedNameComponents.Any(proto.Components.ContainsKey); + return !proto.Abstract && - proto.TryGetComponent(out _, compFactory) && - EntityProjectHelper.MatchesAllowedIds(proto.ID, allowedIds); + hasAllowedComponent && + EntityProjectHelper.MatchesAllowedIds(proto.ID, allowedIds); } private static Dictionary> GetDuplicatesName( @@ -42,6 +52,7 @@ public static class EntityNameDuplicatesJsonGenerator bool duplicatesOnly) { var loc = IoCManager.Resolve(); + var compFactory = IoCManager.Resolve(); var allowedIds = EntityProjectGenerator.GetProjectEntityIds(); return prototypeManager .EnumeratePrototypes() @@ -50,24 +61,17 @@ public static class EntityNameDuplicatesJsonGenerator { var name = TextTools.CapitalizeString(TextTools.GetDisplayName(p, prototypeManager, loc)); + if (p.TryGetComponent(out _, compFactory)) + name = $"{name} {AbilitySuffix}"; + var label = GetLabel(p); var rawSuffix = p.EditorSuffix; - var suffix = string.Empty; - if (!string.IsNullOrWhiteSpace(rawSuffix)) - { - var parts = rawSuffix - .Split([',', ';'], StringSplitOptions.RemoveEmptyEntries) - .Select(part => part.Trim()) - .Where(part => !IgnoredSuffixTokens.Contains(part)) - .ToArray(); - - if (parts.Length > 0) - suffix = string.Join(", ", parts).ToLowerInvariant(); - } + var suffix = TextTools.GetEditorSuffix(rawSuffix, IgnoredSuffixTokens, TextTools.NormalizeSuffixToken); return (Name: name, Label: label, Suffix: suffix); }) + .Where(g => !string.IsNullOrWhiteSpace(g.Key.Name)) .Where(g => !duplicatesOnly || g.Count() > 1) .ToDictionary( g => diff --git a/Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs new file mode 100644 index 0000000000..d7064185ce --- /dev/null +++ b/Content.Server/Corvax/GuideGenerator/EntityParentJsonGenerator.cs @@ -0,0 +1,101 @@ +using System.Linq; +using System.Text.Json.Serialization; +using System.IO; +using System.Text.Json; +using System.Text.Encodings.Web; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Markdown.Mapping; +using Robust.Shared.Serialization.Markdown.Sequence; +using Robust.Shared.Serialization.Markdown.Value; + +namespace Content.Server.Corvax.GuideGenerator; + +public sealed class EntityParentJsonGenerator +{ + [JsonPropertyName("id")] + public string Id { get; } + + [JsonPropertyName("parents")] + public string[] Parents { get; } + + public EntityParentJsonGenerator(EntityPrototype proto) + { + var prototypeManager = IoCManager.Resolve(); + + Id = proto.ID; + Parents = GetParents(proto, prototypeManager); + } + + public static string[] GetParents(EntityPrototype proto, IPrototypeManager prototypeManager) + { + var parents = new HashSet(StringComparer.Ordinal); + var visited = new HashSet(StringComparer.Ordinal); + + void Visit(string id) + { + if (string.IsNullOrWhiteSpace(id) || !visited.Add(id)) + return; + + parents.Add(id); + + if (!YAMLEntry.TryGetRawMapping(prototypeManager, typeof(EntityPrototype), id, out var mapping) || + mapping == null) + { + return; + } + + foreach (var parent in GetParentIds(mapping)) + { + Visit(parent); + } + } + + foreach (var parent in proto.Parents ?? []) + { + Visit(parent); + } + + return parents.OrderBy(x => x, StringComparer.Ordinal).ToArray(); + } + + private static IEnumerable GetParentIds(MappingDataNode mapping) + { + if (mapping.TryGet("parent", out ValueDataNode? parentValue)) + { + if (!string.IsNullOrWhiteSpace(parentValue.Value)) + yield return parentValue.Value; + + yield break; + } + + if (!mapping.TryGet("parent", out SequenceDataNode? parentSequence)) + yield break; + + foreach (var parentNode in parentSequence) + { + if (parentNode is not ValueDataNode valueNode) + continue; + + if (!string.IsNullOrWhiteSpace(valueNode.Value)) + yield return valueNode.Value; + } + } + + public static void PublishJson(StreamWriter file) + { + var prototypeManager = IoCManager.Resolve(); + var prototypes = prototypeManager + .EnumeratePrototypes() + .Where(x => !x.Abstract) + .Select(x => new EntityParentJsonGenerator(x)) + .ToDictionary(x => x.Id, x => x.Parents); + + var serializeOptions = new JsonSerializerOptions + { + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + }; + + file.Write(JsonSerializer.Serialize(prototypes, serializeOptions)); + } +} diff --git a/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs index 2549b4c5ae..e4d07d2e4c 100644 --- a/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/PrototypeJsonGenerator.cs @@ -3,9 +3,7 @@ using System.Reflection; using System.Text.Encodings.Web; using System.Text.Json; using Robust.Shared.ContentPack; -using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Markdown.Mapping; using Robust.Shared.Utility; @@ -154,8 +152,9 @@ public static class PrototypeJsonGenerator } } - return type.GetCustomAttributes(inherit: true).Any(attr => + return type.GetCustomAttributes(inherit: true) + .Any(attr => attr.GetType().Name is nameof(DataDefinitionAttribute) or nameof(SerializableAttribute)) - && HasUnsafeSerializedDataField(type, visited); + && HasUnsafeSerializedDataField(type, visited); } } diff --git a/Content.Server/Corvax/GuideGenerator/TextTools.cs b/Content.Server/Corvax/GuideGenerator/TextTools.cs index 021261ebb4..e1da52f623 100644 --- a/Content.Server/Corvax/GuideGenerator/TextTools.cs +++ b/Content.Server/Corvax/GuideGenerator/TextTools.cs @@ -1,3 +1,5 @@ +using System.Linq; +using System.Text.RegularExpressions; using Robust.Shared.Prototypes; namespace Content.Server.Corvax.GuideGenerator; @@ -60,4 +62,33 @@ public sealed class TextTools return proto.Name; } + + private static readonly Regex SuffixTokenEdgeGarbageRegex = + new(@"^[\p{P}\p{S}\s]+|[\p{P}\p{S}\s]+$", RegexOptions.Compiled); + + public static string NormalizeSuffixToken(string token) + { + return string.IsNullOrWhiteSpace(token) + ? string.Empty + : SuffixTokenEdgeGarbageRegex.Replace(token, string.Empty); + } + + public static string GetEditorSuffix( + string? editorSuffix, + IReadOnlySet ignoredTokens, + Func normalizeToken) + { + if (string.IsNullOrWhiteSpace(editorSuffix)) + return string.Empty; + + var parts = editorSuffix + .Split([',', ';'], StringSplitOptions.RemoveEmptyEntries) + .Select(part => part.Trim()) + .Where(part => !ignoredTokens.Contains(normalizeToken(part))) + .ToArray(); + + return parts.Length > 0 + ? string.Join(", ", parts).ToLowerInvariant() + : string.Empty; + } } diff --git a/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs b/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs index b5578164b1..a8a75f825a 100644 --- a/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs +++ b/Content.Server/Corvax/GuideGenerator/YAMLEntry.cs @@ -36,7 +36,7 @@ public static class YAMLEntry { mapping = null; - if (KindsField?.GetValue(proto) is not object kinds) + if (KindsField?.GetValue(proto) is not { } kinds) return false; var itemProperty = kinds.GetType().GetProperty("Item"); diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index c43ec62571..cea568ce15 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -161,7 +161,8 @@ namespace Content.Server.Entry write(file); file.Flush(); } - 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); From 750d11262ab343e441a25fc18af8c86831496755 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Date: Mon, 6 Apr 2026 22:14:37 -0700 Subject: [PATCH 119/247] fix a couple radiation system bugs as a treat --- .../Radiation/Systems/RadiationSystem.cs | 2 +- .../Anomaly/Effects/GravityAnomalySystem.cs | 7 +++--- .../Radiation/Systems/RadiationSystem.cs | 22 ++++++++----------- .../Components/RadiationSourceComponent.cs | 2 ++ .../Systems/SharedRadiationSystem.cs | 22 +++++++++++++++++++ .../EntitySystems/SharedSingularitySystem.cs | 15 ++++++------- 6 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 Content.Shared/Radiation/Systems/SharedRadiationSystem.cs diff --git a/Content.Client/Radiation/Systems/RadiationSystem.cs b/Content.Client/Radiation/Systems/RadiationSystem.cs index f4f109adc7..f719b7b5b8 100644 --- a/Content.Client/Radiation/Systems/RadiationSystem.cs +++ b/Content.Client/Radiation/Systems/RadiationSystem.cs @@ -5,7 +5,7 @@ using Robust.Client.Graphics; namespace Content.Client.Radiation.Systems; -public sealed class RadiationSystem : EntitySystem +public sealed class RadiationSystem : SharedRadiationSystem { [Dependency] private readonly IOverlayManager _overlayMan = default!; diff --git a/Content.Server/Anomaly/Effects/GravityAnomalySystem.cs b/Content.Server/Anomaly/Effects/GravityAnomalySystem.cs index 02cd0aafc6..e053377d57 100644 --- a/Content.Server/Anomaly/Effects/GravityAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/GravityAnomalySystem.cs @@ -1,9 +1,9 @@ using Content.Server.Physics.Components; +using Content.Server.Radiation.Systems; using Content.Server.Singularity.Components; using Content.Shared.Anomaly.Components; using Content.Shared.Anomaly.Effects; using Content.Shared.Anomaly.Effects.Components; -using Content.Shared.Radiation.Components; namespace Content.Server.Anomaly.Effects; @@ -12,6 +12,8 @@ namespace Content.Server.Anomaly.Effects; /// public sealed class GravityAnomalySystem : SharedGravityAnomalySystem { + [Dependency] private readonly RadiationSystem _radiation = default!; + /// public override void Initialize() { @@ -22,8 +24,7 @@ public sealed class GravityAnomalySystem : SharedGravityAnomalySystem private void OnSeverityChanged(Entity anomaly, ref AnomalySeverityChangedEvent args) { - if (TryComp(anomaly, out var radSource)) - radSource.Intensity = anomaly.Comp.MaxRadiationIntensity * args.Severity; + _radiation.SetIntensity(anomaly.Owner, anomaly.Comp.MaxRadiationIntensity * args.Severity); if (TryComp(anomaly, out var gravityWell)) { diff --git a/Content.Server/Radiation/Systems/RadiationSystem.cs b/Content.Server/Radiation/Systems/RadiationSystem.cs index 39d8d6cd33..fd1a60710d 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.cs @@ -7,10 +7,11 @@ using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Threading; using System.Numerics; +using Content.Shared.Radiation.Systems; 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!; @@ -20,9 +21,8 @@ public sealed partial class RadiationSystem : EntitySystem [Dependency] private readonly IParallelManager _parallel = default!; [Dependency] private readonly EntityQuery _receiverQuery = default!; - private EntityQuery _blockerQuery; - private EntityQuery _resistanceQuery; - private EntityQuery _stackQuery; + [Dependency] private EntityQuery _blockerQuery = default; + [Dependency] private EntityQuery _resistanceQuery = default; private readonly B2DynamicTree _sourceTree = new(); private readonly Dictionary _sourceDataMap = new(); @@ -36,20 +36,16 @@ public sealed partial class RadiationSystem : EntitySystem SubscribeCvars(); InitRadBlocking(); - _blockerQuery = GetEntityQuery(); - _resistanceQuery = GetEntityQuery(); - _stackQuery = GetEntityQuery(); - - SubscribeLocalEvent(OnSourceInit); + SubscribeLocalEvent(OnSourceStartup); SubscribeLocalEvent(OnSourceShutdown); SubscribeLocalEvent(OnSourceMove); SubscribeLocalEvent(OnSourceStackChanged); - SubscribeLocalEvent(OnReceiverInit); + SubscribeLocalEvent(OnReceiverStartup); SubscribeLocalEvent(OnReceiverShutdown); } - private void OnSourceInit(Entity entity, ref ComponentInit args) + private void OnSourceStartup(Entity entity, ref ComponentStartup args) { UpdateSource(entity); } @@ -78,7 +74,7 @@ public sealed partial class RadiationSystem : EntitySystem UpdateSource(entity); } - private void OnReceiverInit(EntityUid uid, RadiationReceiverComponent component, ComponentInit args) + private void OnReceiverStartup(EntityUid uid, RadiationReceiverComponent component, ComponentStartup args) { _activeReceivers.Add(uid); } @@ -88,7 +84,7 @@ public sealed partial class RadiationSystem : EntitySystem _activeReceivers.Remove(uid); } - private void UpdateSource(Entity entity) + protected override void UpdateSource(Entity entity) { var (uid, component) = entity; var xform = Transform(uid); diff --git a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs index 337e9c648e..367d38309f 100644 --- a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs +++ b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Radiation.Systems; using Robust.Shared.Physics; namespace Content.Shared.Radiation.Components; @@ -6,6 +7,7 @@ namespace Content.Shared.Radiation.Components; /// Irradiate all objects in range. /// [RegisterComponent] +[Access(typeof(SharedRadiationSystem))] public sealed partial class RadiationSourceComponent : Component { /// diff --git a/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs new file mode 100644 index 0000000000..caded9c3e6 --- /dev/null +++ b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs @@ -0,0 +1,22 @@ +using Content.Shared.Radiation.Components; + +namespace Content.Shared.Radiation.Systems; + +public abstract partial class SharedRadiationSystem : EntitySystem +{ + [Dependency] protected readonly EntityQuery SourceQuery = default!; + + public void SetIntensity(Entity entity, float intensity) + { + if (!SourceQuery.Resolve(entity, ref entity.Comp)) + return; + + entity.Comp.Intensity = intensity; + UpdateSource((entity, entity.Comp)); + } + + protected virtual void UpdateSource(Entity entity) + { + + } +} diff --git a/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs b/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs index b7bf071457..eccfbe58d8 100644 --- a/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs +++ b/Content.Shared/Singularity/EntitySystems/SharedSingularitySystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Shared.Radiation.Components; +using Content.Shared.Radiation.Systems; using Content.Shared.Singularity.Components; using Content.Shared.Singularity.Events; using Robust.Shared.Containers; @@ -19,6 +20,7 @@ public abstract class SharedSingularitySystem : EntitySystem [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly SharedEventHorizonSystem _horizons = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedRadiationSystem _radiation = default!; [Dependency] protected readonly IViewVariablesManager Vvm = default!; #endregion Dependencies @@ -138,10 +140,7 @@ public abstract class SharedSingularitySystem : EntitySystem _visualizer.SetData(uid, SingularityAppearanceKeys.Singularity, singularity.Level, appearance); } - if (TryComp(uid, out var radiationSource)) - { - UpdateRadiation(uid, singularity, radiationSource); - } + UpdateRadiation(uid, singularity); RaiseLocalEvent(uid, new SingularityLevelChangedEvent(singularity.Level, oldValue, singularity)); if (singularity.Level <= 0) @@ -165,12 +164,12 @@ public abstract class SharedSingularitySystem : EntitySystem /// /// The uid of the singularity to update the radiation of. /// The state of the singularity to update the radiation of. - /// The state of the radioactivity of the singularity to update. - private void UpdateRadiation(EntityUid uid, SingularityComponent? singularity = null, RadiationSourceComponent? rads = null) + private void UpdateRadiation(EntityUid uid, SingularityComponent? singularity = null) { - if(!Resolve(uid, ref singularity, ref rads, logMissing: false)) + if(!Resolve(uid, ref singularity, logMissing: false)) return; - rads.Intensity = singularity.Level * singularity.RadsPerLevel; + + _radiation.SetIntensity(uid, singularity.Level * singularity.RadsPerLevel); } #endregion Getters/Setters From 700bcbf28d39c7dee6e676491392e6307d012e8e Mon Sep 17 00:00:00 2001 From: Super <84590915+SuperGDPWYL@users.noreply.github.com> Date: Tue, 7 Apr 2026 19:44:17 +0100 Subject: [PATCH 120/247] Cause Anomalies to Go Supercritical [Traitor Objective] (#43505) * science explosions are projected to go up by 1% * eof * huh * review * newline * make it exclusive --- ...upercriticalAnomaliesConditionComponent.cs | 14 +++++++ .../SupercriticalAnomaliesConditionSystem.cs | 41 +++++++++++++++++++ .../conditions/anomaly-supercrit.ftl | 1 + .../Prototypes/Objectives/objectiveGroups.yml | 6 +++ Resources/Prototypes/Objectives/traitor.yml | 19 +++++++++ 5 files changed, 81 insertions(+) create mode 100644 Content.Server/Objectives/Components/SupercriticalAnomaliesConditionComponent.cs create mode 100644 Content.Server/Objectives/Systems/SupercriticalAnomaliesConditionSystem.cs create mode 100644 Resources/Locale/en-US/objectives/conditions/anomaly-supercrit.ftl diff --git a/Content.Server/Objectives/Components/SupercriticalAnomaliesConditionComponent.cs b/Content.Server/Objectives/Components/SupercriticalAnomaliesConditionComponent.cs new file mode 100644 index 0000000000..038150d90c --- /dev/null +++ b/Content.Server/Objectives/Components/SupercriticalAnomaliesConditionComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Objective condition that requires a certain number of anomalies (defined by ) to go supercritical while the objective is in play. +/// +[RegisterComponent] +public sealed partial class SupercriticalAnomaliesConditionComponent : Component +{ + /// + /// The number of anomalies that have gone supercritical since this objective was added. + /// + [DataField] + public int SupercriticalAnomalies = 0; +} diff --git a/Content.Server/Objectives/Systems/SupercriticalAnomaliesConditionSystem.cs b/Content.Server/Objectives/Systems/SupercriticalAnomaliesConditionSystem.cs new file mode 100644 index 0000000000..93e649def2 --- /dev/null +++ b/Content.Server/Objectives/Systems/SupercriticalAnomaliesConditionSystem.cs @@ -0,0 +1,41 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Anomaly.Components; +using Content.Shared.Objectives.Components; + +namespace Content.Server.Objectives.Systems; + +public sealed partial class SupercriticalAnomaliesConditionSystem : EntitySystem +{ + [Dependency] private readonly NumberObjectiveSystem _numberObjective = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAnomalySupercrit); + SubscribeLocalEvent(OnGetProgress); + } + + private void OnAnomalySupercrit(ref AnomalyShutdownEvent args) + { + if (!args.Supercritical) + return; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var comp)) + { + comp.SupercriticalAnomalies += 1; + } + } + + private void OnGetProgress(Entity ent, ref ObjectiveGetProgressEvent args) + { + var target = _numberObjective.GetTarget(ent); + if (target == 0) + { + args.Progress = 0f; + return; + } + args.Progress = MathF.Min((float)ent.Comp.SupercriticalAnomalies / target, 1f); + } +} diff --git a/Resources/Locale/en-US/objectives/conditions/anomaly-supercrit.ftl b/Resources/Locale/en-US/objectives/conditions/anomaly-supercrit.ftl new file mode 100644 index 0000000000..358b9e8673 --- /dev/null +++ b/Resources/Locale/en-US/objectives/conditions/anomaly-supercrit.ftl @@ -0,0 +1 @@ +objective-condition-supercrit-anomalies-title = Cause {$count} anomalies to go supercritical diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index 0480a5c9ad..9c524e57af 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -6,6 +6,7 @@ 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 - type: weightedRandom @@ -45,6 +46,11 @@ # 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 diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index f8cc42a222..6829084207 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -378,6 +378,25 @@ stealGroup: NukeDisk owner: objective-condition-steal-station +# 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 From 50b2a327bc5f97d7ed9d3100626185417b269dd3 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:47:19 -0700 Subject: [PATCH 121/247] logmissing false --- Content.Shared/Radiation/Systems/SharedRadiationSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs index caded9c3e6..ea298d313d 100644 --- a/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs +++ b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs @@ -8,7 +8,7 @@ public abstract partial class SharedRadiationSystem : EntitySystem public void SetIntensity(Entity entity, float intensity) { - if (!SourceQuery.Resolve(entity, ref entity.Comp)) + if (!SourceQuery.Resolve(entity, ref entity.Comp, false)) return; entity.Comp.Intensity = intensity; From ae916eb4d201e2cf4b160e95c55631b08b69d529 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 7 Apr 2026 19:00:25 +0000 Subject: [PATCH 122/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 2552eaea39..a086eaaaf8 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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 @@ -4033,3 +4025,10 @@ 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 From 63ce4c9be160e27c6f5dbe5dd1a81267984d57a4 Mon Sep 17 00:00:00 2001 From: KeTuFaisPiKiNut <85769816+ketufaispikinut@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:18:04 -0400 Subject: [PATCH 123/247] Travel Camera (#43322) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Travel camera * Adds TravelCamera as rare maintenance loot * Prevent Travel Camera from converting people to Revolution * Removed AoE for travel_camera * Adds Travel Camera to Uplink * Camera sprites * camera working on modern code * camera melee only * every error fixed + camera sfx * PoC photography * textures * charges, description order, fixed typo * support for colors * typos plus different photograph colors * more typos * final typos? * oh god i dont know how to write so many typos * fix lint warnings * Update Resources/Audio/Misc/attributions.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Resources/Textures/Objects/Misc/photograph.rsi/meta.json Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Content.Server/Photography/PhotographComponent.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Content.Server/Photography/PhotographComponent.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Content.Server/Photography/PhotographComponent.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * follow convention: bases shouldn't have any sprites * uses identity.entity system * Picture taker component comments. * Update Resources/Prototypes/Entities/Objects/Devices/travel_camera.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Resources/Prototypes/Entities/Objects/Devices/travel_camera.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * spaces inserted * 4-spaced indentation * 4-space indent * uses SharedChargesSystem * canconvert component instead of convertitem tag * removed mention of tags from head rev component * component no longer has a constructor * use tables + do the things on shared * fix abstract-ness warning * formatting + comments * cleanup and improvements * oops * fix warnings * 1 tc / disruption + recursion (also i'm pretty sure it can spawn in maints now) * proper recursion checking (i think!) * Added a dot for coherence. * minor cleanup * brought back the melee * fixed description * More accurate, albeit less funny camera description. * deduplicate code with flash system --------- Co-authored-by: SlamBamActionman Co-authored-by: nomdéraisonnablementlong Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Rules/RevolutionaryRuleSystem.cs | 3 +- .../RevolutionaryConverterComponent.cs | 8 ++ .../Flash/Components/FlashComponent.cs | 6 + Content.Shared/Flash/FlashEvents.cs | 8 +- Content.Shared/Flash/SharedFlashSystem.cs | 52 ++++++-- .../Photography/PhotographComponent.cs | 24 ++++ .../Photography/PhotographySystem.cs | 96 +++++++++++++++ .../Photography/PictureTakerComponent.cs | 19 +++ Resources/Audio/Misc/attributions.yml | 5 + Resources/Audio/Misc/camera_snap.ogg | Bin 0 -> 9075 bytes .../Locale/en-US/photography/photography.ftl | 7 ++ .../Locale/en-US/store/uplink-catalog.ftl | 3 + .../Prototypes/Catalog/uplink_catalog.yml | 10 ++ .../Markers/Spawners/Random/maintenance.yml | 1 + .../Objects/Devices/travel_camera.yml | 116 ++++++++++++++++++ .../Entities/Objects/Weapons/security.yml | 1 + .../travel_camera.rsi/equipped-NECK.png | Bin 0 -> 344 bytes .../Devices/travel_camera.rsi/icon.png | Bin 0 -> 754 bytes .../Devices/travel_camera.rsi/inhand-left.png | Bin 0 -> 291 bytes .../travel_camera.rsi/inhand-right.png | Bin 0 -> 297 bytes .../Devices/travel_camera.rsi/meta.json | 27 ++++ .../Objects/Misc/photograph.rsi/black.png | Bin 0 -> 212 bytes .../Objects/Misc/photograph.rsi/blue.png | Bin 0 -> 235 bytes .../Objects/Misc/photograph.rsi/green.png | Bin 0 -> 238 bytes .../Objects/Misc/photograph.rsi/meta.json | 32 +++++ .../Objects/Misc/photograph.rsi/purple.png | Bin 0 -> 243 bytes .../Objects/Misc/photograph.rsi/rainbow.png | Bin 0 -> 277 bytes .../Objects/Misc/photograph.rsi/red.png | Bin 0 -> 219 bytes .../Objects/Misc/photograph.rsi/yellow.png | Bin 0 -> 237 bytes 29 files changed, 403 insertions(+), 15 deletions(-) create mode 100644 Content.Server/Revolutionary/Components/RevolutionaryConverterComponent.cs create mode 100644 Content.Shared/Photography/PhotographComponent.cs create mode 100644 Content.Shared/Photography/PhotographySystem.cs create mode 100644 Content.Shared/Photography/PictureTakerComponent.cs create mode 100644 Resources/Audio/Misc/camera_snap.ogg create mode 100644 Resources/Locale/en-US/photography/photography.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Devices/travel_camera.yml create mode 100644 Resources/Textures/Objects/Devices/travel_camera.rsi/equipped-NECK.png create mode 100644 Resources/Textures/Objects/Devices/travel_camera.rsi/icon.png create mode 100644 Resources/Textures/Objects/Devices/travel_camera.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Devices/travel_camera.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Devices/travel_camera.rsi/meta.json create mode 100644 Resources/Textures/Objects/Misc/photograph.rsi/black.png create mode 100644 Resources/Textures/Objects/Misc/photograph.rsi/blue.png create mode 100644 Resources/Textures/Objects/Misc/photograph.rsi/green.png create mode 100644 Resources/Textures/Objects/Misc/photograph.rsi/meta.json create mode 100644 Resources/Textures/Objects/Misc/photograph.rsi/purple.png create mode 100644 Resources/Textures/Objects/Misc/photograph.rsi/rainbow.png create mode 100644 Resources/Textures/Objects/Misc/photograph.rsi/red.png create mode 100644 Resources/Textures/Objects/Misc/photograph.rsi/yellow.png diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs index cf74513c87..f0cf3087ff 100644 --- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs @@ -146,7 +146,8 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem(ev.Target) && !alwaysConvertible || !_mobState.IsAlive(ev.Target) || - HasComp(ev.Target)) + HasComp(ev.Target) || + !HasComp(ev.Used)) { return; } diff --git a/Content.Server/Revolutionary/Components/RevolutionaryConverterComponent.cs b/Content.Server/Revolutionary/Components/RevolutionaryConverterComponent.cs new file mode 100644 index 0000000000..61acf26d5b --- /dev/null +++ b/Content.Server/Revolutionary/Components/RevolutionaryConverterComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.Revolutionary.Components; + +/// +/// This is a marker component that indicates that a flash can be used to convert someone into a revolutionary. +/// Viva la revolution! +/// +[RegisterComponent] +public sealed partial class RevolutionaryConverterComponent : Component; diff --git a/Content.Shared/Flash/Components/FlashComponent.cs b/Content.Shared/Flash/Components/FlashComponent.cs index d1a8b882d9..9484add5ef 100644 --- a/Content.Shared/Flash/Components/FlashComponent.cs +++ b/Content.Shared/Flash/Components/FlashComponent.cs @@ -16,6 +16,12 @@ public sealed partial class FlashComponent : Component [DataField, AutoNetworkedField] public bool FlashOnUse = true; + /// + /// Flash the area around the entity when the flash is used with ranged interaction? + /// + [DataField, AutoNetworkedField] + public bool FlashOnRangedInteract = false; + /// /// Flash the target when melee attacking them? /// diff --git a/Content.Shared/Flash/FlashEvents.cs b/Content.Shared/Flash/FlashEvents.cs index 1c18ca1676..70a61765ff 100644 --- a/Content.Shared/Flash/FlashEvents.cs +++ b/Content.Shared/Flash/FlashEvents.cs @@ -13,9 +13,15 @@ public record struct FlashAttemptEvent(EntityUid Target, EntityUid? User, Entity } /// -/// Called when a player is successfully flashed. +/// Called when a player is successfully flashed, once for each flashed player. /// Raised on the target hit by the flash, the user of the flash and the flash used. /// The Melee parameter is used to check for rev conversion. /// [ByRefEvent] public record struct AfterFlashedEvent(EntityUid Target, EntityUid? User, EntityUid? Used, bool Melee); + +/// +/// Raised once on the flash entity when it was used, regardless of the flashed status being applied or not. +/// +[ByRefEvent] +public record struct AfterFlashActivatedEvent(EntityUid? Target, EntityUid? User); diff --git a/Content.Shared/Flash/SharedFlashSystem.cs b/Content.Shared/Flash/SharedFlashSystem.cs index 4b8ff3ab7b..da43284bee 100644 --- a/Content.Shared/Flash/SharedFlashSystem.cs +++ b/Content.Shared/Flash/SharedFlashSystem.cs @@ -1,13 +1,18 @@ +using System.Linq; using Content.Shared.Charges.Components; using Content.Shared.Charges.Systems; +using Content.Shared.Clothing.Components; using Content.Shared.Examine; using Content.Shared.Eye.Blinding.Components; using Content.Shared.Flash.Components; using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; using Content.Shared.Light; +using Content.Shared.Movement.Systems; using Content.Shared.Popups; +using Content.Shared.Random.Helpers; using Content.Shared.StatusEffect; using Content.Shared.Stunnable; using Content.Shared.Tag; @@ -17,12 +22,7 @@ using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; -using Robust.Shared.Random; using Robust.Shared.Timing; -using System.Linq; -using Content.Shared.Movement.Systems; -using Content.Shared.Random.Helpers; -using Content.Shared.Clothing.Components; namespace Content.Shared.Flash; @@ -57,6 +57,7 @@ public abstract class SharedFlashSystem : EntitySystem SubscribeLocalEvent(OnFlashMeleeHit); SubscribeLocalEvent(OnFlashUseInHand); + SubscribeLocalEvent(OnRangedInteract); SubscribeLocalEvent(OnLightToggle); SubscribeLocalEvent(OnPermanentBlindnessFlashAttempt); SubscribeLocalEvent(OnTemporaryBlindnessFlashAttempt); @@ -72,7 +73,7 @@ public abstract class SharedFlashSystem : EntitySystem if (!ent.Comp.FlashOnMelee || !args.IsHit || !args.HitEntities.Any() || - !UseFlash(ent, args.User)) + !TryUseFlashItem(ent.AsNullable(), args.User)) { return; } @@ -82,41 +83,66 @@ public abstract class SharedFlashSystem : EntitySystem { Flash(target, args.User, ent.Owner, ent.Comp.MeleeDuration, ent.Comp.SlowTo, melee: true, stunDuration: ent.Comp.MeleeStunDuration); } + + EntityUid? firstTarget = args.HitEntities.Count > 0 ? args.HitEntities[0] : null; // Just pick the first hit entity. + var ev = new AfterFlashActivatedEvent(firstTarget, args.User); + RaiseLocalEvent(ent, ref ev); } private void OnFlashUseInHand(Entity ent, ref UseInHandEvent args) { - if (!ent.Comp.FlashOnUse || args.Handled || !UseFlash(ent, args.User)) + if (!ent.Comp.FlashOnUse || args.Handled || !TryUseFlashItem(ent.AsNullable(), args.User)) return; args.Handled = true; FlashArea(ent.Owner, args.User, ent.Comp.Range, ent.Comp.AoeFlashDuration, ent.Comp.SlowTo, true, ent.Comp.Probability); + var ev = new AfterFlashActivatedEvent(null, args.User); // No direct target. + RaiseLocalEvent(ent, ref ev); + } + + // TODO: This or most of the other systems subscribing to BeforeRangedInteractEvent shouldn't be using this event as handling it stops contact interaction with the used tool, + // but this will need some cleanup of how SharedInteractionSystem handles the code flow. Also the event is raised for both in-range and out of range interactions, which + // is what the subscribers are using it for, but does not seem originally intended from the naming convention. + private void OnRangedInteract(Entity ent, ref BeforeRangedInteractEvent args) + { + if (!ent.Comp.FlashOnRangedInteract || args.Handled || !TryUseFlashItem(ent.AsNullable(), args.User)) + return; + + args.Handled = true; + FlashArea(ent.Owner, args.User, ent.Comp.Range, ent.Comp.AoeFlashDuration, ent.Comp.SlowTo, true, ent.Comp.Probability); + var ev = new AfterFlashActivatedEvent(args.Target, args.User); + RaiseLocalEvent(ent, ref ev); } // needed for the flash lantern and interrogator lamp // TODO: This is awful and all the different components for toggleable lights need to be unified and changed to use Itemtoggle private void OnLightToggle(Entity ent, ref LightToggleEvent args) { - if (!args.IsOn || !UseFlash(ent, null)) + if (!args.IsOn || !TryUseFlashItem(ent.AsNullable(), null)) return; FlashArea(ent.Owner, null, ent.Comp.Range, ent.Comp.AoeFlashDuration, ent.Comp.SlowTo, true, ent.Comp.Probability); + var ev = new AfterFlashActivatedEvent(null, null); // TODO: Add user once someone made toggleable lights not a total mess. + RaiseLocalEvent(ent, ref ev); } /// - /// Use charges and set the visuals. + /// Try to use charges, play the sound and set the visuals of a flash item. + /// This does not actually cause the flash status effect by itself, you will need to either call or as well. /// /// False if no charges are left or the flash is currently in use. - private bool UseFlash(Entity ent, EntityUid? user) + public bool TryUseFlashItem(Entity ent, EntityUid? user) { + if (!Resolve(ent, ref ent.Comp)) + return false; + if (_useDelay.IsDelayed(ent.Owner)) return false; if (TryComp(ent.Owner, out var charges) - && _sharedCharges.IsEmpty((ent.Owner, charges))) + && !_sharedCharges.TryUseCharge((ent.Owner, charges))) return false; - _sharedCharges.TryUseCharge((ent.Owner, charges)); _audio.PlayPredicted(ent.Comp.Sound, ent.Owner, user); var active = EnsureComp(ent.Owner); @@ -126,7 +152,7 @@ public abstract class SharedFlashSystem : EntitySystem if (_sharedCharges.IsEmpty((ent.Owner, charges))) { - _appearance.SetData(ent.Owner, FlashVisuals.Burnt, true); + _appearance.SetData(ent.Owner, FlashVisuals.Burnt, true); // TODO: Reset if charges are refilled. _tag.AddTag(ent.Owner, TrashTag); _popup.PopupClient(Loc.GetString("flash-component-becomes-empty"), user); } diff --git a/Content.Shared/Photography/PhotographComponent.cs b/Content.Shared/Photography/PhotographComponent.cs new file mode 100644 index 0000000000..3fb065a6e7 --- /dev/null +++ b/Content.Shared/Photography/PhotographComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Utility; + +namespace Content.Shared.Photography; + +/// +/// Represents the photograph data on a picture. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PhotographComponent : Component +{ + /// + /// The description of the photographed object. + /// + [DataField, AutoNetworkedField] + public FormattedMessage? Description; + + /// + /// The full text mentioning the name of the photographed object. + /// For example "This is a picture of Urist McHands" + /// + [DataField, AutoNetworkedField] + public string? NameText; +} diff --git a/Content.Shared/Photography/PhotographySystem.cs b/Content.Shared/Photography/PhotographySystem.cs new file mode 100644 index 0000000000..e8b2334b25 --- /dev/null +++ b/Content.Shared/Photography/PhotographySystem.cs @@ -0,0 +1,96 @@ +using Content.Shared.EntityTable; +using Content.Shared.Examine; +using Content.Shared.Flash; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.IdentityManagement; +using Robust.Shared.Network; +using Robust.Shared.Utility; + +namespace Content.Shared.Photography; + +/// +/// Handles everything related to photography. +/// +public sealed class PhotographySystem : EntitySystem +{ + [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly EntityTableSystem _tables = default!; + [Dependency] private readonly INetManager _net = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnFlashActivated); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + using (args.PushGroup(nameof(PhotographComponent))) + { + if (string.IsNullOrEmpty(ent.Comp.NameText)) + args.PushText(Loc.GetString("photograph-name-text-empty")); + else + args.PushText(ent.Comp.NameText); + if (ent.Comp.Description != null) + // TODO: For some weird reason ExamineSystem is adding a new line at the end of message we are pushing with each examine. + // I'm not soaping this PR even more, so for now I'll just bandaid that by sending a clone to prevent it from getting modified. + args.PushMessage(new FormattedMessage(ent.Comp.Description)); + } + } + + // The flash system is handling charges and all interactions, we just print the picture afterwards. + private void OnFlashActivated(Entity ent, ref AfterFlashActivatedEvent args) + { + TakePicture(ent, args.Target, args.User); + } + + /// + /// Processes entity aimed at with a camera and prints a picture of it. + /// TODO: This is basically a placeholder mechanic for a more elaborate photography system. + /// However, this will need major refactoring to be possible. See + /// https://github.com/space-wizards/docs/pull/307 and + /// https://github.com/space-wizards/space-station-14/pull/43327 + /// for details. + /// + public void TakePicture(Entity camera, EntityUid? target, EntityUid? user) + { + if (_net.IsClient) + return; // Can't interact with predictively spawned entities yet. + + var tableResult = _tables.GetSpawns(camera.Comp.Photographs); + var coords = Transform(camera).Coordinates; + + FormattedMessage? description = null; + string? nameText = null; + if (target != null) + { + description = _examine.GetExamineText(target.Value, user); + // Get the full string now instead of indexing it later because we need the entity to know if it uses a proper noun or not. + nameText = Loc.GetString("photograph-name-text", ("entity", Identity.Entity(target.Value, EntityManager))); + // We don't want photographs to contain the descriptions of other photographs, because that makes entities with, in theory, infinite descriptions. + if (HasComp(target.Value)) + { + description = null; + nameText = Loc.GetString("photograph-name-text-photograph"); + } + } + + foreach (var prototype in tableResult) + { + // we generate an individual photograph (there should be only one tough) + var spawned = Spawn(prototype, coords); + var photoComp = EnsureComp(spawned); + photoComp.NameText = nameText; + photoComp.Description = description; + Dirty(spawned, photoComp); + + _hands.PickupOrDrop(user, spawned, dropNear: true); + } + } +} diff --git a/Content.Shared/Photography/PictureTakerComponent.cs b/Content.Shared/Photography/PictureTakerComponent.cs new file mode 100644 index 0000000000..a636b1ede2 --- /dev/null +++ b/Content.Shared/Photography/PictureTakerComponent.cs @@ -0,0 +1,19 @@ +using Content.Shared.EntityTable.EntitySelectors; +using Robust.Shared.GameStates; + +namespace Content.Shared.Photography; + +// since camera is taken... +/// +/// Marks an entity as able to take pictures (when you smash other entities with it). +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PictureTakerComponent : Component +{ + /// + /// The entities that will be spawned & given a PhotographComponent when the owning entity is used. + /// The table should only select one item at a time. + /// + [DataField] + public EntityTableSelector? Photographs; +} diff --git a/Resources/Audio/Misc/attributions.yml b/Resources/Audio/Misc/attributions.yml index 8fda833b1d..ada6b70983 100644 --- a/Resources/Audio/Misc/attributions.yml +++ b/Resources/Audio/Misc/attributions.yml @@ -87,3 +87,8 @@ license: "CC-BY-SA-3.0" copyright: "Taken from Citadel station" source: "https://github.com/Citadel-Station-13/Citadel-Station-13/blob/e31455667ebf3331255c1143e1e7215bc737a287/sound/misc/deltakalaxon.ogg" + +- files: ["camera_snap.ogg"] + license: "CC-BY-4.0" + copyright: "Taken from freesound, edited by ketufaispikinut(GitHub)" + source: "https://freesound.org/people/thecheeseman/sounds/51360/" diff --git a/Resources/Audio/Misc/camera_snap.ogg b/Resources/Audio/Misc/camera_snap.ogg new file mode 100644 index 0000000000000000000000000000000000000000..5730547ad4551c07b3f6db876f150c9caa59f408 GIT binary patch literal 9075 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 literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/photography/photography.ftl b/Resources/Locale/en-US/photography/photography.ftl new file mode 100644 index 0000000000..1876fb194d --- /dev/null +++ b/Resources/Locale/en-US/photography/photography.ftl @@ -0,0 +1,7 @@ +# TODO: Make this a fluent function in RT +photograph-name-text = This is a photograph of { PROPER($entity) -> + *[false] { INDEFINITE($entity) } { $entity } + [true] { $entity } + }. +photograph-name-text-empty = This is a photograph. +photograph-name-text-photograph = This is a photograph of another photograph. diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 685a4d83dd..60f13dbc42 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -409,6 +409,9 @@ uplink-soap-desc = An untrustworthy bar of soap. Smells of fear. uplink-ultrabright-lantern-name = Extra-Bright Lantern uplink-ultrabright-lantern-desc = This ultra-bright lantern can be used to blind people, similar to a flash. +uplink-travel-camera-name = Travel Camera +uplink-travel-camera-desc = Stun people with your photography skills and the conveniently legal camera flash. Makes you look like a tourist. + uplink-combat-medkit-name = Combat Medical Kit uplink-combat-medkit-desc = A medkit made for fixing combat injuries. diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 98c2206f6a..6429a1d4a0 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 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index 99d9a4be99..111bc041d2 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/Objects/Devices/travel_camera.yml b/Resources/Prototypes/Entities/Objects/Devices/travel_camera.yml new file mode 100644 index 0000000000..5ec8b87ae0 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/travel_camera.yml @@ -0,0 +1,116 @@ +- type: entity + parent: BaseItem + id: TravelCamera + name: travel camera + description: A picture says more than a thousand words. Comes with an ultrabright flash and internal recharging photo roll. + components: + # aptly named component for picture taking + - type: PictureTaker + photographs: !type:GroupSelector + children: + - !type:NestedSelector + tableId: TravelCameraPhotographs + - type: Sprite + sprite: Objects/Devices/travel_camera.rsi + state: icon + - type: Flash + flashOnRangedInteract: true + sound: !type:SoundPathSpecifier + path: /Audio/Misc/camera_snap.ogg + - type: MeleeWeapon # allows using the camera in melee + damage: + types: + Blunt: 0 + - type: LimitedCharges + maxCharges: 4 + - type: AutoRecharge + rechargeDuration: 60 + - type: UseDelay + - type: StaticPrice + price: 100 + - type: Clothing + slots: + - NECK + sprite: Objects/Devices/travel_camera.rsi + +# we have a base prototype so that if we need to add components later on its easier +- type: entity + abstract: true + parent: BasePaper + id: BasePhotograph + name: photograph + +- type: entity + parent: BasePhotograph + id: PhotographBlack + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: black + +- type: entity + parent: BasePhotograph + id: PhotographRed + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: red + +- type: entity + parent: BasePhotograph + id: PhotographBlue + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: blue + +- type: entity + parent: BasePhotograph + id: PhotographGreen + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: green + +- type: entity + parent: BasePhotograph + id: PhotographYellow + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: yellow + +- type: entity + parent: BasePhotograph + id: PhotographPurple + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: purple + +- type: entity + parent: BasePhotograph + id: PhotographRainbow + components: + - type: Sprite + sprite: Objects/Misc/photograph.rsi + layers: + - state: rainbow + +- type: entityTable + id: TravelCameraPhotographs + table: !type:GroupSelector + children: + - id: PhotographBlack + - id: PhotographRed + - id: PhotographBlue + - id: PhotographGreen + - id: PhotographYellow + - id: PhotographPurple + - id: PhotographRainbow diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index 2f6ac834ba..6d5830e161 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -145,6 +145,7 @@ visible: false shader: unshaded - type: Flash + - type: RevolutionaryConverter - type: LimitedCharges maxCharges: 5 - type: MeleeWeapon diff --git a/Resources/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 0000000000000000000000000000000000000000..03ae539d6f846992c6bcde26ce226372b47982eb GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zg*;sxLn`LH zy=9ws$Ux-4NA^`mCL}xbGeooLF$AyN<^Fc2mSIWW_A@y*ox}y6NHK0-mFVdQ&MBb@04^JiOaK4? literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2e13f71cfa0f859ccf7df3346792d63398af3128 GIT binary patch literal 754 zcmVPx%tVu*cR9J=WmAz{daTv!x*W~hY>D>jDTBJ=U2LnPD-AZa+x@b#j)j^7=^p6l1 zaS;bcaVn-15d;;fTWx}&x(PZ+g-+(E&`O2mu8pZ_at`SoX>(0_E)M29{qFfaFW={R ze$R8zMHgNC?+~1X(dZcf6h%f+6w{4EUDpL&Hvm{!`Pxyx$4NLiI0#_sy5TB#gMeWI z=AMs^aIC3cP&vrbV7kji4wp0J?72 zWj|=E1QnrJ(#?S1$ISRJvkS{dCr~UI;P;bGjd1(UJ(7ty*<79n_wRCT`WBJMkR9mR z(4rwv~2-EG7;a`ib!OLWFl^pvt*i9u!#ZG z1rMU=6^fce&yRgjVcnm=B`9z8(!wjpk$=(WZ zBoI`Oaa;;HKCswsFLLEOnLaLQ!VTS4~ilUl7#v1A~M-u5jhm$#9mkUehAkT0C3e(oDr zPsK1!4xlL8PA>3-`-NI@-SMtk-L=n$5Gb>`x#_qEJ}rDe{#YT?w?=d=h<_s7qN8aA zBuV0Rhcyg`{0l}$QvjwlUT)B2BP=d1b=2=ThBt3qcSYdfi2gMJNs~wSy!y0hs7ls> zK$ZXIrN7SS7H^9d1eyZ`d(?~?dwmlA?oEH3-kW!JWqkES&NsKBSC%AOuDy`9dvef9 zg}Z+7&$>6ZJd9jwJ+m*wsy}6m6Hv`UbAiitCpQ#Li7eLqne_8*-6gfmlH{y~ORnbZ zSiAlAzk9o1TZM+dy#uiZM1C-OYyBtxeC4cb=LIv*oXnDW5*DSu-qimu^AWqf^Xzy3 e1FAVtzk`8!d*XuE6U489^mw}ZxvXFVdQ&MBb@08{*a&Hw-a literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/travel_camera.rsi/meta.json b/Resources/Textures/Objects/Devices/travel_camera.rsi/meta.json new file mode 100644 index 0000000000..acd1d82967 --- /dev/null +++ b/Resources/Textures/Objects/Devices/travel_camera.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation, edited by SlamBamActionman", + "size": { + "x": 32, + "y": 32 + }, + + "states": [ + { + "name": "equipped-NECK", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/black.png b/Resources/Textures/Objects/Misc/photograph.rsi/black.png new file mode 100644 index 0000000000000000000000000000000000000000..2e1284ebeb77d5076b10d6f1d0ad2feb1a976368 GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}eV#6kArY-_ zC+`+KtRUcWUG8c@v1ovTAg7=uYtTVn-y0K|*(cSg|1^GFt9kkzH-kdUg!<=?IIlpVO9of4A}rCqqNW8n24u&P@ErBrdM@kMYm^$=W#O(s!0c zj~zbnP6;~H+|K%Jj=;pLo40ejwEGDLT)n%0-KzE$4#k$5Io9Ft<=5F-|8W4ijKR~@ K&t;ucLK6VDx>2qG literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d32bb7534c55ab9649988d4ca0a13f9723c70884 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}OFdm2Ln2z= zPEq7za^!LSFZ6`Vz*pblLp8^`1GdNdrR<+8R_M%o!#XRF!$#xIJEz^u9KW7?dH%gK zjp2CVociS#TatUq1!tZyFb?v{|E_1sq|_vEC`Il0nw3{L85jf?-i~2Qe_pqG?PYG8 zo%i~f*H<;RhAt4h$`bbWT{+XWrwt$3_?DYC?5%vM(-_3nkx}xkkn!~*Ev7SnJ}pjs i7U|UY*y7yvN`~jF6r417&+-O3n8DN4&t;ucLK6Vl!d(^s literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6635d990ece133881308ab5823b38edded2a2f38 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}D?D8sLn2z= zPT9!IWXR)c-!bXdCnk~W8xHv%RC*b_)c?4MkK;k6EjkH1m$i3wz2N(;r13#}^7{AU zC%FVz96!8Yqw{diXJ;2R<1;-$r;hKH?r!K%SZXqJ<<=;zTqXtwmm72W&+fimvFX}P z*0?+Cf7Myss=gN27*`^EKtK*h_SguZ5KI8iZdQ4|* meqS{Bth7*pqv^qG8^$%R3jBK#rsM+M%;4$j=d#Wzp$Pzu6JK2b literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/meta.json b/Resources/Textures/Objects/Misc/photograph.rsi/meta.json new file mode 100644 index 0000000000..a76a07d169 --- /dev/null +++ b/Resources/Textures/Objects/Misc/photograph.rsi/meta.json @@ -0,0 +1,32 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by ketufaispikinut (Github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "black" + }, + { + "name": "red" + }, + { + "name": "blue" + }, + { + "name": "green" + }, + { + "name": "yellow" + }, + { + "name": "purple" + }, + { + "name": "rainbow" + } + ] +} diff --git a/Resources/Textures/Objects/Misc/photograph.rsi/purple.png b/Resources/Textures/Objects/Misc/photograph.rsi/purple.png new file mode 100644 index 0000000000000000000000000000000000000000..6bf8b7b25276d0b744b865edeacbc41c8cc7aaa0 GIT binary patch literal 243 zcmVPx#tw}^dR9J=WlrahdF%U(+V&MsfDQql+y@1$xsPw7Q=TNqS7qGP$hCP6l+PEON ztWL~o;k+i0eEz&*2;ex5^Y2V7;TYppLHt)6`mL7w9<7_s;^@n0xHXm8mAMIPx#&q+i&7^FnpLv z)6}Jss#6;9JP*2EhQXE~8`C=VSoNDENpd}N#AyPz?DkU*OUtfNWnG_>=Xs$X=LwWe zX#&3Q|4yKN92CA=YzMN882}?TKxeUG!sI&w*4n$iK+%Uv5fG85AB~8-C8Q{d bqI{hTOh_^>G`#X{00000NkvXXu0mjf4To@_ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9df1683989142f49b813fced609a7ec08a8aa9af GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Q$1ZALn2z= zPT9!IWXR)c|H91TzZ Q4d_4yPgg&ebxsLQ0Ch)EXaE2J literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6cfc8c71ed78430d756aea89eed86494b8367484 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}%ROBjLn2z= zPBG+SG8Ay#-<_Zyc34BF;@Fp!m!9Xy9q_x5xIut_fmzp4z170kBP`Z)dba(K7i6;N zd;G^*_;f+fM=hmh<1;-|F5TW4+}+Tj&|+XQxp!Z^AzyzpEp=VYVet8M kf7?uZp(2^%g{z90XE7?ikEmgK19UQjr>mdKI;Vst0H&B Date: Tue, 7 Apr 2026 20:34:33 +0000 Subject: [PATCH 124/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a086eaaaf8..4522cc66e9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- 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 @@ -4032,3 +4023,10 @@ 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 From e83ea368db0d0aba36886d225813eab27b105bae Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:27:48 -0700 Subject: [PATCH 125/247] Remove unused isfirsttimepredicted checks (#43509) --- .../Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs index eb80b26b58..ffd1d71cc0 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs @@ -67,7 +67,6 @@ public abstract partial class SharedGunSystem { if (args.Handled || !component.MayTransfer || - !Timing.IsFirstTimePredicted || args.Target == null || args.Used == args.Target || Deleted(args.Target) || @@ -356,11 +355,8 @@ public abstract partial class SharedGunSystem public void UpdateBallisticAppearance(Entity ent) { - if (!Timing.IsFirstTimePredicted || !TryComp(ent, out var appearance)) - return; - - Appearance.SetData(ent, AmmoVisuals.AmmoCount, GetBallisticShots(ent.Comp), appearance); - Appearance.SetData(ent, AmmoVisuals.AmmoMax, ent.Comp.Capacity, appearance); + Appearance.SetData(ent, AmmoVisuals.AmmoCount, GetBallisticShots(ent.Comp)); + Appearance.SetData(ent, AmmoVisuals.AmmoMax, ent.Comp.Capacity); } public void SetBallisticUnspawned(Entity entity, int count) From 9261dc2e9b7e03125337a4662abe39c5d240584c Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:24:54 +0200 Subject: [PATCH 126/247] remove redundant IsServer and IsClient checks (#43511) remove redundant checks --- .../FeedbackPopup/ClientFeedbackManager.cs | 5 +---- .../FeedbackSystem/ServerFeedbackManager.cs | 12 ------------ Content.Server/Sound/EmitSoundSystem.cs | 5 ----- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/Content.Client/FeedbackPopup/ClientFeedbackManager.cs b/Content.Client/FeedbackPopup/ClientFeedbackManager.cs index dd390cc2d4..5b2b52b1b7 100644 --- a/Content.Client/FeedbackPopup/ClientFeedbackManager.cs +++ b/Content.Client/FeedbackPopup/ClientFeedbackManager.cs @@ -31,7 +31,7 @@ public sealed class ClientFeedbackManager : SharedFeedbackManager /// public override void Display(List>? prototypes) { - if (prototypes == null || !NetManager.IsClient) + if (prototypes == null) return; var count = _displayedPopups.Count; @@ -42,9 +42,6 @@ public sealed class ClientFeedbackManager : SharedFeedbackManager /// public override void Remove(List>? prototypes) { - if (!NetManager.IsClient) - return; - if (prototypes == null) { _displayedPopups.Clear(); diff --git a/Content.Server/FeedbackSystem/ServerFeedbackManager.cs b/Content.Server/FeedbackSystem/ServerFeedbackManager.cs index 09edd8eefe..4ecb22bcc2 100644 --- a/Content.Server/FeedbackSystem/ServerFeedbackManager.cs +++ b/Content.Server/FeedbackSystem/ServerFeedbackManager.cs @@ -29,9 +29,6 @@ public sealed class ServerFeedbackManager : SharedFeedbackManager /// public override void SendToSession(ICommonSession session, List> popupPrototypes, bool remove = false) { - if (!NetManager.IsServer) - return; - var msg = new FeedbackPopupMessage { FeedbackPrototypes = popupPrototypes, @@ -44,9 +41,6 @@ public sealed class ServerFeedbackManager : SharedFeedbackManager /// public override void SendToAllSessions(List> popupPrototypes, bool remove = false) { - if (!NetManager.IsServer) - return; - var msg = new FeedbackPopupMessage { FeedbackPrototypes = popupPrototypes, @@ -59,9 +53,6 @@ public sealed class ServerFeedbackManager : SharedFeedbackManager /// public override void OpenForSession(ICommonSession session) { - if (!NetManager.IsServer) - return; - var msg = new OpenFeedbackPopupMessage(); NetManager.ServerSendMessage(msg, session.Channel); } @@ -69,9 +60,6 @@ public sealed class ServerFeedbackManager : SharedFeedbackManager /// public override void OpenForAllSessions() { - if (!NetManager.IsServer) - return; - var msg = new OpenFeedbackPopupMessage(); NetManager.ServerSendToAll(msg); } diff --git a/Content.Server/Sound/EmitSoundSystem.cs b/Content.Server/Sound/EmitSoundSystem.cs index 1720d67d02..38878b147f 100644 --- a/Content.Server/Sound/EmitSoundSystem.cs +++ b/Content.Server/Sound/EmitSoundSystem.cs @@ -1,14 +1,12 @@ using Content.Shared.Sound; using Content.Shared.Sound.Components; using Robust.Shared.Timing; -using Robust.Shared.Network; namespace Content.Server.Sound; public sealed class EmitSoundSystem : SharedEmitSoundSystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly INetManager _net = default!; public override void Update(float frameTime) { @@ -49,9 +47,6 @@ public sealed class EmitSoundSystem : SharedEmitSoundSystem private void SpamEmitSoundReset(Entity entity) { - if (_net.IsClient) - return; - entity.Comp.NextSound = _timing.CurTime + ((entity.Comp.MinInterval < entity.Comp.MaxInterval) ? Random.Next(entity.Comp.MinInterval, entity.Comp.MaxInterval) : entity.Comp.MaxInterval); From e1f43b1a6a8169323f50613cdd9f432c8582cdd5 Mon Sep 17 00:00:00 2001 From: alexalexmax <149889301+alexalexmax@users.noreply.github.com> Date: Tue, 7 Apr 2026 23:58:35 -0400 Subject: [PATCH 127/247] Fix unsticking items with StickySystem (#43514) * fix it * change to the surface the sticky thing is on --------- Co-authored-by: seanpimble <149889301+seanpimble@users.noreply.github.com> --- Content.Shared/Sticky/Systems/StickySystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Sticky/Systems/StickySystem.cs b/Content.Shared/Sticky/Systems/StickySystem.cs index bc04c81f55..f060cfe700 100644 --- a/Content.Shared/Sticky/Systems/StickySystem.cs +++ b/Content.Shared/Sticky/Systems/StickySystem.cs @@ -102,7 +102,7 @@ public sealed class StickySystem : EntitySystem private void OnStickyDoAfter(Entity ent, ref StickyDoAfterEvent args) { - // target is the sticky item when unsticking and the surface when sticking, it will never be null + // target is the surface when sticking/unsticking, it will never be null if (args.Handled || args.Cancelled || args.Args.Target is not {} target) return; @@ -141,7 +141,7 @@ public sealed class StickySystem : EntitySystem } // start unsticking object - _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, comp.UnstickDelay, new StickyDoAfterEvent(), uid, target: uid) + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, comp.UnstickDelay, new StickyDoAfterEvent(), uid, target: stuckTo) { BreakOnMove = true, NeedHand = true, From 1b402f704b5f89b0ebc2e87d9309454091616fcd Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 8 Apr 2026 04:14:26 +0000 Subject: [PATCH 128/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4522cc66e9..cd2045dfa6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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! @@ -4030,3 +4023,10 @@ 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 From 6d08929e19458eea234db0399e57b407627229a4 Mon Sep 17 00:00:00 2001 From: TriviaSolari <154280615+TriviaSolari@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:25:01 -0400 Subject: [PATCH 129/247] Add notification for AI core being damaged (#43513) * Add notification for AI core being damaged * No need to check damagePercent --- Content.Server/Silicons/StationAi/StationAiSystem.cs | 11 +++++++++-- Resources/Locale/en-US/silicons/station-ai.ftl | 1 + Resources/Prototypes/Chat/notifications.yml | 9 ++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Content.Server/Silicons/StationAi/StationAiSystem.cs b/Content.Server/Silicons/StationAi/StationAiSystem.cs index cc2f7a4e2f..b16c98f56b 100644 --- a/Content.Server/Silicons/StationAi/StationAiSystem.cs +++ b/Content.Server/Silicons/StationAi/StationAiSystem.cs @@ -67,6 +67,7 @@ public sealed class StationAiSystem : SharedStationAiSystem private readonly ProtoId _aiWireSnippedChatNotificationPrototype = "AiWireSnipped"; private readonly ProtoId _aiLosingPowerChatNotificationPrototype = "AiLosingPower"; private readonly ProtoId _aiCriticalPowerChatNotificationPrototype = "AiCriticalPower"; + private readonly ProtoId _aiTakingDamageChatNotificationPrototype = "AiTakingDamage"; private readonly ProtoId _stationAiJob = "StationAi"; private readonly EntProtoId _stationAiBrain = "StationAiBrain"; @@ -235,7 +236,7 @@ public sealed class StationAiSystem : SharedStationAiSystem private void OnDamageChanged(Entity entity, ref DamageChangedEvent args) { - UpdateCoreIntegrityAlert(entity); + UpdateCoreIntegrityAlert(entity, args.DamageIncreased); UpdateDamagedAccent(entity); } @@ -285,7 +286,7 @@ public sealed class StationAiSystem : SharedStationAiSystem } } - private void UpdateCoreIntegrityAlert(Entity ent) + private void UpdateCoreIntegrityAlert(Entity ent, bool damageIncreased = false) { if (!TryComp(ent, out var damageable)) return; @@ -303,6 +304,12 @@ public sealed class StationAiSystem : SharedStationAiSystem var damageLevel = Math.Round(damagePercent.Float() * proto.MaxSeverity); _alerts.ShowAlert(held.Value, _damageAlert, (short)Math.Clamp(damageLevel, 0, proto.MaxSeverity)); + + if (damageIncreased) + { + var ev = new ChatNotificationEvent(_aiTakingDamageChatNotificationPrototype, ent); + RaiseLocalEvent(held.Value, ref ev); + } } private void OnDoAfterAttempt(Entity ent, ref DoAfterAttemptEvent args) diff --git a/Resources/Locale/en-US/silicons/station-ai.ftl b/Resources/Locale/en-US/silicons/station-ai.ftl index cbe3ef6ec0..1f35af41a8 100644 --- a/Resources/Locale/en-US/silicons/station-ai.ftl +++ b/Resources/Locale/en-US/silicons/station-ai.ftl @@ -8,6 +8,7 @@ station-ai-has-no-power-for-upload = Upload failed - the AI core is unpowered. station-ai-is-too-damaged-for-upload = Upload failed - the AI core must be repaired. station-ai-core-losing-power = Your AI core is now running on reserve battery power. station-ai-core-critical-power = Your AI core is critically low on power. External power must be re-established or severe data corruption may occur! +station-ai-core-taking-damage = Your AI core is sustaining physical damage. # Ghost role station-ai-ghost-role-name = Station AI diff --git a/Resources/Prototypes/Chat/notifications.yml b/Resources/Prototypes/Chat/notifications.yml index cea67fa0ee..f155b99433 100644 --- a/Resources/Prototypes/Chat/notifications.yml +++ b/Resources/Prototypes/Chat/notifications.yml @@ -32,4 +32,11 @@ message: station-ai-core-critical-power sound: /Audio/Effects/alert.ogg color: Red - nextDelay: 120 \ No newline at end of file + nextDelay: 120 + +- type: chatNotification + id: AiTakingDamage + message: station-ai-core-taking-damage + sound: /Audio/Misc/notice2.ogg + color: Orange + nextDelay: 30 From 4f35ac5686f9a49d136da6fc237944cb4095b894 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 8 Apr 2026 05:41:10 +0000 Subject: [PATCH 130/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index cd2045dfa6..ef8f1b6bd6 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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, @@ -4030,3 +4023,10 @@ 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 From a6ec5e7d3eb87e3ede058f38def7aa664ff28818 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:21:53 +0200 Subject: [PATCH 131/247] allow changelings to drop stored disguises (#43487) * drop option * popup and better icon --- .../ChangelingTransformBoundUserInterface.cs | 38 ++++++++++-- .../Systems/ChangelingTransformSystem.UI.cs | 16 ++++- .../Systems/ChangelingTransformSystem.cs | 61 ++++++++++++------- .../Systems/SharedChangelingIdentitySystem.cs | 16 +++++ .../Locale/en-US/changeling/changeling.ftl | 13 +++- 5 files changed, 115 insertions(+), 29 deletions(-) diff --git a/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs b/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs index dc40147220..25cfb1ae82 100644 --- a/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs +++ b/Content.Client/Changeling/UI/ChangelingTransformBoundUserInterface.cs @@ -4,6 +4,7 @@ using Content.Shared.Changeling.Components; using Content.Shared.Changeling.Systems; using JetBrains.Annotations; using Robust.Client.UserInterface; +using Robust.Shared.Utility; namespace Content.Client.Changeling.UI; @@ -12,7 +13,9 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne { private SimpleRadialMenu? _menu; private static readonly Color SelectedOptionBackground = Palettes.Green.Element.WithAlpha(128); + private static readonly Color DisabledOptionBackground = Palettes.Slate.Element.WithAlpha(128); private static readonly Color SelectedOptionHoverBackground = Palettes.Green.HoveredElement.WithAlpha(128); + private static readonly Color DisabledOptionHoverBackground = Palettes.Slate.HoveredElement.WithAlpha(128); protected override void Open() { @@ -42,21 +45,41 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne ) { var buttons = new List(); + var dropButtons = new List(); + foreach (var identity in identities) { - if (!EntMan.TryGetComponent(identity, out var metadata)) - continue; - + // Options for selecting identities. var option = new RadialMenuActionOption(SendIdentitySelect, EntMan.GetNetEntity(identity)) { IconSpecifier = RadialMenuIconSpecifier.With(identity), - ToolTip = metadata.EntityName, - BackgroundColor = (currentIdentity == identity) ? SelectedOptionBackground : null, + ToolTip = Loc.GetString("changeling-transform-bui-select-entity", ("entity", identity)), + BackgroundColor = (currentIdentity == identity) ? SelectedOptionBackground : null, // mark as selected HoverBackgroundColor = (currentIdentity == identity) ? SelectedOptionHoverBackground : null }; buttons.Add(option); + + // Options for dropping identities. + var dropOption = new RadialMenuActionOption(SendIdentityDrop, EntMan.GetNetEntity(identity)) + { + IconSpecifier = RadialMenuIconSpecifier.With(identity), + ToolTip = (currentIdentity == identity) + ? Loc.GetString("changeling-transform-bui-drop-identity-cannot-drop") + : Loc.GetString("changeling-transform-bui-drop-identity-entity", ("entity", identity)), + BackgroundColor = (currentIdentity == identity) ? DisabledOptionBackground : null, // cannot drop your current identity + HoverBackgroundColor = (currentIdentity == identity) ? DisabledOptionHoverBackground : null + }; + dropButtons.Add(dropOption); } + // Menu category for dropping identities. + var dropMenuButton = new RadialMenuNestedLayerOption(dropButtons) + { + IconSpecifier = RadialMenuIconSpecifier.With(new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/delete.svg.192dpi.png"))), + ToolTip = Loc.GetString("changeling-transform-bui-drop-identity-menu") + }; + buttons.Add(dropMenuButton); + return buttons; } @@ -64,4 +87,9 @@ public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owne { SendPredictedMessage(new ChangelingTransformIdentitySelectMessage(identityId)); } + + private void SendIdentityDrop(NetEntity identityId) + { + SendPredictedMessage(new ChangelingTransformIdentityDropMessage(identityId)); + } } diff --git a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs index e555147352..50f5ceb547 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.UI.cs @@ -3,13 +3,25 @@ namespace Content.Shared.Changeling.Systems; /// -/// Send when a player selects an intentity to transform into in the radial menu. +/// Send when a player selects an identity to transform into in the radial menu. /// [Serializable, NetSerializable] public sealed class ChangelingTransformIdentitySelectMessage(NetEntity targetIdentity) : BoundUserInterfaceMessage { /// - /// The uid of the cloned identity. + /// The uid of the stored identity. + /// + public readonly NetEntity TargetIdentity = targetIdentity; +} + +/// +/// Send when a player selects an identity to drop from their storage. +/// +[Serializable, NetSerializable] +public sealed class ChangelingTransformIdentityDropMessage(NetEntity targetIdentity) : BoundUserInterfaceMessage +{ + /// + /// The uid of the stored identity. /// public readonly NetEntity TargetIdentity = targetIdentity; } diff --git a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs index 8bea4dedc6..8ec2d36cd8 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs @@ -30,6 +30,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem [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() @@ -38,8 +39,9 @@ public sealed partial class ChangelingTransformSystem : EntitySystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnTransformAction); - SubscribeLocalEvent(OnSuccessfulTransform); SubscribeLocalEvent(OnTransformSelected); + SubscribeLocalEvent(OnTransformDrop); + SubscribeLocalEvent(OnSuccessfulTransform); SubscribeLocalEvent(OnShutdown); // Components that need special handling outside of cloning. @@ -79,6 +81,43 @@ public sealed partial class ChangelingTransformSystem : EntitySystem // 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 ChangelingIdentityComponent, @@ -125,26 +164,6 @@ public sealed partial class ChangelingTransformSystem : EntitySystem }); } - private void OnTransformSelected(Entity ent, - ref ChangelingTransformIdentitySelectMessage args) - { - _ui.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.ContainsKey(targetIdentity.Value)) - return; // this identity does not belong to this player - - TransformInto(ent.AsNullable(), targetIdentity.Value); - } - private void OnSuccessfulTransform(Entity ent, ref ChangelingTransformDoAfterEvent args) { diff --git a/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs b/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs index 12d8f162a9..b9e2fbef3e 100644 --- a/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs +++ b/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs @@ -184,6 +184,22 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem return clone; } + /// + /// Drop a stored identity from the changeling's storage. + /// + public void DropStoredIdentity(Entity ent, EntityUid identity) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + if (!HasComp(identity)) + return; // Not a stored identity. + + PredictedQueueDel(identity); + if (ent.Comp.ConsumedIdentities.Remove(identity)) + Dirty(ent); + } + /// /// Simple helper to add a PVS override to a nullspace identity. /// diff --git a/Resources/Locale/en-US/changeling/changeling.ftl b/Resources/Locale/en-US/changeling/changeling.ftl index 873744fa06..d5c88d06f2 100644 --- a/Resources/Locale/en-US/changeling/changeling.ftl +++ b/Resources/Locale/en-US/changeling/changeling.ftl @@ -1,6 +1,8 @@ -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! @@ -14,7 +16,16 @@ changeling-devour-begin-consume-others = { CAPITALIZE(POSS-ADJ($user)) } uncanny 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. +# 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 From 6b59ccb9be2d271399e8d3971f95d263a892c859 Mon Sep 17 00:00:00 2001 From: _Svist_ <148935188+Svist666s@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:42:38 +0400 Subject: [PATCH 132/247] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=A1=D0=A0=D0=9F=20=D0=B8=D0=BD?= =?UTF-8?q?=D0=B6=D0=B5=D0=BD=D0=B5=D1=80=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B4=D0=B5=D0=BB=D0=B0=20(#3557)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../corvax/guidebook/SOP/engineering.ftl | 100 ++++++++++++------ .../Corvax/Guidebook/SOP/Engineering.xml | 20 ++++ 2 files changed, 90 insertions(+), 30 deletions(-) diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl index b4bcc89f67..274cda8536 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/engineering.ftl @@ -1,64 +1,104 @@ +# Общие СРП +guidebook-SOP-engineering-general-must = + 1. Соблюдать [tooltip="guidebook-SOP-engineering-tooltip-safety-precautions" text="технику безопасности"]. + 1. Обеспечить объект [tooltip="guidebook-SOP-engineering-tooltip-generator-main" text="основным"] источником электропитания в кратчайшие возможные сроки. + 1. Обеспечить объект [tooltip="guidebook-SOP-engineering-tooltip-generator-spare" text="резервным"] источником электропитания. + 1. В случае, если при эвакуации с объекта не останется сотрудников, способных обслуживать генераторы, основанные на Сингулярности или Тесле, провести их безопасное отключение с помощью замедлителя частиц или специального гранатомёта. + - При невозможности безопасного отключения, на объекте должен остаться минимум один сотрудник, способный обслуживать такой генератор. + - При присутствия на объекте критической угрозы безопасности, требующей эвакуации всего персонала, ЦК должно быть уведомлено об угрозе нарушения условий эксплуатации. + +guidebook-SOP-engineering-general-right = + 1. Заниматься индивидуальными проектами при отсутствии иных рабочих задач. + 1. В [color=yellow][bold]жёлтый код[/bold][/color] уровня угрозы получить базовый доступ во все отсеки станции. + 1. Взламывать двери, чтобы в интересах следствия получить несанкционированный доступ к отсеку, по устному запросу Главы Службы Безопасности. + +guidebook-SOP-engineering-general-prohibited = + 1. Строить дополнительные источники питания, пока по крайней мере один источник питания не будет правильно подключен и настроен. + # Должностные СРП ## Старший инженер guidebook-SOP-ChiefEngineer-must = - 1. Удостовериться, что гравитационный сингулярный двигатель, и/или двигатель на антиматерии (ДАМ), и/или солнечные панели исправно снабжают станцию электроэнергией, перед тем как инженерный отдел будет предпринимать дальнейшие действия. - 1. Контролировать кадровые и материальные ресурсы отдела. Распределять сотрудников отдела по заявкам, полученным в устной форме лично или по общей рации. Следить за выполнением этих заявок. - 1. Следить за исправностью систем, не допускать продолжительное игнорирование неполадок с этими системами и их выход из строя. - 1. Следить за исправностью основного двигателя станции. Любая халатность, связанная с остановкой работы основного двигателя станции, без обстоятельств непреодолимой силы, карается немедленным увольнением и дисциплинарным взысканием по окончании смены. - 1. Руководить работами по восстановлению подачи электроэнергии в порядке важности, начиная с: система жизнеобеспечения, мостик, медицинский отдел, отдел службы безопасности. + 1. Удостовериться, что [tooltip="guidebook-SOP-engineering-tooltip-generator-main" text="основной"] и [tooltip="guidebook-SOP-engineering-tooltip-generator-spare" text="резервный"] источники питания правильно подключены и настроены, перед тем как отдел будет предпринимать дальнейшие действия. + 1. Не допускать игнорирования проблем, решение которых находится в компетенции инженерного отдела. + 1. В случае поступления заявок на ремонт распределить сотрудников отдела для выполнения заявки. + 1. Руководить работами по восстановлению подачи электроэнергии в порядке важности, начиная с: систем жизнеобеспечения, хранилища, мостика, медицинского отдела, отдела службы безопасности, прочие отделы. 1. Убедиться, что на станции всегда есть хотя бы один незадействованный укрепленный инженерный скафандр, если нет чрезвычайной ситуации, требующей использования всех костюмов. - 1. Обеспечивать сохранность и стабильную работу генератора гравитации станции. В случае потери генератора — срочно сообщить всем главам и написать отчёт ЦК с описанием обстоятельств. - 1. Обеспечивать сохранность и стабильную работу серверов телекоммуникации на станции. В случае неполадок отдать распоряжение на устранение в течение 10 минут либо заняться этим самостоятельно. - 1. Держать материальную базу отдела на контроле. Всегда иметь по 30 единиц базовых материалов в хранилище отдела для экстренного ремонта во время ЧС. + 1. Обеспечивать сохранность и стабильную работу генератора гравитации. В случае потери генератора — срочно сообщить всем главам и написать отчёт ЦК с описанием обстоятельств. + 1. Обеспечивать сохранность и стабильную работу серверов телекоммуникации на станции. + 1. В отсутствии или при чрезмерной нагрузке инженеров или атмосферных техников — временно заменять их, соблюдая требования их СРП. guidebook-SOP-ChiefEngineer-right = 1. Выдавать разрешение на строительство членам экипажа. - 1. Обозначить зону "небезопасно", если повреждения не позволяют завершить ремонт в приемлемый срок либо зона представляет смертельную опасность для неподготовленного члена экипажа. - 1. Совершить взлом в запрещённую территорию по устному запросу ГСБ о помощи следствию и требовать содействия от подчинённых. - 1. Совершать контролируемые разгерметизации любого масштаба для устранения ЧС при условии, что система жизнеобеспечения способна быстро восстановить безопасность помещения. + 1. При наличии подозрений на незаконную перестройку отсеков, в устной форме запросить у ответственного за отсек сопровождение для личной проверки или любую необходимую информацию. + 1. Совершать контролируемые разгерметизации любого масштаба для устранения атмосферной угрозы. + 1. Обладать правами всех сотрудников своего отдела. guidebook-SOP-ChiefEngineer-prohibited = - 1. Извлекать пожарный топор из шкафчика, если нет непосредственной угрозы жизни или необходимости срочного доступа к месту происшествия. После ликвидации угрозы топор должен быть возвращён. - 1. Выходить за пределы станции, не оставив заместителя, способного управлять отделом из числа атмосферных техников или инженеров. + 1. Извлекать пожарный топор из шкафчика без непосредственной угрозы жизни или необходимости срочного доступа. + 1. Выходить за пределы станции, не оставив заместителя, способного управлять отделом из числа сотрудников отдела. + +## Ведущий Инженер +guidebook-SOP-SeniorEngineer-must = + 1. Обеспечить усвоение теоретических знаний и их закрепление на практике всеми стажёрами отдела. + 1. При отсутствии стажёров, в отсутствии или при чрезмерной нагрузке инженеров или атмосферных техников — временно заменять их, соблюдая требования их СРП. + 1. Замещать Старшего Инженера в вопросах руководства отделом. +guidebook-SOP-SeniorEngineer-right = + 1. Проводить дообучение персонала отдела. + 1. Отдавать приказы сотрудникам отдела при выполнении указаний Старшего Инженера или при ЧС. +guidebook-SOP-SeniorEngineer-prohibited = + 1. Извлекать пожарный топор из шкафчика без непосредственной угрозы жизни или необходимости срочного доступа. ## Инженер guidebook-SOP-StationEngineer-must = - 1. Как можно быстрее добираться до места вызова с целью устранения поломки. - 1. Удостовериться, что гравитационный сингулярный двигатель, и/или двигатель на антиматерии (ДАМ), и/или солнечные панели исправно снабжают станцию электроэнергией. - 1. Периодически проверять гравитационный сингулярный двигатель, если он является выбранным источником энергии. - 1. Поддерживать работоспособность сети электропитания станции. - 1. Оперативно реагировать на любые повреждения во внешней обшивке, независимо от их размера. - 1. Обеспечивать сохранность и стабильную работу серверов телекоммуникации на станции. + 1. Оперативно реагировать на любые повреждения оборудования, электросетей, утилизационной трубопроводной системы и внутренних конструкций объекта. + 1. Как минимум раз в 30 минут проводить проверку состояния запущенных двигателей, основанных на Сингулярности или Тесле. + 1. Выполнять запросы других отделов по перестройке и улучшению отсеков при наличии соответствующего письменного разрешения от Старшего Инженера и ответственного за отсек. + 1. В отсутствии или при чрезмерной нагрузке атмосферных техников — временно заменять их, соблюдая требования СРП атмосферного техника. guidebook-SOP-StationEngineer-right = 1. Взламывать двери, чтобы получить несанкционированный доступ к отсеку, если данный отсек требует срочного ремонта, предупредив ответственного за отсек по рации или лично. - 1. Заниматься перестройкой или индивидуальными проектами при отсутствии повреждений, требующих ремонта. guidebook-SOP-StationEngineer-prohibited = - 1. Строить дополнительные источники питания (гравитационный сингулярный двигатель, двигатель на антиматерии или солнечные панели), пока по крайней мере один источник питания не будет правильно подключен и настроен. + 1. Носить скафандр отдела без необходимости его применения. ## Атмосферный техник guidebook-SOP-AtmosphericTechnician-must = - 1. Как можно быстрее добираться до места вызова для устранения разгерметизации или утечки газа. + 1. Оперативно реагировать на разгерметизации, утечки газа, повреждения газовой трубопроводной системы, прочие атмосферные неполадки. 1. Периодически проверять состав газов в распределительной трубопроводной системе на предмет аномалий в составе или температурном режиме. - 1. В случае отсутствия доступных инженеров — временно заменять их, соблюдая требования СРП инженера. - 1. Сообщать о любых отклонениях в атмосферной составляющей станции старшему инженеру и лишь затем приступать к исправлению. - 1. Оперативно реагировать на повреждения трубопроводов и обшивки станции. - 1. Оцеплять все опасные зоны атмосферным голопроектором и оповещать экипаж по общей рации. + 1. Сообщать о любых отклонениях в атмосферной составляющей объекта Старшему Инженеру и лишь затем приступать к исправлению. + 1. Оцеплять все зоны атмосферной угрозы, оповещать экипаж в общий канал связи о проводимых работах. + 1. В отсутствии или при чрезмерной нагрузке инженеров — временно заменять их, соблюдая требования СРП инженера. guidebook-SOP-AtmosphericTechnician-right = 1. Полностью перестроить систему жизнеобеспечения при условии, что она не будет перекачивать вредные газы никуда, кроме камер фильтрации или открытого космоса. + 1. Создавать контролируемые разгерметизации для устранения атмосферной угрозы. + 1. Потребовать у любого члена экипажа покинуть зону атмосферной угрозы, не вмешиваться в работу пожарных шлюзов и прочих мер сдерживания атмосферной угрозы. + - Отказ члена экипажа приравнивается к нарушению статьи 142 Корпоративного Закона. + 1. Запросить у Старшего Инженера или Капитана установку [color=yellow][bold]жёлтого кода[/bold][/color] уровня угрозы. guidebook-SOP-AtmosphericTechnician-prohibited = 1. Извлекать пожарный топор из шкафчика без непосредственной угрозы жизни или необходимости срочного доступа. - 1. Создавать летучие смеси с плазмой и кислородом вне камеры смешивания и переносить канистры с такими газами без сопровождения минимум одного вооружённого офицера СБ. + 1. Создавать летучие смеси с плазмой и кислородом вне камеры смешивания и переносить канистры с такими газами вне отдела без сопровождения минимум одного вооружённого офицера СБ. + 1. Без разрешения Старшего Инженера, создавать разгерметизации, затрагивающие целые отделы объекта. ## Технический ассистент guidebook-SOP-TechnicalAssistant-must = - 1. Содержать станцию в хорошем состоянии. + 1. Быть приписанным к старшему сотруднику отдела для обучения и прохождения практики. guidebook-SOP-TechnicalAssistant-right = - 1. При нехватке знаний попросить инженера или атмосферного техника выполнить задачу. + 1. В пределах своих сил и знаний помогать сотрудникам отдела с выполнением рабочих задач. guidebook-SOP-TechnicalAssistant-prohibited = - 1. Выходить за пределы станции без разрешения старшего инженера или возникновения чрезвычайной ситуации. + 1. Самостоятельно производить ремонт критически важного оборудования. + 1. Выходить за пределы станции без разрешения приписанного сотрудника. + 1. Носить скафандр отдела без необходимости его применения. + +# Tooltip +guidebook-SOP-engineering-tooltip-safety-precautions = Техника безопасности инженерного отдела: + - При работе с электричеством носить изолированные перчатки; + - При работе, предусматривающей нахождение в агрессивной внешней среде, использовать защитную экипировку; + - При работе в открытом космосе иметь при себе установленное в КПК приложение АстроНав или Глобальную Систему Позиционирования (ГСП). + +guidebook-SOP-engineering-tooltip-generator-main = Источник, способный запитать весь объект. Например: генераторы на основе Сингулярности или Теслы, ТЭГ, Суперматерия. + +guidebook-SOP-engineering-tooltip-generator-spare = Источник, способные поддерживать минимальный функционал объекта. Например: солнечные панели, ДАМ. \ No newline at end of file diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml index bee73ba66d..231ad0484e 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml @@ -1,4 +1,12 @@ + # Общие СРП + ### Сотрудник инженерного отдела обязан + + ### Сотрудник инженерного отдела имеет право + + ### Сотруднику инженерного отдела запрещено + + # Должностные СРП ## СРП Старшего инженера ### Старший инженер обязан @@ -10,6 +18,18 @@ ### Старшему инженеру запрещено + ## СРП Ведущего инженера + ### Ведущий инженер обязан + + + ### Ведущий инженер имеет право + + + ### Ведущему инженеру запрещено + + + + ## СРП Инженера ### Инженер обязан From 9ef3b237ebe03a808d12d8e7167256c6cee05d40 Mon Sep 17 00:00:00 2001 From: mikey <23003816+mikeysaurus@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:50:33 -0700 Subject: [PATCH 133/247] Add logic to Debug/Set Outfit command to also fill storage containers (#43346) * add logic to fill backpacks * updates per feedback * update naming * resolve * remove unnecessary reassign * consistency --- .../Clothing/Systems/OutfitSystem.cs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Content.Server/Clothing/Systems/OutfitSystem.cs b/Content.Server/Clothing/Systems/OutfitSystem.cs index b1efbffd60..c1187657fd 100644 --- a/Content.Server/Clothing/Systems/OutfitSystem.cs +++ b/Content.Server/Clothing/Systems/OutfitSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Hands.Systems; using Content.Server.Preferences.Managers; +using Content.Server.Storage.EntitySystems; using Content.Shared.Access.Components; using Content.Shared.Clothing; using Content.Shared.Hands.Components; @@ -11,6 +12,8 @@ using Content.Shared.Preferences; using Content.Shared.Preferences.Loadouts; using Content.Shared.Roles; using Content.Shared.Station; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Storage; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -23,6 +26,8 @@ public sealed class OutfitSystem : EntitySystem [Dependency] private readonly HandsSystem _handSystem = default!; [Dependency] private readonly InventorySystem _invSystem = default!; [Dependency] private readonly SharedStationSpawningSystem _spawningSystem = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; + [Dependency] private readonly StorageSystem _storageSystem = default!; public bool SetOutfit(EntityUid target, string gear, Action? onEquipped = null, bool unremovable = false) { @@ -68,9 +73,35 @@ public sealed class OutfitSystem : EntitySystem } } + var coords = Transform(target).Coordinates; + foreach (var (slotName, storageContainers) in startingGear.Storage) + { + if (storageContainers.Count == 0) + continue; + + if (!_invSystem.TryGetSlotEntity(target, slotName, out var slotEnt)) + continue; + + if (TryComp(slotEnt, out var storage)) + { + foreach (var entProto in storageContainers) + { + var spawnedEntity = SpawnAtPosition(entProto, coords); + _storageSystem.Insert(slotEnt.Value, spawnedEntity, out _, user: null, storageComp: storage, playSound: false); + } + } + else if (TryComp(slotEnt, out var itemSlots)) + { + foreach (var entProto in storageContainers) + { + var spawnedEntity = SpawnAtPosition(entProto, coords); + _itemSlotsSystem.TryInsertEmpty((slotEnt.Value, itemSlots), spawnedEntity, null, excludeUserAudio: true); + } + } + } + if (TryComp(target, out HandsComponent? handsComponent)) { - var coords = Comp(target).Coordinates; foreach (var prototype in startingGear.Inhand) { var inhandEntity = Spawn(prototype, coords); From c921f18f33a9698d87c0e9afdff1ecbd66ed41fc Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 9 Apr 2026 05:06:12 +0000 Subject: [PATCH 134/247] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index cf6842025e..43420c1019 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1699,5 +1699,13 @@ Entries: 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 Name: Admin Order: 3 From ee798da9dd77385c847667232806607b38b2d302 Mon Sep 17 00:00:00 2001 From: MureixloI <132683811+MureixloI@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:13:46 +0300 Subject: [PATCH 135/247] resprite ert magboots (#3475) --- .../magboots-ert.rsi/equipped-FEET-vox.png | Bin 811 -> 926 bytes .../Boots/magboots-ert.rsi/equipped-FEET.png | Bin 650 -> 790 bytes .../Shoes/Boots/magboots-ert.rsi/icon-on.png | Bin 590 -> 439 bytes .../Shoes/Boots/magboots-ert.rsi/icon.png | Bin 589 -> 439 bytes .../Boots/magboots-ert.rsi/inhand-left.png | Bin 382 -> 746 bytes .../Boots/magboots-ert.rsi/inhand-right.png | Bin 395 -> 710 bytes .../Shoes/Boots/magboots-ert.rsi/meta.json | 2 +- .../magboots-ert.rsi/on-equipped-FEET-vox.png | Bin 798 -> 925 bytes .../magboots-ert.rsi/on-equipped-FEET.png | Bin 1014 -> 778 bytes .../Boots/magboots-ert.rsi/on-inhand-left.png | Bin 379 -> 756 bytes .../Boots/magboots-ert.rsi/on-inhand-right.png | Bin 398 -> 704 bytes 11 files changed, 1 insertion(+), 1 deletion(-) 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 4d0f8d2ea258218ca2d5a2ed4c9e9d0ad7b48644..0b3d47be055670d2c84113d8d4d918ab1cf041f2 100644 GIT binary patch delta 891 zcmV->1BCpm2A&6yFn5JCtcgb+dqA%qY@2!A1j_(*6$FHC=#b4}9# zpiTIk5OyK{rsijF+aBPWRphsO$}lo z$VHA**R|MK5Iw$>Z>);+!PcNICEbpb{PxBVP@^LuCW zK8@C&#eYNp2MHmB5JCtcgb+dqA%qY@e9%I5PrM$z*Noh+18g2qXZUSZYSE2Z`o2XuOlH2|_BKGhz9HStaN4O-`r`qW zXO9kVKEP8{9b(q*waDqL8`TrUES>R`!T8l1?tjXq?~kMi7p~rLfo`jO(cQ08T~r-n zA@&ShfLc4wsHIcCbDg!@I}A;J`$nsL?aEI?B10k4hf}(CnO19u&%gW8Uu&u|>r1e+ z{wtC03`4C?3x7X>+T)&vt#G0ixw!=ZqI$xQ)mB!?%`KoMXTxk6ik6%uH@84-Wz}Ev z&UBEQTfm80p@t&}`x6{EVY%~P&vZY9*RTBg(!E{t0WnL5K0nBJ;r-|uwj!$^V|nc< zQ9Z%r{Iow6lGTr~^xN;DTJxa`&^re(_}MAw4k;Yvd;ooB6ngX1XLjKruK>i{k0$ZE R%^d&$002ovPDHLkV1jbIx@Z6Z delta 776 zcmV+j1NZ!%2df5ZQiQ@W3h$Qv{}Y5CM=qbijzsEVPS7A)+Mx{f zW+f+RKrYwL)ORumXSoc=aH_SuDEiHswF9LLj%uCp=sV%6jC`1>eChxI002ovPDHLk GU;%tO$@gH>Q&c9GM)rE+G((1z2MX@nxiLcmfOms+F1L3;R1k+AJ-jL*h zy&q)r?##P~Gxw3jI{?El48t%C!!QiPFbu;m48t(YIl(3K@PCcl^Q7w<_V@EBegB3p zxeue0UDq{aL=~l} zX_@UDF=yVg?Qgf<4s9C{j3sG&QiIosV10A1p|-xc2d@#q_@suAnIdTwZ7{aHLK#uT z>h^(ceRccb&ws~Bq^0^xjf=(+8mACyNjRCpW@`?80GSh zt{s7~z{-k-nw1qe8Zt1yxYV@?2$?DJ z?`~mdZNs*{v$g^M?iPZvq*VX53<$=O6pfq6h$*oP5o+)n7^^QCFtB zqqtmhzJE*o4gQa?0jdS4h7C|HKs9WDY5}TY15^u84I7|ZfNIzP)dEz*2B;RG8a6<+ z0M)Poss*Tq4NxsWHEe)t0jfFo23&OVQZF+Bt+;vT{+Yaj>>iFMpqH6|W;Dwy$l%z3 zUStAV5i(Ov^9o&qh(JVekIs^wNK+sXCXdHQdUzsDwOX%!dLm68kB$bi$C0Iw10N-n^TT2{boL~Qd4vN*aPKb;Ba x9}d?AbTSuU#|7^M;cq^mQY@UwE684t{|4pQqPVtZp7HPx%L`g(JRCt{2+R8sn5qT*U6A-z_8iK?Dj7a z%zpm*Bk{O&1b|>Bu-m^}`#WIRY-EalH>!WrzlP)70{|T79yk5#sD9D!rm8R10Nb|1 z?R*|DH}S!Ew{%<--_GY@+jf|^n1a{!4-Su(>K_~)DSy|Odjj-h460`1{iQV(fb!#-kAi+45TJ6A=*+ z5fKp)5fKp){S%bF;cY4tuu4*1AniI*`GD6pkqKC3RbC-2>CfYFHjxQP^1Hl(ETsl) zDig3uQfpozEu{v;WdhJsdjysAT1d((q@~n=xJ-a)ny6XUT5|z$YL*q6rirAyLRv~M z!KF+SNqpAT0VQIQlYuV+YXDIDJ zFrId%7K|UgX33^ymkAhAL>hqX`cWN0|DQ}P7+z1Ng?5tEZ;D9lRu1_CEGKQ zX(|jIL2PZ;iGM`q0lcSW);|dCB$d?`y8r+H07*qo IM6N<$g7n0~82|tP delta 566 zcmV-60?GZi1I`4HBYyw^b5ch_0Itp)=>Px%2uVaiR9J=Wl)-C~VHm)FW(V~XNrx`l zbm4^WP@`1?#fUB~B9Kl&FzB%UfQ}tH$YB4%I(F+6o+1!CD@4uhFapC-x_!*PP`^Zd z4H<6-=SQ}wIlArpo!;;FzVGuqJm13$78Vv3XC2$57gSZ1PJgEpm%{--yVF6_G<-fE zfBOEO5Q0LXFf`BkiA!F3OfY`W{H*3OpuIfizvMDR>K`fY?oOOyD2a`%>~@j0?hD!N zB4f#vv`1IvNj|ZWmFh}Ts;VkS#hj=E-*@)8y`BQVAIMTKW&rr1R|dR4(CfgzFwqRy zqpK2NX*n_AKYzV{3&7PIH;>)vG;|8D*Z$88M2zfFLWo3y13of8PddNJgC|c-U*6fq zzV&8g%&{Ga$71F`oDGH8FPEv->r8pQ=9z1Yi&SehTFoZu>P52?j;Tu+U$;-+fp{z? zXG0;Td_DlGwHn@|_1_*qal5%sg>-(?EMi;-&W+kUynp)~apxIZ)63*+3uAqssO>YI zhTc1S{n6uqa5yX*jRtzcQ4BEi~x~@lCQ z0~E>2yomJ(z6>4p@G;$5g zJJ5`50pR%3YpW%ka?n%*_-GaXJ_A3$#1)c_o=Ec~6Vxsc&VZ#OSn|w#Ginx9!$722 zVdOOoucov>z<&>5T&+T_gD06__VF4sO%Ky}$O(@@7{DD(Yp#%N)Cb|g&>dxtQ6w)S zO=s)??r2(D-i{gd{Yf5xxI9Hx?PI3tJE4bYt0l(%50nelU%j)dC!BK7RNGu%G(rIM z8ZROXaR7awhTUf~O*i+wKU1i6n4Vjp+bRk}*x1NZuegFUf delta 565 zcmV-50?Pfj1I+}GBYyw^b5ch_0Itp)=>Px%2T4RhR9J=Wl+SAuK@@<$v>uXd2qqzE zu}vyzFb6GGEi}Y~7u!mqcoGD`gZ=>?qJbpYU6`EQ#tW^7giExV{Qlc(r0`Z=bVY zU8Sn)?9VMC%Vp+Ye&o2nlAdKN2XKZ3{My`MU~Ul&2uFt5%4GrgtybWW2duMR^fd#6 z(@#u*shNc%{(sw-PXJuIdF#ZRR!ybwZux9xAYrT=B!omFdc-Hj$I0Z^n1B4#@}jVzg$31}L^Kp*~nf#hnM6V7E*d6ZOeSd+t^NjVO8FH?Pp1#Ah>pQKQ z+P-_^!RZTt_Jj+yLaV0oq3G-C8}&MkdL3wgKpm=|mu=(Q&Oq|fk_jNoWy?+c%_FYf z=YJkR2w^$-;%xx-zP{<2$F_iYJZ{!%HNxRAe++}mV`Ka*7V&t!7>0o)Ni-S_BuPRD zf#2^(G%lMAMIw|Hh3Lo#%I+?zs`mZmJ2*HvoFD!HHPPcG9MvSo00000NkvXXu0mjf DO-d3w 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 2b35b6a700caf92de07fb47a9a4656b4accb3972..4401465b085ddc42c3cdad7aa0a4761e0178e522 100644 GIT binary patch delta 723 zcmeyz^on(Yay`>VPZ!6KiaBp*Id%&r${dsTP4Vu{;mS>)qL{L2K^Rly9f3v7$6YyI z?%prJ93-vclKjeI*YpPDm?n7xx-7h>|E2eqgb-8wr&oZd1d-Ly8-CzFS zElKkSN3GHw{|A)eIqt4|Bj2otQnQ}HZD7$@l;T9|FZ=R+k}1@Zx7zN!-7G& zU$Jcau|41A71!0rIQhvx_TvHs`e1yQC?s2^8X z78E`=O3@TZuU*W#{5tz|p(gj>0&AHgSI@JGtiR*5dy#U%tvQuzfg0s}4}R|G5!a7D zytE}@%BRJT{nqpEb?fW4o*sQ*ie#QQ8$-3Itea;4gUjg=k@1J??|ox8^X7QgZeR6X z&RV@3Xq&-d({g485owzCNU?S3x!FUm$U_G+Z#?OaV>ufAw zG?6i~-naeKnN3G5!cGfwiytUTm*4y5+LcW$SDB99k?%X#oT7C&YpazMpJd}@wXWTf z(rssj7EjrJMfQP|gUYOgWoPzGNXmS6QN+b>-^1gFLuIDD&Im6l`TFXG;jXFg7`87z z{OXw6ZaF@uq7y!6aymY^buGPl>X#JX>C&A2Hyg_%PQ7|{^<&?mGX=t1cblf&tKV}= q`mx8xFDulH*PC)QDR4Zpe`J1N?{&hFeX`3LfWXt$&t;ucLK6TrlTy6^ delta 356 zcmV-q0h|8n1^xn%BYyw^b5ch_0Itp)=>Px$I7vi7RCt{2+CNLfKpe*L$7+Sv4weML zMBl?%1cSSivt!5jCUg~a=-}WaF1`V$W^(ZjsCfl~5QI`ow+w;C!9QD}wRb}B``Hfi z+=bt8%RLeR00000z(2F(YGY$eDM?wDDGWnNQWym4C&^4_E`OSz)r!r{z8Pc6FbFj4 z#ELdIH0htIc6g$}+dxVZ=lOX!8NhaY_4%`slG_{Y9>mhu`Y-bHus(qOC@LEdml~(3 zymnj1J4UaM*BW+WjbEOf=jT=G+mo2ib8pwYbywTzIjU1sb?;l2LJ#700000005?|>@$kb@&COW zSSCBP%LJWP(-9I=&e4j?k`ngfchcMW6dUD_25R<$V`bK`9|IEUD8cIzQ*4?`G_hJvva;|mK&omOY#=3n^xPEP4 zO;O9#wZ0lkM#TyGfi5hJrhQK*D@Vtl&$8;0d{8m1*70uHz1E4-B)>{^rOAgaePT6@ z!}7+I2C4Q?P2Gn4dHO?f8@9ubFaC}AvzcMXALc)6D}Kp; z&_Beqf;r$Y8AMvGqtU{XHF~=;%h_8P8=?Q0Np~!=@wdtmcJzqW)`>UB)YWVCZ|Ic`@K127)yZ*yR;u}IW9R&q7 zd$#bP0 Hl+XkK@TokR delta 369 zcmV-%0gnF01&afaBYyw^b5ch_0Itp)=>Px$MM*?KRCt{2+P_M}P!z}UpV11f9V{(^ ziM~TDg2mm**|B4M30(ypIyg9qi!b2ROfJ5FnkNv1B9vOXbtp6r{)?JW(;Dg3?`ON@ zT=?Y-cR5D_000000QhHSl0LYu8%k2E)so{lk|f8r)%5+uJAc#UyZPsmE<3iZPSsOw zc}c_OfvoL4wIAA&?`IxeP zTqkU12yILp~HG6sVJAW{N zaq@q75Gs{QIBK8ENs{t~f(CH7npS))tdnZ7>|C)I<+{2c%Q;178MnAxzw1I~#p P00000NkvXXu0mjfssOGH diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/meta.json b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/meta.json index 6e4a57f3c0..70d8a4d43d 100644 --- a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/meta.json +++ b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/debf90acfcafa4fb8d6723a37e0b8ac556c0702b. equipped-FEET-vox & on-equipped-FEET-vox state taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/31d6576ba8102135d058ef49c3cb6ecbe8db8a79/icons/mob/species/vox/shoes.dmi, and equipped-FEET-vox state modified to fix inconsistencies. ERT recolor by davyei for SS14.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/debf90acfcafa4fb8d6723a37e0b8ac556c0702b. equipped-FEET-vox & on-equipped-FEET-vox state taken from vgstation13 at https://github.com/vgstation-coders/vgstation13/blob/31d6576ba8102135d058ef49c3cb6ecbe8db8a79/icons/mob/species/vox/shoes.dmi, and equipped-FEET-vox state modified to fix inconsistencies. ERT recolor by davyei for SS14, resprited by @mishutka09(discord:1152277579206774854)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET-vox.png b/Resources/Textures/Clothing/Shoes/Boots/magboots-ert.rsi/on-equipped-FEET-vox.png index d7d694fb929c552749c4ddcbf925c57b321e80b4..dcabc77e8a0b031c1fe319799a3458a0850f6c8c 100644 GIT binary patch delta 890 zcmV-=1BLva2Av0xFnXWsYdu~+xIfaj|9|?yk0HqG`9EIq^4zCH;>L+J z-G;)QKU(k4n4d*6D|x+?O;J@nGRs$RRFlKbJ^+WEeH_&!vwVfB>amnfF|(4_fu7K3 ze9JO%RFiH)4aM&^6pm_QS!Uq)zA`m-KJIO3UiyUH{oncXw?=4i-W_)h&i_-B^Jxt* zJ8|H}IDa1BJ)8S$yn9(4`5h#L5JCtcgb+dqA%qY@2=Sj5sCweo=-p!E`zpZJ8C8Z) z+uO<&H9+F+Uf})!-wUnF*L31~lG@aqSAwpuZ+nHvnU%bb#EthRWt6O3QFj|^DDF}= z1^vo!;_V)_Cy!{ocsjcN09RG@h{T7-Cg;;SY=6Xv#0?f~lZD%N_2#$VoJkUH-M*`V zeyKdKoqmb_#2EwZ>8m)RYZKp|S@PPQH(d@y&ibqdkp#sfz+ zsjqKSh?dzXbcn)f%vSlF2HHT8groFl4 zjdQtYC{=8(X6k{OBM5p4j-8L|>{qM(U*YJJcfSO;KfrBX81-G?UUc1tB5jOQdH9^L z5o0Nv^14FO#yIOg{Sv4&A2Qsw)c^nh delta 762 zcmVh;8WKJTYz zW?-b(f@}5TvhcotJO8C<_`>?TVBD<%=xG1|+jU{jZ-ZY4;K%t8SiW^_!LtW2u=Y!S zQdW^v-avh=M#_I7dWh3f0oJtz>)IN-&el##1<@@d^X$PNt&b!805-~J@azFRdw`^@ z;%;FJ0C2akg@2^125TGTv&e@K(XCa3?@3t&R(D5()!or}%ylA!9)NyaPRzEB;0^EJ zzG;0AeFA_tv@qK`iv53peq2su+(wY!XUQz&c?3epl_OJOSxF{2_b|KLI@#* z5JCtcgb+gh36W}e?{{{5z7Ef0^|LEU`|SvP!B-2R)yKQ40;15y_q;< z_YQp4r(kw2L#6Q3QUNEWO5{BwwaD;wc)nE5=GokH{t1lB>VPk9?t?YZUbm) zznzO+Qwx>oC(ZF^eN3b`we<1O=j-qQ&^Jxk^K;u+`f*>f?wclR^?Izog7tjfe>*o9 zjN4KUO79$O?)K&eMtTiNSw;KPe!NXmOwb3AP6uJ0WI^ejqYnhlN}gl^^?H=8A7#&P s<5pJT4M%wj%3_%r6tvc)P$zNW!aXx4W$?bwatQ| z^q@Hfym-|F{0q71q5gxOdQ$TjdWzCh1F_YEiHAaBpe0VR0X2aTs(~;bG{L0XkXe!p z*zY;c%+B}n&Nu8~UVtD7f*=TjAP9mW2!bF8f*=Uuf}n^jzJGb=J~s>jz9bf~6=)Ly=0F1443YHFAd=?A6fU0=gV!wmp9X}B@FzJ{Gd1&Ns?zGYqx zR%z!Qq`tyBQZksN9T)-hKrFf2!M*h&S4)&X8C z&fN$5$ZcnA=X2W`xDWQ>3B?BA0iIBd2cs6!tcl3#B7f?vh(@Ih0BBUosIwv>tBXjp zCW29G;3jyjI1ffG7>15IE236l9rbGkhB_<4Fmwc?mXHa3waeHQv%TTNF?V& z>F$A|D1TzU3;hQFN5}x#0%Su5$QB?QGC;Nf*^mLU1;~aBkS#zqWPoe|vLORx3y=*N zAX|WJ$N<>_WJ3nX79blkK(+wcTzCU6Ir(UmnSfs0y8GZvUO{vZ#~m=rOu%`Zmsb$M zu>qsV1oXmd#m~(vh~UV8?o5DcYK{dsMKv|p0+w$Sk`4q#~Eh;CXP zcTXEfYA=tLmebO%GLpy9?o5DcYD38d*wM-gsHSF{R}jT<2mG4}n3(pp1q?D5V8^(+ l$niHH(5RHp1VIr*?CgCt64pD}K!U_sX79(D zIhVUq2z%<1lrK*x)1e+)wB9@MI~{c~d5 zwNvms!&Ry2UzeOFzeKM#wa^g+x8dTtsipVfatHdJjFg$GOCH(oY!=?nA&`WgJMb+# zSvAf%dxXT6{GwYpID{d&A6)qbl_6)?-~zKb0006*Nq{1h(Ei+&fr=b_!{ zWNMZb7Jqhj;1sISwZmpZdfmTW|E+BCQtcudKePcnwg3RG@54Q~#LIaA;X0eJs4u1h zfjfbN>M!+knt`0Y4`a_rRsW6bCT^}yAf^IhDq`msb2nEfvP-5GxRXDck0afH)5a|V zcY?s3Ag394E}a7ao=fM*X+|`6+PF=<2^kOVFMrW_PBVZ?eLkqv=i`{`WJon&*lc8W z`_~9&KY#s^_`Y-mfM6!D+rM7>J7CysWQu+_s(;_VgX25_037ED_x-!5e$nrysxQ?5 z+qT2)d>$`1@!oj1bX*kQ&gWsA08bm*Oz+&^fL^yW%!;4^jZjJ z0)JbY4glEF^m$WyE%=^?LAH#cex`Dti3Zs+#-q`^K7iTy>(cyU?0X)@qY(z#@>=T? z5fKp)5fKp)5fKsn6O_K;Z7LJ6N>W}R?K)EVfY&yW30P%SULh^%&*O16kqJohyS#!d zr3P#&6R=8BULh@|2E=6o&{KN^mGoLj%33R=rPP49On_;cs9Dxpa{+N`mKB<&iKM(j zT1qd$rA$Df0H;u$|0S{l=ya4eBFjCv#5c`AFcZ+d&%k2~L8Tt$71H_v(_=v(P_LNY P00000NkvXXu0mjfn4H^f 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 a5d0ba43f9faefdef25a29203bba2e9860e2a5fc..20f60ab60305a3079c78cb3cb24d2bef7486cfd5 100644 GIT binary patch delta 733 zcmey(^o4bTay`>-PZ!6KiaBp*o$nS(lsP6JJ27=`rzw9}UsB>_rx&N%TBP+Z9&~O$ z%CYjcQpLL)iZ|*PewJ(U`^#LdX|-jel4{^vs|_=6D!og2tEl#gy*nje+&xNMJiYp< z>#w>zE93XSKYpA$^YCv57DoX%I&!k(*d(2)=Qb7ApPT5F9%(Y|iB#OWbPA$j&}Zd~(Alcys&PEtiWH$UHlIW@(FC6zlxDGX`I#{e1fQ z(}mBbZ*|4(^|T*Y{Q4iYIpgi0$jUWwYIowACv&kXL^+sf+4UAtu0! zMX(;LsD2n79SmIkF%i+XB%-Emh*}`=``HT%kENT} zM6FFc|FHT0b@80Nm3hwVBa<>Jz3(!vV{}ROOgq)X?Qe59CDd`tUr~GUbxGSpoENWI zyfpmR?H6IYnD3Oehc#=je)A=UL9V@jCa>VVibs-W`}cI*4dQ+6Z~pMz)J;)UsT&p> z7~OGsJL%M!-~Hj$$pZd1m)D={$>wWP;Ar~M|ASe?_AOiAnMyAPAn^zJK)?z6%_-Rh0xYZ!lT?1}<8OFhY2$J+CcR#7*8JB|n z%Uj=f)$Aqf=6kklx=j39BN--U@HbLn*Y2kU77Ul{Pft&*Vf>vKab^BGiQ|TTt6lT0 zA3T2Ac7AT~vG@7cKBwx=mf2QrrZFc^zM*$A_nf@>>o0bEO`AORxcBRy_YJBR?pPPS z=4uwh6=~j2HtMI1wnf?G|2ms3bKa)-;vT3=!PJSIskLcFSLHW+vdNo$@}GmMc)}+8 xfSs%}-tkMkWxtWO*gjyTcfempK_F{|&kY#)~A zb@;Th@xP4VN%WY}d}E{cMn}!Pl^-uT^IP-$Z@pW7SAIhoQywO!oUv>>v)8(0Vz0`&tS7T`@1H;0a^0pfPPy?rt3+~-;W04r&zQSTU5WE< z>6#d=uCm?Dizjb;Z=tnwjhcz?(Q~iO_ox2;x6`sLCS_80$XZ1cUseXg*fTo?-PLnL z);>JKY|HKORNLw9jgq@5LE#&!3(QiF{p|8GnzZYYm7KS@i&p%{xd!K%UfnLt5&a%> zT)h8vL{z+U$fR^WnI$V!l-H$fsE8?_D}5vOe3Hz(^F=BPch4(7Y2fO(vvHMa%iR?} z`&5w606AP_>yqKmJGBSDT5a!d-&GKRh^HT~oEA>GNg2Dqrr})vb+p zT|*`rZrHt0Q$g!wQbM`=eAUA)_p=_}oa|mvSJ4@F`IGL!QiG}MJDz^hI_GcwVow$4 zNdv|MJAYodvt@q#X^rV!H*75qU-~sGpK~7Tm$hOAU;niHYg?cArJ`oRsW&%pl%$@> zj&Im(n7DdJ`>(Q^Pc28(ZYe)Gv(5N4FjDmL?m7Ott-9yi^A^Vk`x$lz0t2%(WyYh& ze?@hshcC?3;>*9=mNKLGzy68Ttq$uAzxEux#3~qW!~j&+y1>7uhUgCe!pd{@ht)w4ObP0l+XkK D4hBi% delta 372 zcmX@W+Q&RWxt@WsILO_JVcj{ImkbPy9-c0aAr*7p-rDGOI6&ms$Ng8i*2?N|Hg)m~ zx^7sZuaMW2?EYIsFVCu^AT*|i!FuM&qX$FJ3UfGVutan5$r>Fw8D+Y3S?-dH@3+Sm z>D5>sjy=aH2mur8)6y=mA1_p5xc_})t~A5#O*_i&HrAIfJL&)V{LxrG=SAUVH!Y)< zZ`yMtG`D;2X6vRjhIO;6eiW*nl)v2&XZRp4Zo}Oi2FF(i)m1(J<@4TH6SC;gTTR*X zhc%|JTx(@>fBxr9We(3@8eV-cd4fs(y8jyrZ*=rz$u=DQo7|H%H*EJ=rdlmA~j8u%K|I-mQTQA4D=CDCL1?bM^2s!aLknQ!e+xc0fb0|7i{xycEq*496W zeN$00W6gX1FVZU+Bk#+3omDn16@btK7d= P7=Xaj)z4*}Q$iB}cR#MF From 0d4a75a58195439fd86e24d9303345a1f8c61b33 Mon Sep 17 00:00:00 2001 From: _Svist_ <148935188+Svist666s@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:49:27 +0400 Subject: [PATCH 136/247] =?UTF-8?q?=D0=92=D0=B5=D1=89=D0=B8=20=D0=B2=D0=B5?= =?UTF-8?q?=D0=B4=D1=83=D1=89=D0=B8=D1=85=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D1=8C=20=D1=82=D1=80=D0=B5=D0=B1=D1=83=D1=8E=D1=82=20=D0=B2?= =?UTF-8?q?=D1=80=D0=B5=D0=BC=D1=8F=20&=20=D0=A8=D0=B8=D0=BD=D0=B5=D0=BB?= =?UTF-8?q?=D1=8C=D0=BA=D0=B0=20=D0=A1=D0=91=20=D0=B2=20=D0=BB=D0=BE=D0=B4?= =?UTF-8?q?=D0=B0=D1=83=D1=82=20(#3554)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Locale/ru-RU/corvax/job/job-names.ftl | 4 +- Resources/Prototypes/Body/Species/human.yml | 2 +- .../Jobs/Engineering/station_engineer.yml | 28 +++++++------- .../Loadouts/Jobs/Medical/medical_doctor.yml | 30 +++++++-------- .../Loadouts/Jobs/Medical/role_timers.yml | 4 +- .../Loadouts/Jobs/Science/scientist.yml | 30 +++++++-------- .../Jobs/Security/security_officer.yml | 37 ++++++++++++++----- .../Loadouts/LoadoutGroups/loadout_groups.yml | 1 + 8 files changed, 77 insertions(+), 59 deletions(-) diff --git a/Resources/Locale/ru-RU/corvax/job/job-names.ftl b/Resources/Locale/ru-RU/corvax/job/job-names.ftl index 890b969157..b4a1248631 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/Prototypes/Body/Species/human.yml b/Resources/Prototypes/Body/Species/human.yml index 1f59cbefe5..b569527a61 100644 --- a/Resources/Prototypes/Body/Species/human.yml +++ b/Resources/Prototypes/Body/Species/human.yml @@ -39,7 +39,7 @@ limit: 1 required: false enum.HumanoidVisualLayers.Tail: - limit: 1 # Corvax-Sponsors + limit: 0 required: false enum.HumanoidVisualLayers.Special: limit: 0 diff --git a/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml b/Resources/Prototypes/Loadouts/Jobs/Engineering/station_engineer.yml index b04f8388db..6cb62fb089 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 49bb43558d..7ff040dc31 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 7eeab8ea8e..b07be72f89 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/role_timers.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/role_timers.yml @@ -6,12 +6,12 @@ requirement: !type:RoleTimeRequirement role: JobChemist - time: 20h # Corvax-RoleTime + time: 6h # Corvax-RoleTime - !type:JobRequirementLoadoutEffect requirement: !type:RoleTimeRequirement role: JobMedicalDoctor - time: 20h # Corvax-RoleTime + time: 6h # Corvax-RoleTime - !type:JobRequirementLoadoutEffect requirement: !type:DepartmentTimeRequirement diff --git a/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml b/Resources/Prototypes/Loadouts/Jobs/Science/scientist.yml index 21965389c4..080015f0da 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 523476e775..8fcde02222 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 b2571c917d..5a84e039c5 100644 --- a/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml +++ b/Resources/Prototypes/Loadouts/LoadoutGroups/loadout_groups.yml @@ -1246,6 +1246,7 @@ - ArmorVestSec # Corvax - ArmorVest - ArmorVestSlim + - SecurityOfficerOvercoat # Corvax - SecurityOfficerWintercoat - type: loadoutGroup From 3ddbee184a75252ca1969078db2cc0e7aec8c831 Mon Sep 17 00:00:00 2001 From: _Svist_ <148935188+Svist666s@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:04:52 +0400 Subject: [PATCH 137/247] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D1=86=D0=B5=D0=B4?= =?UTF-8?q?=D1=83=D1=80=D1=8B=20=D0=BC=D0=B0=D0=B3=D0=B8=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D1=82=D0=B0=20(#3558)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ru-RU/corvax/guidebook/SOP/legal.ftl | 270 +++++++++++------- .../ru-RU/corvax/guidebook/SOP/security.ftl | 5 + .../ru-RU/corvax/guidebook/SOP/service.ftl | 29 -- .../ServerInfo/Corvax/Guidebook/SOP/Legal.xml | 32 ++- .../Corvax/Guidebook/SOP/Service.xml | 10 - 5 files changed, 199 insertions(+), 147 deletions(-) diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl index 51077456ef..2d7f6765b6 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/legal.ftl @@ -1,26 +1,72 @@ # Должностные СРП # Магистрат guidebook-SOP-Magistrate-must = - 1. Ответить на запрос, полученный от лица, имеющего право обращения к магистрату. - 1. При необходимости принять меры одним из следующих методов: - - С помощью факса. Предпочтительный метод. - - Прибыть на объект лично, если задача не может быть решена удалённо. Обеспечить решение в кратчайшие сроки. - 1. Обеспечивать защиту прав существ, обладающих ОПРС. + 1. Инициировать корпоративный суд при передаче службой безопасности дел с обвинениями по статьям категории XX2 и выше при действии зелёного или синего кода. В условиях красного кода инициирование корпоративного суда допускается только по делам с обвинениями категории XX3 и выше. + - В случае невозможности проведения суда по причине чрезмерной нагрузки дел, магистрат обязан передать дела с обвинениями в службу безопасности для вынесения вердикта. + - В случае отсутствия или недееспособности магистрата данные дела автоматически переходят в юрисдикцию службы безопасности. + - В условиях чрезвычайной ситуации инициирование корпоративного суда запрещено. + 1. Соблюдать установленные процедуры корпоративного суда и вынесения вердикта. + 1. Быть [tooltip="guidebook-SOP-Magistrate-tooltip-equanimity" text="беспристрастным"]. + 1. Обеспечить правильное применение Корпоративного закона и защиту прав существ, обладающих ОПРС, на объектах корпорации NanoTrasen + 1. На постоянной основе использовать [tooltip="guidebook-SOP-command-tooltip-business-speech" text="Корпоративно-деловой стиль речи"]. + - Данный пункт утрачивает свою актуальность в условиях чрезвычайной ситуации + 1. Соблюдать утверждённый [tooltip="guidebook-SOP-Magistrate-tooltip-dress-code" text="дресс-код"]. + 1. Осуществлять контроль за деятельностью адвоката в рамках его СРП. guidebook-SOP-Magistrate-right = - 1. Вольно трактовать Корпоративный Закон, выносить и отменять вердикты в отношении лиц, пребывающих под следствием или отбывающих своё наказание. - 1. Руководить агентами внутренних дел на объектах NanoTrasen. - 1. Инициировать проведение судебного заседания согласно соответствующей процедуре, при невозможности решения правового спора иными методами. - 1. Хранить при себе любые виды вооружения и средств самообороны вне зависимости от кода. - 1. Пересмотреть любой документ, оформленный и заверенный в пределах станции, при необходимости признать его недействительным. - 1. В одностороннем порядке предоставить лицу юридическую неприкосновенность либо лишить её. - - Магистрат не может использовать данное право в отношении самого себя. + 1. Для судебного разбирательства магистрат вправе выписывать ордера на арест, обыск личности или отдела, задержание, исключительно в целях обеспечения рассмотрения дела, как по собственной инициативе при наличии оснований, так и по обоснованному запросу службы безопасности. + 1. Отменять любое решение по вопросам, касающимся Корпоративного закона и ОПРС, включая УДО. + - Исключения: приказы Центрального командования и сотрудников ДСО. + 1. Хранить при себе вспышку и использовать её в целях самообороны. + 1. Во время судебного разбирательства, запросить у службы безопасности любые материалы по делу, включая отчёты, доказательства и свидетельские показания, а также материалы, относящиеся к уже заверенному приговору, для анализа и принятия решения. + - В условиях [bold][color=green]зелёного[/color][/bold] или [bold][color=dodgerblue]синего кода[/color][/bold] запросить составление документов по материалу делу. + 1. В условиях [bold][color=green]зелёного[/color][/bold] или [bold][color=dodgerblue]синего кода[/color][/bold], запросить для проверки юридической законности любой документ, составленный на объекте корпорации NanoTrasen. + 1. Отменить юридическую законность любого документа на объекте корпорации NanoTrasen, исключения документы, заверенные печатью Центком. + - В случае отмены документа магистрат обязан написать причину отзыва документа непосредственно в самом документе и заверить документ печатью магистрата. + 1. Проверять любой вынесенный на станции приговор и, при выявлении нарушений, отменять его с обязательным указанием причины + - В случае отмены магистрат обязан составить новый приговор от своего лица, основываясь на материалах дела, доказательств и соблюдении корпоративного закона. + 1. Публично объявить о предстоящем суде, называет место и время начала процедуры. + 1. Рассматривать обращения о досрочном освобождении и утверждать УДО заключённых в соответствии с установленной процедурой выдачи УДО. + 1. Редактировать документы по запросу персонала станции с помощью ручки юридического департамента. + - Редактирование собственных и чужих документов допустимо в случае необходимости исправления ошибок в тексте (без изменения исходного содержания). + - Редактирование чужих документов по аналогичной процедуре. + - В редактированном документе должно быть добавлено [tooltip="guidebook-SOP-legal-tooltip-edit-note" text="примечание о редактировании"], он должен быть заверен печатью магистрата. guidebook-SOP-Magistrate-prohibited = - 1. Принимать решения, противоречащие ОПРС. - 1. Выполнять функции службы безопасности, исключая функции, предусмотренные дозволениями. - 1. Без веских причин отменять действие документов. - 1. Отдавать приказы персоналу станции. Исключение — сотрудники юридического департамента. + 1. Принимать решения, противоречащие Корпоративному закону или ОПРС. + - Выносить некорректные или неправомерные вердикты. + 1. Вмешиваться в деятельность глав отделов, капитана или службы безопасности без [tooltip="guidebook-SOP-Magistrate-tooltip-legal-grounds" text="законных оснований"]. + 1. Проводить допросы по процедуре допроса. + 1. Злопотреблять доступом. + +# Адвокат +guidebook-SOP-Lawyer-must = + 1. Обеспечивать правовую защиту клиентов в рамках действующего Корпоративного Законодательства NanoTrasen, защищать права ОПРС. + 1. Соблюдать [tooltip="guidebook-SOP-service-tooltip-Lawyer-secret" text="адвокатскую тайну"] за исключением случаев, когда информация может нанести прямой ущерб NanoTrasen. + - Нарушения этого пункта расценивается как нарушения статьи КЗ: 144. + 1. Соблюдать утверждённый [tooltip="guidebook-SOP-service-tooltip-Lawyer-dress-code" text="Корпоративный дресс-код"]. + - Обязательные элементы униформы могут исключаться или заменяться в случае физиологических или видовых особенностей сотрудника. + - Запрещено передавать или обменивать служебную униформу третьим лицам. + 1. Вести общение с клиентами, сотрудниками службы безопасности и командованием станции в уважительной форме, соблюдая [tooltip="guidebook-SOP-service-tooltip-Lawyer-business-style" text="Корпоративно-деловой стиль речи"]. + - Данный пункт утрачивает свою актуальность в условиях ЧС. + 1. Оказывать консультации по правовым вопросам существам защищённым ОПРС. + 1. Содействовать клиентам в составлении, оформлении и заверении документации. + +guidebook-SOP-Lawyer-right = + 1. Присутствовать на территории брига, а также при проведении процедур службы безопасности, касающихся клиента. + - Адвокат имеет право явиться в бриг по вызову любого разумного существа, находящегося под арестом или в заключении, для оказания консультации, заключения контрактов и обеспечения юридической защиты. + 1. Заключать юридические контракты о предоставлении защиты с сотрудниками и иными лицами. + - Допускается заключение как устных, так и письменных контрактов. + - Письменный контракт имеет приоритет в случае разногласий. + - Письменный контракт должен содержать чётко определённые условия оказания защиты. + - Адвокат обязан хранить экземпляр письменного контракта (при его наличии) и предоставлять его по требованию службы безопасности. + 1. Запрашивать у службы безопасности любые материалы дела, относящиеся к защите клиента. + 1. Расторгнуть контракт о юридической защите в случае, когда клиент сознательно предоставляет ложные сведения или препятствует ведению защиты. + +guidebook-SOP-Lawyer-prohibited = + 1. Злоупотреблять доступом на территории брига или использовать его вне профессиональной деятельности. + - Нарушения этого пункта расценивается как нарушения статьи КЗ: 423. + 1. Использовать адвокатскую деятельность в целях личной выгоды, не связанных с защитой клиента. # АВД guidebook-SOP-IAA-must = @@ -57,10 +103,10 @@ guidebook-SOP-IAA-right = 1. Хранить при себе и использовать для исправления заверенных документов ручку «центком»: - Изменение собственных документов допустимо в случае необходимости исправления ошибок в тексте (без изменения исходного содержания). - Изменение чужих документов допустимо по аналогичной причине, но возможно лишь по прямому запросу лица, заверившего документ. - - В исправленный документ должно быть добавлено [tooltip="guidebook-SOP-legal-tooltip-iaa-edit-note" text="примечание о редактировании"], он должен быть заверен печатью АВД. + - В исправленный документ должно быть добавлено [tooltip="guidebook-SOP-legal-tooltip-edit-note" text="примечание о редактировании"], он должен быть заверен печатью АВД. 1. Запросить помощь ЦК/ДСО/юридического департамента при определённых условиях. - Запросить вызов ПЦК в случае бездействия руководства станции по факту выявленных нарушений. - - Запросить помощь магистрата в соответствии со специальной процедурой. + - Запросить помощь Магистрата в соответствии со специальной процедурой. - Запросить помощь подразделений ДСО, если в данный момент капитан и главы станции недоступны. 1. Отправлять Центральному Командованию отчёты о ситуации на станции с помощью факса, но не чаще чем один раз в 30 минут. @@ -92,94 +138,122 @@ guidebook-SOP-legal-procedure-internal-investigation = 1. По завершению расследования АВД обязан вернуть ключ шифрования отдела и сдать полученные доступы. 1. АВД должен составить письменный отчёт о проведённом расследовании и предоставить копии главе отдела и капитану для принятия мер по факту выявленных нарушений. -## Процедура обращения к магистрату -guidebook-SOP-legal-procedure-appeal-to-magistrate = - 1. Обращение к магистрату может быть направлено исключительно лицами, имеющими такое право, по одной из указанных причин: - - Возникновение ситуации, при которой Корпоративный Закон не способен защитить ущемление ОПРС какого-либо лица. - - Необходимость оспорить приговор, вынесенный капитаном. - - Необходимость отстранить от работы агента внутренних дел при нарушении им ОПРС, КЗ или СРП. - - Необходимость проведения судебной процедуры. - 1. Обращение должно быть оформлено соответствующим документом и отправлено по факсу, а оператор Центрального Командования должен быть проинформирован о его отправке. - 1. На основании обращения магистрат может поступить следующим образом: - - Принять решение удалённо, затребовав от АВД сбор необходимых материалов для вынесения вердикта. - - Лично прибыть на станцию для решения поставленной задачи. - -## Процедура суда +## Процедура корпоративного суда ### Общие положения guidebook-SOP-legal-court-general = + {"[italic]Корпоративный суд — это официальное разбирательство под руководством магистрата, проводимое в рамках законов NanoTrasen.[/italic]"} + 1. Судебный процесс не может быть инициирован во время чрезвычайной ситуации. + - В случае, если во время судебного заседания, была объявлена чрезвычайная ситуация, суд должен отложен до снятия ЧС. + 1. Процесс корпоративного суда влияет на срок отбывания наказания. Первые 10 минут судебного заседания не учитываются в срок заключения. По истечении 10 минут, дальнейшее время судебного разбирательства засчитывается в срок отбывания наказания. В случае если оставшийся срок заключения в ходе судебного заседания достигает 0, заключённый признаётся полностью отбывшим наказание и подлежит освобождению. + - Пример: общий срок заключения составляет 30 минут. Если судебное заседание длится 20 минут, в срок заключения засчитываются только 10 минут, и оставшийся срок составит 20 минут. + 1. Все судебные процессы должны быть проведены в зале суда. + - В случае если на объекте корпорации отсутствует зал суда или он повреждён, допустимо провести суд в другом публичном месте: церковь, сцена и т.д. + 1. Все суды должны быть открыты для зрителей. + 1. Суд не может длиться дольше 20 минут. По истечению срока магистрат обязан вынести вердикт. + 1. Все участники суда обязаны исполнять требования магистрата. + 1. Все участники суда должны обращаться к магистрату, используя обращение "Ваша честь, магистрат, судья, уважаемый суд". Нарушение данного требования может быть расценено как нарушение статьи 121 "Неуважение к суду" корпоративного закона. + - Магистрат вправе отменить данную форму обращения. + 1. Все участники суда должны говорить исключительно с разрешения магистрата. Нарушение данного требования может быть расценено как нарушение статьи 121 "Неуважение к суду" корпоративного закона. + 1. Все участники суда должны вести себя подобающим образом. Любые случаи неподобающего и вызывающего поведения могут быт расценены как нарушение статьи 121 "Неуважение к суду" корпоративного закона. + - Все участники обязуются говорить искренне, дача ложных показаний, либо намеренное искажение информации, являются нарушением статьи 223 "Дача ложных показаний" корпоративного закона + +### Общие нормы суда +guidebook-SOP-legal-court-norms = 1. Подсудимый считается невиновным, пока в его отношении не будет зачитан обвинительный приговор. - 1. Судебный процесс должен быть открытым либо проходить в закрытом формате. Формат суда определяется судьёй. - 1. Проведение суда возможно, если мерой наказания для задержанного могут быть избраны пожизненное заключение или высшая мера наказания. + 1. Проведение суда невозможна без судьи в лице магистрата. В случае отсутствия магистрата служба безопасности действует по протоколу вынесение вердикта на допросе. 1. Для полноценной реализации судебного процесса требуется наличие нескольких сторон. Без упомянутых лиц проведение процедуры суда невозможно: - - Судья — выносит решение по делу, руководит процессом суда. Таковым является магистрат; в случаях, при которых личное присутствие магистрата невозможно, в качестве судьи могут выступать капитан, Представитель ЦК, лидер ОБР. - - Сторона обвинения — выдвигает обвинение по делу, предоставляет доказательства, подтверждающие виновность подсудимого лица. В качестве данной стороны выступает глава Службы Безопасности. - - Сторона защиты — сторона, которой было выдвинуто обвинение. Представлена лицом, которому выдвинуто обвинение, а также адвокатом, если таковой имеется. - 1. Также в суде принимают участие лица, содействующие осуществлению правосудия, приглашённые стороной обвинения или защиты. Отказ данных лиц от участия в суде может быть расценён как нарушение статьи «Сопротивление органам власти» Корпоративного Закона. + - Судья — выносит решение по делу, руководит процессом суда. Таковым является магистрат. + - Сторона обвинения — выдвигает обвинение по делу, предоставляет доказательства, подтверждающие виновность подсудимого лица. В качестве данной стороны выступает: Капитан, Глава Службы Безопасности, Смотритель, прочие сотрудники службы безопасности с разрешения вышестоящих по должности в качестве обвинителя. + - Сторона защиты — сторона, которой было выдвинуто обвинение. Представлена лицом, которому выдвинуто обвинение, а также адвокатом. В качестве адвоката может выступать: адвокат, глава отдела, капитан. Присутствие адвоката необязательно. + - В суде принимают участие лица, содействующие осуществлению правосудия, приглашенные стороной обвинения или защиты. Отказ данных лиц от участия в суде может быть расценен как нарушение статьи 112 "Сопротивление органам власти" корпоративного закона. - Потерпевшие — лица, ставшие жертвами преступления и пострадавшие в ходе обстоятельств дела. - - Эксперты — лица, привлечённые к исследованию вещественных доказательств, физического состояния потерпевших и подсудимого, оценке ущерба от совершённого преступления и т.д. - - Свидетели — лица, обладающие информацией по делу, полученной при помощи органов чувств. + - Эксперты — лица, привлеченные и исследованию вещественных доказательств, физического состояния потерпевших и подсудимого, оценке ущерба от совершенного преступления и т.д. + - Свидетели — лица, обладающее информацией по делу, полученной при помощи органов чувств. -### Нормы поведения в суде -guidebook-SOP-legal-court-behavior = - 1. Все участники суда должны обращаться к судье, используя обращение «Ваша честь». Нарушение данного требования может быть расценено как нарушение статьи «Неуважение к суду» Корпоративного Закона. Судья вправе отменить данную форму обращения. - 1. Все участники суда должны говорить исключительно с разрешения судьи. Нарушение данного требования может быть расценено как нарушение статьи «Неуважение к суду» Корпоративного Закона. - 1. Все участники суда должны вести себя подобающим образом. Любые случаи неподобающего и вызывающего поведения могут быть расценены как нарушение статьи «Неуважение к суду» Корпоративного Закона. - 1. Все участники обязуются говорить искренне; дача ложных показаний либо намеренное искажение информации являются нарушением статьи «Крупное мошенничество» Корпоративного Закона. +### Обязанности сторон +guidebook-SOP-legal-court-responsibilities = + {"Магистрат обязан:"} + 1. Открыть заседание и объявить стороны процесса. + 1. Предоставить первое слово стороне обвинения. + 1. После изложения обвинения уточнить у подсудимого, согласен ли он с обвинением и изложенными фактами. + 1. Предоставить слово стороне защиты. + 1. Руководить допросом свидетелей и экспертов, контролируя порядок в суде. + 1. Предоставить сторонам время для прений: первой выступает сторона обвинения, затем защита. + 1. Предоставить подсудимому последнее слово. + 1. Вынести и огласить приговор. + 1. Объявить заседание закрытым. + {"Сторона обвинения обязана:"} + 1. Первой изложить обвинение и предоставить доказательства. + 1. По необходимости пригласить потерпевших и свидетелей. + 1. Сформулировать требуемую меру наказания в прениях. + {"Сторона защиты обязана:"} + 1. После обвинения изложить свою позицию. + 1. Предоставить доказательства в защиту подсудимого. + 1. Пригласить свидетелей защиты (при наличии). + 1. В прениях указать на смягчающие обстоятельства или требовать оправдания. + {"Подсудимый имеет право:"} + 1. Озвучить свою позицию после выступления стороны обвинения. + 1. Давать показания и представлять доказательства. + 1. Воспользоваться последним словом перед вынесением приговора. -### Досудебный этап -guidebook-SOP-legal-court-open = - 1. Открытый судебный процесс не может быть инициирован в условиях угрозы уровня [bold][color=red]красного кода[/color][/bold] или [color=gold][bold]гамма и выше[/bold][/color]. При объявлении [bold][color=red]красного кода[/color][/bold] во время процедуры суд должен быть перенесён до снижения кода угрозы до [bold][color=dodgerblue]синего[/color][/bold]. - 1. Судья публично объявляет о предстоящем суде, называет место и время начала процедуры. С момента объявления и до начала суда должно пройти не менее 10 минут. - 1. Подсудимый до начала суда должен находиться под стражей. Время пребывания под стражей должно быть учтено при вынесении приговора. - 1. Обязательные участники суда должны прибыть в зал суда за 5 минут до начала слушания. - 1. Лица, содействующие осуществлению правосудия, могут явиться до начала слушания без опозданий. - 1. В открытом судебном процессе допустимо присутствие зрителей в зале суда. +## Судебное разбирательство +### Судебное разбирательство +guidebook-SOP-legal-court-litigation = + {"[italic]Судебное разбирательство — процесс, в ходе которого магистрат изучает материалы дела, заслушивает показания сторон, свидетелей и экспертов, оценивает доказательства и принимает решение о виновности или невиновности подсудимого.[/italic]"} + 1. Для вынесения обвинительного приговора необходимо наличие доказательной базы, подтверждающей вину подсудимого. + 1. При рассмотрении дела учитываются как прямые, так и косвенные доказательства. + 1. Доказательства признаются состоятельными, если подтверждают факт совершения преступления и не противоречат друг другу. + 1. В случае противоречий материалы считаются недействительными. + 1. При отрицании подсудимым своей вины именно доказательства должны являться достаточными для её подтверждения. + 1. Если доказательств недостаточно — подсудимый признаётся невиновным. + 1. Прямые доказательства: + - Сам объект или субъект преступления (потерпевший). + - Вещественные доказательства: орудие преступления, украденное имущество и д.р. + - Показания минимум трёх свидетелей с совпадающими деталями. + - Показания боргов (ИИ, киборгов). + - Заключения экспертов. + - Явка с повинной. + - Биологические следы: отпечатки пальцев, образцы ДНК, следы крови и т.п. + - Аудиозаписи и фотографии. + 1. Косвенные доказательства: + - Наличие у подсудимого мотива. + - Отсутствие алиби. + - Негативные личные отношения с жертвой. + - Тяжёлое материальное положение. + - Наличие дисциплинарных взысканий, выговоров. + - Судимости. + - Состояние алкогольного или наркотического опьянения. + 1. Обвинение может быть снято магистратом на этапе судебного разбирательства при наличии одного или нескольких из следующих факторов: + - Недостаток прямых доказательств. + - Отсутствует субъект преступления или орудие преступления. + - Нет вещественных доказательств, подтверждающих факт нарушения. + - Показания свидетелей (минимум трёх) противоречивы или не соответствуют фактам. + - Биологические следы не подтверждают участие подсудимого. + - Сомнительность косвенных доказательств. + - Мотив не подтверждён другими фактами. + - Нет убедительных свидетельств отсутствия алиби. + - Негативные отношения с жертвой, материальное положение, дисциплинарные взыскания, судимости или опьянение не подтверждают факт совершения преступления. + - Противоречие между доказательствами. + - Прямые доказательства противоречат друг другу. + - Косвенные доказательства опровергают прямые факты. + - Явная невиновность подсудимого. + - Подсудимый признаёт факт, но доказательства указывают на то, что он не совершал преступления. + - Любой факт, который исключает вину подсудимого по сути или обстоятельствам дела. - {"[head=3]Подготовка к слушанию[/head]"} - 1. Если процедура проводится в условиях [bold][color=dodgerblue]синего кода[/color][/bold], то все участники суда, не имеющие юридической неприкосновенности, должны быть подвергнуты процедуре обыска перед посещением зала суда. - 1. Перед началом слушания все участники должны занять места в зале. - 1. Подсудимый может быть закован в наручники и должен находиться под контролем офицера СБ. - 1. Все участники суда должны исполнять требования судьи. - 1. Лица, содействующие осуществлению правосудия, и зрители могут быть исключены из зала суда по решению судьи. - - {"[head=3]Слушание[/head]"} - 1. Судья открывает слушание: зачитывает вступительную речь, информирует, в каком порядке пройдёт суд, оглашает правила поведения в суде. - 1. Сторона обвинения представляет материалы дела. - 1. Сторона защиты представляет свои интересы. - 1. Судья приглашает лиц, содействующих осуществлению правосудия, для дачи показаний. - 1. Судья уточняет, будут ли стороны приобщать дополнительные материалы к делу. - 1. Подсудимый говорит последнее слово. - - В своём последнем слове подсудимый может признаться в преступлении и просить снисхождения, настаивать на своей невиновности либо вовсе промолчать. - 1. Судья оглашает приговор. - - {"[head=3]Приговор[/head]"} - 1. При вынесении приговора судья должен руководствоваться материалами дела, чтобы установить виновность или невиновность подсудимого, и на основании полученных данных озвучить приговор. - 1. Приговор вступает в силу сразу после его оглашения. - - В случае обвинительного приговора подсудимый признаётся виновным в совершении преступления и отправляется отбывать наказание с учётом времени, проведённого в бриге до начала слушания. - - В случае оправдательного приговора подсудимый признаётся невиновным и должен быть немедленно освобождён, личное имущество подсудимого должно быть возвращено в кратчайшие сроки. - 1. После оглашения приговора суд считается завершённым. - - В течение 10 минут судья должен составить приговор в письменном виде, предоставить его копии стороне обвинения и защиты. - -### Досудебный этап -guidebook-SOP-legal-court-closed = - 1. Закрытый судебный процесс не может быть инициирован в условиях угрозы уровня [color=gold][bold]гамма кода[/bold][/color]. При объявлении гамма-кода во время процедуры суд должен быть перенесён до снижения кода угрозы до [bold][color=red]красного кода[/color][/bold]. - 1. Судья должен огласить место проведения слушания и время его начала участникам суда либо обеспечить получение информации данными лицами. Слушание может быть начато сразу же после объявления. - - {"[head=3]Слушание[/head]"} - 1. Вне зависимости от кода угрозы все участники суда, не имеющие юридической неприкосновенности, должны быть подвергнуты процедуре обыска. - 1. На постоянной основе в месте проведения суда находятся лишь обязательные участники слушания. - 1. Лица, содействующие осуществлению правосудия, вызываются в зал суда по одному; после дачи показаний они покидают суд. - 1. Судья проводит опрос сторон суда в удобном ему порядке. - 1. Судья объявляет о готовности вынести вердикт по делу. - - {"[head=3]Приговор[/head]"} - 1. При вынесении приговора судья должен руководствоваться материалами дела, чтобы установить виновность или невиновность подсудимого, и на основании полученных данных озвучить приговор. - 1. Приговор вступает в силу сразу после его оглашения. - - В случае обвинительного приговора подсудимый признаётся виновным в совершении преступления и отправляется отбывать наказание с учётом времени, проведённого в бриге до начала слушания. - - В случае оправдательного приговора подсудимый признаётся невиновным и должен быть немедленно освобождён, личное имущество подсудимого должно быть возвращено в кратчайшие сроки. - 1. После оглашения приговора суд считается завершённым. - - В течение 10 минут судья должен составить приговор в письменном виде, предоставить его копии стороне обвинения и защиты. +### Вынесение вердикта +guidebook-SOP-legal-court-verdict = + {"[italic]Вердикт — официальное решение магистрата о виновности или невиновности подсудимого, который может быть оспорен только Центральным Командованием.[/italic]"} + 1. Порядок: + - После анализа дела магистрат формирует вывод. + - Приговор оглашается в присутствии сторон. + - Оглашение должно содержать: квалификацию совершённого деяния (со статьями корпоративного закона), указание на смягчающие/отягчающие модификаторы при наличии. + 1. Приговор подлежит обязательному письменному составлением магистратом и передаче копии в службу безопасности. + - Исключение составляют случаи действия чрезвычайных ситуаций. # Tooltip -guidebook-SOP-legal-tooltip-iaa-edit-note = В документ над полем для печатей должна быть добавлена строка, в которой будет указано, что документ был отредактирован; в строке должны быть указаны ФИ АВД. +guidebook-SOP-legal-tooltip-edit-note = В документ над полем для печатей должна быть добавлена строка, в которой будет указано, что документ был отредактирован; в строке должны быть указаны ФИ редактора. guidebook-SOP-legal-tooltip-doc-forgery = Данный пункт подразумевает любое редактирование заверенного документа (с печатью), изменяющее его изначальный смысл, либо изменение документа без согласия лица, заверившего его своей печатью. + +guidebook-SOP-Magistrate-tooltip-equanimity = Рассматривать судебные дела объективно, не допуская личной заинтересованности или влияния внешних факторов на вынесение решения. В случае наличия личной заинтересованности, конфликта интересов или иных отягощающих факторов, магистрат обязан заявить самоотвод и передать дело иному уполномоченному лицу либо приостановить разбирательство до устранения указанных обстоятельств. +guidebook-SOP-Magistrate-tooltip-dress-code = Мантия судьи, парик, значок адвоката. +guidebook-SOP-Magistrate-tooltip-legal-grounds = Является необходимость проведения следствия или инспекция без вмешательства в ход работы. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl index 40773619f7..28c24ac427 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/security.ftl @@ -10,6 +10,10 @@ guidebook-SOP-security-general-must = 1. Исполнять законные требования представителей юридического департамента и Центрального Командования, а также требования вышестоящих по иерархии лиц. 1. По требованию смотрителя или главы службы безопасности предъявить личные вещи для досмотра в любой момент времени. 1. В случае временного отстранения от службы прекратить исполнение текущей задачи и обеспечить скорейшее устранение фактора, повлекшего отстранение. В случае отстранения ввиду нарушения КЗ или СРП сотрудник обязан явиться в бриг в кратчайшие сроки. + 1. Передавать задержанных, обвиняемых по статьям категории XX2 и выше, в корпоративный суд. + - В условиях [color=red][bold]красного кода[/bold][/color] передавать задержанных в корпоративный суд по делам с обвинениями категории XX3 и выше. + - В условиях чрезвычайной ситуации передача задержанных в корпоративный суд не осуществляется. + - В отсутвие или недееспособности магистрата данный пункт игнорируется. guidebook-SOP-security-general-right = 1. Использовать снаряжение, разрешённое в рамках текущего уровня угрозы. @@ -386,6 +390,7 @@ guidebook-SOP-security-procedure-investigation-interrogation = - Сокращение срока не предусмотрено, если итоговый срок заключения, к которому может быть приговорён подозреваемый составляет более 75 минут. - Суммарное время допроса не может превышать срок, равный 10 минутам в сумме с потенциальным сроком заключения, к которому подозреваемый может быть приговорён при подтверждении его виновности. - Если в результате сокращения срока заключения фактический срок заключения равен 0, подозреваемый должен быть признан как отбывший срок заключения в полном объеме. + - В случае направления задержанного в корпоративный суд, допрос ограничивается сроком до 5 минут, после чего задержанный должен быть незамедлительно направлен в суд. Магистрат может отменить это требование в случае занятости судебными делами. 1. В процедуре допроса принимают участие задержанный, допрашивающий, а также адвокат, свидетели и эксперты, если таковые имеются. - В роли допрашивающего может выступать смотритель, помощник смотрителя, глава службы безопасности, капитан, либо иной сотрудник службы безопасности — с разрешения ранее упомянутых лиц. - При нарушении порядка проведения процедуры агент внутренних дел уполномочен отстранить допрашивающего от дальнейшего проведения допроса, в таком случае проведение допроса должно быть передано вышестоящему сотруднику, вплоть до капитана. Агент внутренних дел не может отстранить допрашивающего в [color=red][bold]красном коде[/bold][/color] и выше. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl index fec05b3140..7e9f742a12 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl @@ -40,35 +40,6 @@ guidebook-SOP-HeadOfPersonnel-prohibited = 1. Вне чрезвычайных ситуаций покидать рабочее место, если в очереди к нему стоит персонал. 1. Заниматься работой Службы Безопасности, кроме случая, описанного в пункте 7 дозволений. -## Адвокат -guidebook-SOP-Lawyer-must = - 1. Обеспечивать правовую защиту клиентов в рамках действующего Корпоративного Законодательства NanoTrasen, защищать права ОПРС. - 1. Соблюдать [tooltip="guidebook-SOP-service-tooltip-Lawyer-secret" text="адвокатскую тайну"] за исключением случаев, когда информация может нанести прямой ущерб NanoTrasen. - - Нарушения этого пункта расценивается как нарушения статьи КЗ: 144. - 1. Соблюдать утверждённый [tooltip="guidebook-SOP-service-tooltip-Lawyer-dress-code" text="Корпоративный дресс-код"]. - - Обязательные элементы униформы могут исключаться или заменяться в случае физиологических или видовых особенностей сотрудника. - - Запрещено передавать или обменивать служебную униформу третьим лицам. - 1. Вести общение с клиентами, сотрудниками службы безопасности и командованием станции в уважительной форме, соблюдая [tooltip="guidebook-SOP-service-tooltip-Lawyer-business-style" text="Корпоративно-деловой стиль речи"]. - - Данный пункт утрачивает свою актуальность в условиях ЧС. - 1. Оказывать консультации по правовым вопросам существам защищённым ОПРС. - 1. Содействовать клиентам в составлении, оформлении и заверении документации. - -guidebook-SOP-Lawyer-right = - 1. Присутствовать на территории брига, а также при проведении процедур службы безопасности, касающихся клиента. - - Адвокат имеет право явиться в бриг по вызову любого разумного существа, находящегося под арестом или в заключении, для оказания консультации, заключения контрактов и обеспечения юридической защиты. - 1. Заключать юридические контракты о предоставлении защиты с сотрудниками и иными лицами. - - Допускается заключение как устных, так и письменных контрактов. - - Письменный контракт имеет приоритет в случае разногласий. - - Письменный контракт должен содержать чётко определённые условия оказания защиты. - - Адвокат обязан хранить экземпляр письменного контракта (при его наличии) и предоставлять его по требованию службы безопасности. - 1. Запрашивать у службы безопасности любые материалы дела, относящиеся к защите клиента. - 1. Расторгнуть контракт о юридической защите в случае, когда клиент сознательно предоставляет ложные сведения или препятствует ведению защиты. - -guidebook-SOP-Lawyer-prohibited = - 1. Злоупотреблять доступом на территории брига или использовать его вне профессиональной деятельности. - - Нарушения этого пункта расценивается как нарушения статьи КЗ: 423. - 1. Использовать адвокатскую деятельность в целях личной выгоды, не связанных с защитой клиента. - ## Радиоведущий guidebook-SOP-RadioHost-must = 1. Обеспечивать стабильную и корректную работу радиоканала, информировать экипаж станции о событиях на объекте корпорации, проводить прямые радиоэфиры. diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml index af4a9492a7..044ec54d7c 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Legal.xml @@ -10,6 +10,16 @@ ### Магистрату запрещено + ## СРП Адвоката + ### Адвокат обязан + + + ### Адвокат имеет право + + + ### Адвокату запрещено + + ## СРП Агента внутренних дел ### Агент внутренних дел обязан @@ -24,19 +34,21 @@ ## Процедура внутреннего расследования - ## Процедура обращения к магистрату - - - ## Процедура суда + ## Процедура корпоративного суда ### Общие положения - ### Нормы поведения в суде - + ### Общие нормы суда + - ### Открытый суд - + ### Обязанности сторон + + + ## Судебное разбирательство + ### Судебное разбирательство + + + ### Вынесение вердикта + - ### Закрытый суд - diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml index 55979b5966..e55adf6313 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml @@ -10,16 +10,6 @@ ### Главе персонала запрещено - ## СРП Адвоката - ### Адвокат обязан - - - ### Адвокат имеет право - - - ### Адвокату запрещено - - ## СРП Радиоведущего ### Радиоведущий обязан From 83bab5db51b745842339da0b517074efccfe7985 Mon Sep 17 00:00:00 2001 From: _Svist_ <148935188+Svist666s@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:03:41 +0400 Subject: [PATCH 138/247] markingfix(#3560) --- Resources/Prototypes/Body/Species/human.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Body/Species/human.yml b/Resources/Prototypes/Body/Species/human.yml index b569527a61..1f59cbefe5 100644 --- a/Resources/Prototypes/Body/Species/human.yml +++ b/Resources/Prototypes/Body/Species/human.yml @@ -39,7 +39,7 @@ limit: 1 required: false enum.HumanoidVisualLayers.Tail: - limit: 0 + limit: 1 # Corvax-Sponsors required: false enum.HumanoidVisualLayers.Special: limit: 0 From a41669358cb969b955a60f1cda9cca99eb66ddc7 Mon Sep 17 00:00:00 2001 From: Dmitry <57028746+DIMMoon1@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:11:09 +0700 Subject: [PATCH 139/247] proto fix --- Content.Server/Telephone/TelephoneSystem.cs | 4 +- .../structures/Walls/invisible_wall.ftl | 2 +- .../Corvax/SoundCollections/growl.yml | 6 -- .../Corvax/Voice/speech_emote_sounds.yml | 69 ------------------- .../Prototypes/SoundCollections/vulpkanin.yml | 17 ++--- 5 files changed, 12 insertions(+), 86 deletions(-) delete mode 100644 Resources/Prototypes/Corvax/SoundCollections/growl.yml diff --git a/Content.Server/Telephone/TelephoneSystem.cs b/Content.Server/Telephone/TelephoneSystem.cs index 0314a116b9..052fcd8871 100644 --- a/Content.Server/Telephone/TelephoneSystem.cs +++ b/Content.Server/Telephone/TelephoneSystem.cs @@ -118,12 +118,12 @@ public sealed class TelephoneSystem : SharedTelephoneSystem // If speaker entity has TTS, the telephone will speak with the same voice if(TryComp(args.MessageSource, out var ttsSpeaker)) { - var ttsTelephone = EnsureComp(entity); + var ttsTelephone = EnsureComp(speaker); ttsTelephone.VoicePrototypeId = ttsSpeaker.VoicePrototypeId; } else // Remove TTS if the speaker has no TTS { - RemComp(entity); + RemComp(speaker); } // Corvax-TTS-End _chat.TrySendInGameICMessage(speaker, args.Message, volume, range, nameOverride: name, checkRadioPrefix: false); diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/structures/Walls/invisible_wall.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/structures/Walls/invisible_wall.ftl index 7b55aafa1b..9702062853 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/structures/Walls/invisible_wall.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/structures/Walls/invisible_wall.ftl @@ -1,3 +1,3 @@ ent-MarkerBlocker = невидимая стена .desc = { ent-MarkerBase.desc } - .suffix = Маркер, Невидимая + .suffix = Маркер, Админ diff --git a/Resources/Prototypes/Corvax/SoundCollections/growl.yml b/Resources/Prototypes/Corvax/SoundCollections/growl.yml deleted file mode 100644 index d98820e832..0000000000 --- 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 d0f2b47350..def73de064 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/SoundCollections/vulpkanin.yml b/Resources/Prototypes/SoundCollections/vulpkanin.yml index de3ea1f6a1..4ad5712ae4 100644 --- a/Resources/Prototypes/SoundCollections/vulpkanin.yml +++ b/Resources/Prototypes/SoundCollections/vulpkanin.yml @@ -8,13 +8,13 @@ - type: soundCollection id: VulpkaninGrowls files: - - /Audio/Voice/Vulpkanin/dog_growl1.ogg - - /Audio/Voice/Vulpkanin/dog_growl2.ogg - - /Audio/Voice/Vulpkanin/dog_growl3.ogg - - /Audio/Voice/Vulpkanin/dog_growl4.ogg - - /Audio/Voice/Vulpkanin/dog_growl5.ogg - - /Audio/Voice/Vulpkanin/dog_growl6.ogg -# Corvax-CorvaxVulp_Port-Start + # Corvax-CorvaxVulp_Port-Start + #- /Audio/Voice/Vulpkanin/dog_growl1.ogg + #- /Audio/Voice/Vulpkanin/dog_growl2.ogg + #- /Audio/Voice/Vulpkanin/dog_growl3.ogg + #- /Audio/Voice/Vulpkanin/dog_growl4.ogg + #- /Audio/Voice/Vulpkanin/dog_growl5.ogg + #- /Audio/Voice/Vulpkanin/dog_growl6.ogg - /Audio/Corvax/Effects/Growl/growl1.ogg - /Audio/Corvax/Effects/Growl/growl2.ogg - /Audio/Corvax/Effects/Growl/growl3.ogg @@ -35,4 +35,5 @@ - type: soundCollection id: VulpkaninHowls files: - - /Audio/Voice/Vulpkanin/howl.ogg + #- /Audio/Voice/Vulpkanin/howl.ogg + - /Audio/Corvax/Effects/howl.ogg # Corvax-CorvaxVulp_Port \ No newline at end of file From 50b15b7c459944af1d78d8b052263916db7771bf Mon Sep 17 00:00:00 2001 From: SnappingOpossum Date: Fri, 10 Apr 2026 06:38:43 +1000 Subject: [PATCH 140/247] Move food, drink, and vending random spawners to tables + minor cleanup (#42278) * Move food/drink related spawners to entity tables * Give RandomFoodBreakfast a different suffix * More unrelated convention stuff because I felt like it --- .../Random/Food_Drinks/donkpocketbox.yml | 41 +-- .../Random/Food_Drinks/drinks_glass.yml | 263 +++++++++--------- .../Random/Food_Drinks/food_baked_single.yml | 176 ++++++------ .../Random/Food_Drinks/food_baked_whole.yml | 113 ++++---- .../Random/Food_Drinks/food_breakfast.yml | 19 +- .../Spawners/Random/Food_Drinks/food_meal.yml | 178 ++++++------ .../Random/Food_Drinks/food_single.yml | 126 +++++---- .../Markers/Spawners/Random/vending.yml | 58 ++-- .../Markers/Spawners/Random/vendingdrinks.yml | 38 +-- .../Markers/Spawners/Random/vendingsnacks.yml | 30 +- 10 files changed, 530 insertions(+), 512 deletions(-) diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/donkpocketbox.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/donkpocketbox.yml index 05ad0ba725..b921582ff7 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/donkpocketbox.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/donkpocketbox.yml @@ -1,23 +1,24 @@ - type: entity - name: Donkpocket Box Spawner - id: DonkpocketBoxSpawner parent: MarkerBase + id: DonkpocketBoxSpawner + name: Donkpocket Box Spawner components: - - type: Sprite - layers: - - state: red - - sprite: Objects/Consumable/Food/Baked/donkpocket.rsi - state: box - - type: RandomSpawner - prototypes: - - FoodBoxDonkpocket - - FoodBoxDonkpocketSpicy - - FoodBoxDonkpocketTeriyaki - - FoodBoxDonkpocketPizza - - FoodBoxDonkpocketStonk - - FoodBoxDonkpocketBerry - - FoodBoxDonkpocketHonk - - FoodBoxDonkpocketDink - - FoodBoxDonkpocketMoth - chance: 0.5 - offset: 0.0 + - type: Sprite + layers: + - state: red + - sprite: Objects/Consumable/Food/Baked/donkpocket.rsi + state: box + - type: EntityTableSpawner + table: !type:GroupSelector + prob: 0.5 + children: + - id: FoodBoxDonkpocket + - id: FoodBoxDonkpocketSpicy + - id: FoodBoxDonkpocketTeriyaki + - id: FoodBoxDonkpocketPizza + - id: FoodBoxDonkpocketStonk + - id: FoodBoxDonkpocketBerry + - id: FoodBoxDonkpocketHonk + - id: FoodBoxDonkpocketDink + - id: FoodBoxDonkpocketMoth + offset: 0.0 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml index d049250b55..ac845e55d4 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/drinks_glass.yml @@ -1,140 +1,143 @@ - type: entity + parent: MarkerBase id: RandomDrinkGlass name: random drink spawner suffix: Glass - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Drinks/beerglass.rsi - state: icon - - type: RandomSpawner - #small item - prototypes: - - DrinkAbsintheGlass - - DrinkAleGlass - - DrinkAlienBrainHemorrhage - - DrinkAloe - - DrinkAndalusia - - DrinkAntifreeze - - DrinkArnoldPalmer - - DrinkB52Glass - - DrinkBahamaMama - - DrinkBananaHonkGlass - - DrinkBarefootGlass - - DrinkBeerglass - - DrinkBerryJuice - - DrinkBlackRussianGlass - - DrinkBlueCuracaoGlass - - DrinkBlueHawaiianGlass - - DrinkBloodyMaryGlass - - DrinkBooger - - DrinkBraveBullGlass - - DrinkBronxGlass - - BudgetInsulsDrinkGlass - - DrinkCarrotJuice - - DrinkCoconutRum - - DrinkChocolateGlass - - DrinkCognacGlass - - DrinkCosmopolitan - - DrinkCrushDepthGlass - - DrinkCubaLibreGlass - - DrinkDarkandStormyGlass - - DrinkDeadRumGlass - - DrinkDevilsKiss - - DrinkDriestMartiniGlass - - DrinkDrGibbGlass - - DrinkElectricSharkGlass - - DrinkErikaSurprise - - DrinkFourteenLokoGlass - - DrinkGargleBlasterGlass - - DrinkGinFizzGlass - - DrinkGinGlass - - DrinkGinTonicglass - - DrinkGildlagerGlass - - DrinkGrapeJuice - - DrinkGreenTeaGlass - - DrinkGrogGlass - - DrinkHippiesDelightGlass - - DrinkIcedCoffeeGlass - - DrinkIcedGreenTeaGlass - - DrinkIcedBeerGlass - - DrinkIceCreamGlass - - IrishBoolGlass - - DrinkIrishSlammer - - DrinkIrishCoffeeGlass - - DrinkLemonadeGlass - - DrinkJackRoseGlass - - DrinkJungleBirdGlass - - DrinkKalimotxoGlass - - DrinkOrangeLimeSodaGlass - - DrinkLongIslandIcedTeaGlass - - DrinkManhattanGlass - - DrinkManlyDorfGlass - - DrinkMargaritaGlass - - DrinkMartiniGlass - - DrinkMeadGlass - - DrinkMilkshake - - DrinkMojito - - DrinkMonkeyBusinessGlass - - DrinkNTCahors - - DrinkPainkillerGlass - - DrinkPatronGlass - - DrinkPinaColadaGlass - - DrinkPoscaGlass - - DrinkRadlerGlass - - DrinkRedMeadGlass - - DrinkRewriter - - DrinkRoyRogersGlass - - DrinkRootBeerFloatGlass - - RubberneckGlass - - DrinkRumGlass - - DrinkSakeGlass - - DrinkSbitenGlass - - DrinkScrewdriverCocktailGlass - - DrinkShirleyTempleGlass - - DrinkSuiDreamGlass - - DrinkSingulo - - DrinkSoyLatte - - DrinkSyndicatebomb - - DrinkTequilaSunriseGlass - - DrinkThreeMileIslandGlass - - DrinkTortugaGlass - - DrinkToxinsSpecialGlass - - DrinkVampiroGlass - - DrinkVodkaMartiniGlass - - DrinkVodkaRedBool - - DrinkVodkaTonicGlass - - DrinkWatermelonJuice - - DrinkWatermelonWakeup - - DrinkWhiskeyColaGlass - - DrinkWhiskeySodaGlass - - DrinkWhiteRussianGlass - - DrinkWineGlass - - XenoBasherGlass - - DrinkShakeBlue - - DrinkShakeWhite - - DrinkTheMartinez - - DrinkMoonshineGlass - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Drinks/beerglass.rsi + state: icon + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector #small item + weight: 0.95 + prob: 0.8 + children: + - id: DrinkAbsintheGlass + - id: DrinkAleGlass + - id: DrinkAlienBrainHemorrhage + - id: DrinkAloe + - id: DrinkAndalusia + - id: DrinkAntifreeze + - id: DrinkArnoldPalmer + - id: DrinkB52Glass + - id: DrinkBahamaMama + - id: DrinkBananaHonkGlass + - id: DrinkBarefootGlass + - id: DrinkBeerglass + - id: DrinkBerryJuice + - id: DrinkBlackRussianGlass + - id: DrinkBlueCuracaoGlass + - id: DrinkBlueHawaiianGlass + - id: DrinkBloodyMaryGlass + - id: DrinkBooger + - id: DrinkBraveBullGlass + - id: DrinkBronxGlass + - id: BudgetInsulsDrinkGlass + - id: DrinkCarrotJuice + - id: DrinkCoconutRum + - id: DrinkChocolateGlass + - id: DrinkCognacGlass + - id: DrinkCosmopolitan + - id: DrinkCrushDepthGlass + - id: DrinkCubaLibreGlass + - id: DrinkDarkandStormyGlass + - id: DrinkDeadRumGlass + - id: DrinkDevilsKiss + - id: DrinkDriestMartiniGlass + - id: DrinkDrGibbGlass + - id: DrinkElectricSharkGlass + - id: DrinkErikaSurprise + - id: DrinkFourteenLokoGlass + - id: DrinkGargleBlasterGlass + - id: DrinkGinFizzGlass + - id: DrinkGinGlass + - id: DrinkGinTonicglass + - id: DrinkGildlagerGlass + - id: DrinkGrapeJuice + - id: DrinkGreenTeaGlass + - id: DrinkGrogGlass + - id: DrinkHippiesDelightGlass + - id: DrinkIcedCoffeeGlass + - id: DrinkIcedGreenTeaGlass + - id: DrinkIcedBeerGlass + - id: DrinkIceCreamGlass + - id: IrishBoolGlass + - id: DrinkIrishSlammer + - id: DrinkIrishCoffeeGlass + - id: DrinkLemonadeGlass + - id: DrinkJackRoseGlass + - id: DrinkJungleBirdGlass + - id: DrinkKalimotxoGlass + - id: DrinkOrangeLimeSodaGlass + - id: DrinkLongIslandIcedTeaGlass + - id: DrinkManhattanGlass + - id: DrinkManlyDorfGlass + - id: DrinkMargaritaGlass + - id: DrinkMartiniGlass + - id: DrinkMeadGlass + - id: DrinkMilkshake + - id: DrinkMojito + - id: DrinkMonkeyBusinessGlass + - id: DrinkNTCahors + - id: DrinkPainkillerGlass + - id: DrinkPatronGlass + - id: DrinkPinaColadaGlass + - id: DrinkPoscaGlass + - id: DrinkRadlerGlass + - id: DrinkRedMeadGlass + - id: DrinkRewriter + - id: DrinkRoyRogersGlass + - id: DrinkRootBeerFloatGlass + - id: RubberneckGlass + - id: DrinkRumGlass + - id: DrinkSakeGlass + - id: DrinkSbitenGlass + - id: DrinkScrewdriverCocktailGlass + - id: DrinkShirleyTempleGlass + - id: DrinkSuiDreamGlass + - id: DrinkSingulo + - id: DrinkSoyLatte + - id: DrinkSyndicatebomb + - id: DrinkTequilaSunriseGlass + - id: DrinkThreeMileIslandGlass + - id: DrinkTortugaGlass + - id: DrinkToxinsSpecialGlass + - id: DrinkVampiroGlass + - id: DrinkVodkaMartiniGlass + - id: DrinkVodkaRedBool + - id: DrinkVodkaTonicGlass + - id: DrinkWatermelonJuice + - id: DrinkWatermelonWakeup + - id: DrinkWhiskeyColaGlass + - id: DrinkWhiskeySodaGlass + - id: DrinkWhiteRussianGlass + - id: DrinkWineGlass + - id: XenoBasherGlass + - id: DrinkShakeBlue + - id: DrinkShakeWhite + - id: DrinkTheMartinez + - id: DrinkMoonshineGlass + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: DrinkAcidSpitGlass + - id: DrinkAlliesCocktail + - id: DrinkAmasecGlass + - id: DrinkAtomicBombGlass + - id: DrinkDemonsBlood + - id: DrinkDoctorsDelightGlass + - id: DrinkNeurotoxinGlass + - id: DrinkNuclearColaGlass + - id: DrinkSilencerGlass + - id: DrinkShakeMeat + - id: DrinkShakeRobo + - id: DrinkHoochGlass + - id: DrinkBeepskySmashGlass + - id: DrinkBacchusBlessing offset: 0.0 - #rare - rarePrototypes: - - DrinkAcidSpitGlass - - DrinkAlliesCocktail - - DrinkAmasecGlass - - DrinkAtomicBombGlass - - DrinkDemonsBlood - - DrinkDoctorsDelightGlass - - DrinkNeurotoxinGlass - - DrinkNuclearColaGlass - - DrinkSilencerGlass - - DrinkShakeMeat - - DrinkShakeRobo - - DrinkHoochGlass - - DrinkBeepskySmashGlass - - DrinkBacchusBlessing - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_single.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_single.yml index 7d0f734acc..1a4d8defb8 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_single.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_single.yml @@ -1,96 +1,100 @@ - type: entity + parent: MarkerBase id: RandomFoodBakedSingle name: random baked food spawner suffix: Single Serving - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Food/Baked/pie.rsi - state: plain-slice - - type: RandomSpawner - prototypes: - - FoodBreadPlainSlice - - FoodBreadMeatSlice - - FoodBreadBananaSlice - - FoodBreadCreamcheeseSlice - - FoodBreadMoldySlice - - FoodBreadGarlicSlice - - FoodBreadCornSlice - - FoodBreadSausageSlice - - FoodBreadButteredToast - - FoodBreadJellySlice - - FoodBreadFrenchToast - - FoodBreadTwoSlice - - FoodBreadVolcanicSlice - - FoodBreadTofuSlice - - FoodBreadBaguetteSlice - - FoodCakeBlueberrySlice - - FoodCakePlainSlice - - FoodCakeCarrotSlice - - FoodCakeCheeseSlice - - FoodCakeOrangeSlice - - FoodCakeLimeSlice - - FoodCakeLemonSlice - - FoodCakeChocolateSlice - - FoodCakeAppleSlice - - FoodCakeSlimeSlice - - FoodCakePumpkinSlice - - FoodCakeChristmasSlice - - FoodCakeVanillaSlice - - FoodCakeBirthdaySlice - - FoodCakeBerryDelightSlice - - FoodCakeCottonSlice - - FoodBakedMuffin - - FoodBakedMuffinBerry - - FoodBakedMuffinCherry - - FoodBakedMuffinBluecherry - - FoodBakedMuffinChocolate - - FoodBakedMuffinBanana - - FoodBakedBunHoney - - FoodBakedBunHotX - - FoodBakedBunMeat - - FoodBakedCookie - - FoodBakedCookieOatmeal - - FoodBakedCookieRaisin - - FoodBakedCookieSugar - - FoodBakedGrilledCheeseSandwich - - FoodBakedGrilledCheeseSandwichCotton - - FoodBakedNugget - - FoodBakedPancake - - FoodBakedPancakeBb - - FoodBakedPancakeCc - - FoodBakedWaffle - - FoodBakedWaffleSoy - - FoodBakedWaffleSoylent - - FoodBakedWaffleRoffle - - FoodBakedPretzel - - FoodBakedCannoli - - FoodPieAppleSlice - - FoodPieBaklavaSlice - - FoodPieClafoutisSlice - - FoodPieMeatSlice - - FoodPieCherrySlice - - FoodPieFrostySlice - - FoodTartGrape - - FoodTartCoco - - FoodBakedBrownie - - FoodPieBananaCreamSlice - - FoodTartMimeSlice - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Food/Baked/pie.rsi + state: plain-slice + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector + weight: 0.95 + prob: 0.8 + children: + - id: FoodBreadPlainSlice + - id: FoodBreadMeatSlice + - id: FoodBreadBananaSlice + - id: FoodBreadCreamcheeseSlice + - id: FoodBreadMoldySlice + - id: FoodBreadGarlicSlice + - id: FoodBreadCornSlice + - id: FoodBreadSausageSlice + - id: FoodBreadButteredToast + - id: FoodBreadJellySlice + - id: FoodBreadFrenchToast + - id: FoodBreadTwoSlice + - id: FoodBreadVolcanicSlice + - id: FoodBreadTofuSlice + - id: FoodBreadBaguetteSlice + - id: FoodCakeBlueberrySlice + - id: FoodCakePlainSlice + - id: FoodCakeCarrotSlice + - id: FoodCakeCheeseSlice + - id: FoodCakeOrangeSlice + - id: FoodCakeLimeSlice + - id: FoodCakeLemonSlice + - id: FoodCakeChocolateSlice + - id: FoodCakeAppleSlice + - id: FoodCakeSlimeSlice + - id: FoodCakePumpkinSlice + - id: FoodCakeChristmasSlice + - id: FoodCakeVanillaSlice + - id: FoodCakeBirthdaySlice + - id: FoodCakeBerryDelightSlice + - id: FoodCakeCottonSlice + - id: FoodBakedMuffin + - id: FoodBakedMuffinBerry + - id: FoodBakedMuffinCherry + - id: FoodBakedMuffinBluecherry + - id: FoodBakedMuffinChocolate + - id: FoodBakedMuffinBanana + - id: FoodBakedBunHoney + - id: FoodBakedBunHotX + - id: FoodBakedBunMeat + - id: FoodBakedCookie + - id: FoodBakedCookieOatmeal + - id: FoodBakedCookieRaisin + - id: FoodBakedCookieSugar + - id: FoodBakedGrilledCheeseSandwich + - id: FoodBakedGrilledCheeseSandwichCotton + - id: FoodBakedNugget + - id: FoodBakedPancake + - id: FoodBakedPancakeBb + - id: FoodBakedPancakeCc + - id: FoodBakedWaffle + - id: FoodBakedWaffleSoy + - id: FoodBakedWaffleSoylent + - id: FoodBakedWaffleRoffle + - id: FoodBakedPretzel + - id: FoodBakedCannoli + - id: FoodPieAppleSlice + - id: FoodPieBaklavaSlice + - id: FoodPieClafoutisSlice + - id: FoodPieMeatSlice + - id: FoodPieCherrySlice + - id: FoodPieFrostySlice + - id: FoodTartGrape + - id: FoodTartCoco + - id: FoodBakedBrownie + - id: FoodPieBananaCreamSlice + - id: FoodTartMimeSlice + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: FoodBreadMeatSpiderSlice + - id: FoodBreadMimanaSlice + - id: FoodCakeBrainSlice + - id: FoodCakeClownSlice + - id: FoodCakeSpacemanSlice + - id: FoodTartGapple + - id: FoodBreadMeatXenoSlice + - id: FoodPieXenoSlice + - id: FoodBakedCannabisBrownie offset: 0.0 - #rare - rarePrototypes: - - FoodBreadMeatSpiderSlice - - FoodBreadMimanaSlice - - FoodCakeBrainSlice - - FoodCakeClownSlice - - FoodCakeSpacemanSlice - - FoodTartGapple - - FoodBreadMeatXenoSlice - - FoodPieXenoSlice - - FoodBakedCannabisBrownie - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_whole.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_whole.yml index 7683f19884..f7c553b022 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_whole.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_baked_whole.yml @@ -1,65 +1,68 @@ - type: entity + parent: MarkerBase id: RandomFoodBakedWhole name: random baked food spawner suffix: Whole - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Food/Baked/pie.rsi - state: plain - - type: RandomSpawner - #small item - prototypes: - - FoodBreadVolcanic - - FoodBreadPlain - - FoodBreadMeat - - FoodBreadCorn - - FoodBreadSausage - - FoodBreadBanana - - FoodBreadTofu - - FoodBreadCreamcheese - - FoodBreadBaguette - - FoodCakeBlueberry - - FoodCakePlain - - FoodCakeCheese - - FoodCakeCarrot - - FoodCakeOrange - - FoodCakeLime - - FoodCakeLemon - - FoodCakeChocolate - - FoodCakeApple - - FoodCakeSlime - - FoodCakePumpkin - - FoodCakeChristmas - - FoodCakeBirthday - - FoodCakeVanilla - - FoodCakeBerryDelight - - FoodCakeCotton - - FoodPieApple - - FoodPieBaklava - - FoodPieBananaCream - - FoodPieClafoutis - - FoodPieCherry - - FoodPieMeat - - FoodPieFrosty - - FoodBakedBrownieBatch - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Food/Baked/pie.rsi + state: plain + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector #small item + weight: 0.95 + prob: 0.8 + children: + - id: FoodBreadVolcanic + - id: FoodBreadPlain + - id: FoodBreadMeat + - id: FoodBreadCorn + - id: FoodBreadSausage + - id: FoodBreadBanana + - id: FoodBreadTofu + - id: FoodBreadCreamcheese + - id: FoodBreadBaguette + - id: FoodCakeBlueberry + - id: FoodCakePlain + - id: FoodCakeCheese + - id: FoodCakeCarrot + - id: FoodCakeOrange + - id: FoodCakeLime + - id: FoodCakeLemon + - id: FoodCakeChocolate + - id: FoodCakeApple + - id: FoodCakeSlime + - id: FoodCakePumpkin + - id: FoodCakeChristmas + - id: FoodCakeBirthday + - id: FoodCakeVanilla + - id: FoodCakeBerryDelight + - id: FoodCakeCotton + - id: FoodPieApple + - id: FoodPieBaklava + - id: FoodPieBananaCream + - id: FoodPieClafoutis + - id: FoodPieCherry + - id: FoodPieMeat + - id: FoodPieFrosty + - id: FoodBakedBrownieBatch + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: FoodBreadMeatSpider + - id: FoodBreadMimana + - id: FoodCakeBrain + - id: FoodCakeClown + - id: FoodCakeSpaceman + - id: FoodPieXeno + - id: FoodPieAmanita + - id: FoodPiePlump + - id: FoodBreadMeatXeno + - id: FoodPieXeno + - id: FoodBakedCannabisBrownieBatch offset: 0.0 - #rare - rarePrototypes: - - FoodBreadMeatSpider - - FoodBreadMimana - - FoodCakeBrain - - FoodCakeClown - - FoodCakeSpaceman - - FoodPieXeno - - FoodPieAmanita - - FoodPiePlump - - FoodBreadMeatXeno - - FoodPieXeno - - FoodBakedCannabisBrownieBatch - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_breakfast.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_breakfast.yml index e4213ad31f..27256c673e 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_breakfast.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_breakfast.yml @@ -1,18 +1,19 @@ - type: entity + parent: MarkerBase id: RandomFoodBreakfast name: random food spawner - suffix: Meal - parent: MarkerBase + suffix: Breakfast placement: mode: AlignTileAny components: - type: Sprite layers: - - sprite: Objects/Consumable/Food/breakfast.rsi - state: fullamerican - - type: RandomSpawner - prototypes: - - FoodBreakfastAmerican - - FoodBreakfastEnglish - chance: 0.8 + - sprite: Objects/Consumable/Food/breakfast.rsi + state: fullamerican + - type: EntityTableSpawner + table: !type:GroupSelector + prob: 0.8 + children: + - id: FoodBreakfastAmerican + - id: FoodBreakfastEnglish offset: 0.0 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_meal.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_meal.yml index 4c64bdd786..14aea5b79c 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_meal.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_meal.yml @@ -1,97 +1,101 @@ - type: entity + parent: MarkerBase id: RandomFoodMeal name: random food spawner suffix: Meal - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Food/meals.rsi - state: cornedbeef - - type: RandomSpawner - prototypes: - - FoodMealPotatoLoaded - - FoodMealFries - - FoodMealFriesCheesy - - FoodMealFriesCarrot - - FoodMealNachos - - FoodMealNachosCheesy - - FoodMealNachosCuban - - FoodMealEggplantParm - - FoodMealPotatoYaki - - FoodMealCubancarp - - FoodMealCornedbeef - - FoodMealPigblanket - - FoodMealRibs - - FoodMealEggsbenedict - - FoodMealOmelette - - FoodMealFriedegg - - FoodMealQueso - - FoodMealSashimi - - FoodMealEnchiladas - - FoodNoodlesChowmein - - FoodNoodlesSpesslaw - - FoodNoodlesMeatball - - FoodNoodlesBoiled - - FoodNoodles - - FoodNoodlesButter - - FoodMealHappyHonkClown - - FoodSoupPea - - FoodSaladHerb - - FoodSaladValid - - FoodSaladFruit - - FoodSaladJungle - - FoodSaladCitrus - - FoodSaladCaesar - - FoodSaladColeslaw - - FoodSaladKimchi - - FoodSaladWatermelonFruitBowl - - FoodRiceBoiled - - FoodRiceEgg - - FoodRicePork - - FoodRicePudding - - FoodRiceGumbo - - FoodOatmeal - - FoodSoupMeatball - - FoodSoupVegetable - - FoodSoupNettle - - FoodSoupChiliHot - - FoodSoupChiliCold - - FoodSoupTomato - - FoodSoupMiso - - FoodSoupMushroom - - FoodSoupBeet - - FoodSoupBeetRed - - FoodSoupPotato - - FoodSoupOnion - - FoodSoupBisque - - FoodSoupBungo - - FoodMealCornInButter - - FoodSoupStew - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Food/meals.rsi + state: cornedbeef + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector + weight: 0.95 + prob: 0.8 + children: + - id: FoodMealPotatoLoaded + - id: FoodMealFries + - id: FoodMealFriesCheesy + - id: FoodMealFriesCarrot + - id: FoodMealNachos + - id: FoodMealNachosCheesy + - id: FoodMealNachosCuban + - id: FoodMealEggplantParm + - id: FoodMealPotatoYaki + - id: FoodMealCubancarp + - id: FoodMealCornedbeef + - id: FoodMealPigblanket + - id: FoodMealRibs + - id: FoodMealEggsbenedict + - id: FoodMealOmelette + - id: FoodMealFriedegg + - id: FoodMealQueso + - id: FoodMealSashimi + - id: FoodMealEnchiladas + - id: FoodNoodlesChowmein + - id: FoodNoodlesSpesslaw + - id: FoodNoodlesMeatball + - id: FoodNoodlesBoiled + - id: FoodNoodles + - id: FoodNoodlesButter + - id: FoodMealHappyHonkClown + - id: FoodSoupPea + - id: FoodSaladHerb + - id: FoodSaladValid + - id: FoodSaladFruit + - id: FoodSaladJungle + - id: FoodSaladCitrus + - id: FoodSaladCaesar + - id: FoodSaladColeslaw + - id: FoodSaladKimchi + - id: FoodSaladWatermelonFruitBowl + - id: FoodRiceBoiled + - id: FoodRiceEgg + - id: FoodRicePork + - id: FoodRicePudding + - id: FoodRiceGumbo + - id: FoodOatmeal + - id: FoodSoupMeatball + - id: FoodSoupVegetable + - id: FoodSoupNettle + - id: FoodSoupChiliHot + - id: FoodSoupChiliCold + - id: FoodSoupTomato + - id: FoodSoupMiso + - id: FoodSoupMushroom + - id: FoodSoupBeet + - id: FoodSoupBeetRed + - id: FoodSoupPotato + - id: FoodSoupOnion + - id: FoodSoupBisque + - id: FoodSoupBungo + - id: FoodMealCornInButter + - id: FoodSoupStew + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: FoodMealMint + - id: FoodMealBearsteak + - id: FoodMealMilkape + - id: DisgustingSweptSoup + - id: FoodMealMemoryleek + - id: FoodSaladAesir + - id: FoodSaladEden + - id: FoodJellyDuff + - id: FoodJellyAmanita + - id: FoodSoupSlime + - id: FoodSoupTomatoBlood + - id: FoodSoupWingFangChu + - id: FoodSoupClown + - id: FoodSoupMystery + - id: FoodSoupChiliClown + - id: FoodSoupMonkey + - id: FoodSoupEyeball + - id: FoodSoupElectron + - id: FoodNoodlesCopy offset: 0.0 - #rare - rarePrototypes: - - FoodMealMint - - FoodMealBearsteak - - FoodMealMilkape - - DisgustingSweptSoup - - FoodMealMemoryleek - - FoodSaladAesir - - FoodSaladEden - - FoodJellyDuff - - FoodJellyAmanita - - FoodSoupSlime - - FoodSoupTomatoBlood - - FoodSoupWingFangChu - - FoodSoupClown - - FoodSoupMystery - - FoodSoupChiliClown - - FoodSoupMonkey - - FoodSoupEyeball - - FoodSoupElectron - - FoodNoodlesCopy - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml index fda7b85b75..ea44f18599 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_single.yml @@ -1,71 +1,75 @@ - type: entity + parent: MarkerBase id: RandomFoodSingle name: random food spawner suffix: Single Serving - parent: MarkerBase placement: mode: AlignTileAny components: - type: Sprite layers: - - state: red - - sprite: Objects/Consumable/Food/burger.rsi - state: plain - - type: RandomSpawner - prototypes: - - FoodBagel - - FoodBagelPoppy - - FoodBurgerJelly - - FoodBurgerCarp - - FoodBurgerTofu - - FoodBurgerXeno - - FoodBurgerBig - - FoodBurgerFive - - FoodBurgerRat - - FoodBurgerBacon - - FoodBurgerEmpowered - - FoodBurgerCrab - - FoodBurgerHuman - - FoodBurgerSoy - - FoodBurgerMcrib - - FoodBurgerMcguffin - - FoodBurgerChicken - - FoodBurgerDuck - - FoodBurgerCheese - - FoodNoodlesBoiled - - FoodNoodles - - FoodNoodlesCopy - - FoodNoodlesButter - - FoodPizzaMargheritaSlice - - FoodPizzaMeatSlice - - FoodPizzaMushroomSlice - - FoodPizzaVegetableSlice - - FoodPizzaDonkpocketSlice - - FoodPizzaDankSlice - - FoodPizzaSassysageSlice - - FoodPizzaPineappleSlice - - FoodPizzaMoldySlice - - FoodBakedDumplings - - FoodBakedChevreChaud - - FoodBakedNugget - - FoodTacoShell - chance: 0.8 + - state: red + - sprite: Objects/Consumable/Food/burger.rsi + state: plain + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - !type:GroupSelector + weight: 0.95 + prob: 0.8 + children: + - id: FoodBagel + - id: FoodBagelPoppy + - id: FoodBurgerJelly + - id: FoodBurgerCarp + - id: FoodBurgerTofu + - id: FoodBurgerXeno + - id: FoodBurgerBig + - id: FoodBurgerFive + - id: FoodBurgerRat + - id: FoodBurgerBacon + - id: FoodBurgerEmpowered + - id: FoodBurgerCrab + - id: FoodBurgerHuman + - id: FoodBurgerSoy + - id: FoodBurgerMcrib + - id: FoodBurgerMcguffin + - id: FoodBurgerChicken + - id: FoodBurgerDuck + - id: FoodBurgerCheese + - id: FoodNoodlesBoiled + - id: FoodNoodles + - id: FoodNoodlesCopy + - id: FoodNoodlesButter + - id: FoodPizzaMargheritaSlice + - id: FoodPizzaMeatSlice + - id: FoodPizzaMushroomSlice + - id: FoodPizzaVegetableSlice + - id: FoodPizzaDonkpocketSlice + - id: FoodPizzaDankSlice + - id: FoodPizzaSassysageSlice + - id: FoodPizzaPineappleSlice + - id: FoodPizzaMoldySlice + - id: FoodBakedDumplings + - id: FoodBakedChevreChaud + - id: FoodBakedNugget + - id: FoodTacoShell + - !type:GroupSelector #rare + weight: 0.05 + children: + - id: FoodBurgerAppendix + - id: FoodBurgerRobot + - id: FoodBurgerBaseball + - id: FoodBurgerBear + - id: FoodBurgerCat + - id: FoodBurgerClown + - id: FoodBurgerMime + - id: FoodBurgerBrain + - id: FoodBurgerGhost + - id: FoodBurgerSpell + - id: FoodBurgerSuper + - id: FoodBurgerCrazy + - id: FoodPizzaArnoldSlice + - id: FoodPizzaUraniumSlice + - id: FoodPizzaWorldpeasSlice offset: 0.0 - #rare - rarePrototypes: - - FoodBurgerAppendix - - FoodBurgerRobot - - FoodBurgerBaseball - - FoodBurgerBear - - FoodBurgerCat - - FoodBurgerClown - - FoodBurgerMime - - FoodBurgerBrain - - FoodBurgerGhost - - FoodBurgerSpell - - FoodBurgerSuper - - FoodBurgerCrazy - - FoodPizzaArnoldSlice - - FoodPizzaUraniumSlice - - FoodPizzaWorldpeasSlice - rareChance: 0.05 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/vending.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/vending.yml index 0b530c68b1..98dfc20c13 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/vending.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/vending.yml @@ -1,38 +1,38 @@ - type: entity + parent: MarkerBase id: RandomVending name: random vending machine spawner suffix: Any - parent: MarkerBase components: - type: Sprite layers: - state: red - sprite: Structures/Machines/VendingMachines/random.rsi state: any - - type: RandomSpawner - prototypes: - - VendingMachineChang - - VendingMachineCigs - - VendingMachineCoffee - - VendingMachineCola #Robust Sofdrinks - - VendingMachineColaBlack #Robust Sofdrinks [Black] - - VendingMachineColaRed #Space Cola - - VendingMachineDiscount - - VendingMachineDonut - - VendingMachineDrGibb - - VendingMachinePwrGame - - VendingMachineShamblersJuice - - VendingMachineSmite - - VendingMachineSnack - - VendingMachineSnackBlue - - VendingMachineSnackGreen - - VendingMachineSnackOrange - - VendingMachineSnackTeal - - VendingMachineSoda #Robust Sofdrinks [Soda] - - VendingMachineSovietSoda #Boda - - VendingMachineSpaceUp - - VendingMachineStarkist - chance: 1 + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - id: VendingMachineChang + - id: VendingMachineCigs + - id: VendingMachineCoffee + - id: VendingMachineCola #Robust Sofdrinks + - id: VendingMachineColaBlack #Robust Sofdrinks [Black] + - id: VendingMachineColaRed #Space Cola + - id: VendingMachineDiscount + - id: VendingMachineDonut + - id: VendingMachineDrGibb + - id: VendingMachinePwrGame + - id: VendingMachineShamblersJuice + - id: VendingMachineSmite + - id: VendingMachineSnack + - id: VendingMachineSnackBlue + - id: VendingMachineSnackGreen + - id: VendingMachineSnackOrange + - id: VendingMachineSnackTeal + - id: VendingMachineSoda #Robust Sofdrinks [Soda] + - id: VendingMachineSovietSoda #Boda + - id: VendingMachineSpaceUp + - id: VendingMachineStarkist - type: entityTable @@ -40,19 +40,17 @@ table: !type:GroupSelector children: - id: VendingMachineClothing - weight: 40 + weight: 4 - id: VendingMachineWinter - weight: 40 + weight: 4 - id: VendingMachinePride - weight: 10 - id: VendingMachineTheater - weight: 10 - type: entity + parent: MarkerBase id: RandomVendingClothing name: random vending machine spawner suffix: Clothing - parent: MarkerBase components: - type: Sprite layers: diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingdrinks.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingdrinks.yml index 1c90664b3d..09c4ce8761 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingdrinks.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingdrinks.yml @@ -1,26 +1,26 @@ - type: entity + parent: MarkerBase id: RandomVendingDrinks name: random vending machine spawner suffix: Drinks - parent: MarkerBase components: - type: Sprite layers: - - state: red - - sprite: Structures/Machines/VendingMachines/random.rsi - state: drink - - type: RandomSpawner - prototypes: - - VendingMachineCoffee - - VendingMachineCola #Robust Sofdrinks - - VendingMachineColaBlack #Robust Sofdrinks [Black] - - VendingMachineColaRed #Space Cola - - VendingMachineDrGibb - - VendingMachinePwrGame - - VendingMachineShamblersJuice - - VendingMachineSmite - - VendingMachineSoda #Robust Sofdrinks [Soda] - - VendingMachineSovietSoda #Boda - - VendingMachineSpaceUp - - VendingMachineStarkist - chance: 1 + - state: red + - sprite: Structures/Machines/VendingMachines/random.rsi + state: drink + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - id: VendingMachineCoffee + - id: VendingMachineCola #Robust Sofdrinks + - id: VendingMachineColaBlack #Robust Sofdrinks [Black] + - id: VendingMachineColaRed #Space Cola + - id: VendingMachineDrGibb + - id: VendingMachinePwrGame + - id: VendingMachineShamblersJuice + - id: VendingMachineSmite + - id: VendingMachineSoda #Robust Sofdrinks [Soda] + - id: VendingMachineSovietSoda #Boda + - id: VendingMachineSpaceUp + - id: VendingMachineStarkist diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingsnacks.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingsnacks.yml index b634d50cc6..e919cf192d 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingsnacks.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/vendingsnacks.yml @@ -1,22 +1,22 @@ - type: entity + parent: MarkerBase id: RandomVendingSnacks name: random vending machine spawner suffix: Snacks - parent: MarkerBase components: - type: Sprite layers: - - state: red - - sprite: Structures/Machines/VendingMachines/random.rsi - state: snack - - type: RandomSpawner - prototypes: - - VendingMachineDiscount - - VendingMachineSnack - - VendingMachineSnackBlue - - VendingMachineSnackGreen - - VendingMachineSnackOrange - - VendingMachineSnackTeal - - VendingMachineChang - - VendingMachineDonut - chance: 1 + - state: red + - sprite: Structures/Machines/VendingMachines/random.rsi + state: snack + - type: EntityTableSpawner + table: !type:GroupSelector + children: + - id: VendingMachineDiscount + - id: VendingMachineSnack + - id: VendingMachineSnackBlue + - id: VendingMachineSnackGreen + - id: VendingMachineSnackOrange + - id: VendingMachineSnackTeal + - id: VendingMachineChang + - id: VendingMachineDonut From ee9ba28e9ec83080e5d41dbbaaf20ac9dd3fabd4 Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:27:53 -0400 Subject: [PATCH 141/247] Fix fireball sound for client (#43535) Fix fireball sound --- Content.Shared/Magic/SharedMagicSystem.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 62d3dbdbd4..4018258ae6 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -270,11 +270,14 @@ public abstract class SharedMagicSystem : EntitySystem #region Projectile Spells private void OnProjectileSpell(ProjectileSpellEvent ev) { - if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || !_net.IsServer) + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer)) return; ev.Handled = true; + if (!_net.IsServer) + return; // client returns handled for predicted audio + var xform = Transform(ev.Performer); var fromCoords = xform.Coordinates; var toCoords = ev.Target; From b49e60df5ef5a2732e3b8348f069013e8d270c61 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 9 Apr 2026 21:43:48 +0000 Subject: [PATCH 142/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ef8f1b6bd6..03e8db56d3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,15 +1,4 @@ Entries: -- 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. @@ -4030,3 +4019,10 @@ 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 From 7adf035b99c796f1cadf6e97d4abf528cc196b0d Mon Sep 17 00:00:00 2001 From: AndrewFenriz <78079974+AndrewFenriz@users.noreply.github.com> Date: Fri, 10 Apr 2026 08:57:45 +0300 Subject: [PATCH 143/247] New janibelt/janicart ItemMapper and trash bag equipped sprites (#43492) just for fun --- .../Catalog/Fills/Boxes/general.yml | 1 + .../Prototypes/Entities/Clothing/Belt/job.yml | 44 +- .../Objects/Specific/Janitorial/trashbag.yml | 8 + .../Entities/Objects/Tools/light_replacer.yml | 3 + .../Weapons/Throwable/canister_grenades.yml | 3 + .../Structures/Specific/Janitor/janicart.yml | 462 +++++++++--------- Resources/Prototypes/tags.yml | 9 + .../Belt/belt_overlay.rsi/cleaner_grenade.png | Bin 0 -> 260 bytes .../Belt/belt_overlay.rsi/golden_plunger.png | Bin 0 -> 195 bytes .../Belt/belt_overlay.rsi/holosign.png | Bin 0 -> 296 bytes .../Belt/belt_overlay.rsi/light_replacer.png | Bin 0 -> 343 bytes .../Clothing/Belt/belt_overlay.rsi/meta.json | 40 +- .../Belt/belt_overlay.rsi/plunger.png | Bin 0 -> 206 bytes .../Clothing/Belt/belt_overlay.rsi/soap.png | Bin 0 -> 261 bytes .../Belt/belt_overlay.rsi/trashbag.png | Bin 0 -> 202 bytes .../Belt/belt_overlay.rsi/trashbag_blue.png | Bin 0 -> 180 bytes .../Belt/belt_overlay.rsi/wetfloorsign.png | Bin 0 -> 197 bytes .../Belt/belt_overlay.rsi/wire_brush.png | Bin 0 -> 204 bytes .../janitorial_cart.rsi/cart_garbage_blue.png | Bin 0 -> 326 bytes .../Janitorial/janitorial_cart.rsi/meta.json | 6 +- .../trashbag.rsi/blue-equipped-BELT.png | Bin 0 -> 339 bytes .../Janitorial/trashbag.rsi/equipped-BELT.png | Bin 0 -> 294 bytes .../Janitorial/trashbag.rsi/meta.json | 8 + 23 files changed, 341 insertions(+), 243 deletions(-) create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/cleaner_grenade.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/golden_plunger.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/holosign.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/light_replacer.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/plunger.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/soap.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/trashbag.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/trashbag_blue.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/wetfloorsign.png create mode 100644 Resources/Textures/Clothing/Belt/belt_overlay.rsi/wire_brush.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/cart_garbage_blue.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/blue-equipped-BELT.png create mode 100644 Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/equipped-BELT.png diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml index 326afa6d4a..519764ce25 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/Entities/Clothing/Belt/job.yml b/Resources/Prototypes/Entities/Clothing/Belt/job.yml index a40740f46a..a551ce4418 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/job.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/job.yml @@ -135,11 +135,11 @@ - WetFloorSign - HolosignProjector - Plunger + - TrashBagBlue - GoldenPlunger - WireBrush - components: - LightReplacer - - SmokeOnTrigger + - CleanerGrenade - type: ItemMapper sprite: *BeltOverlay mapLayers: @@ -151,10 +151,46 @@ whitelist: tags: - Spray - wrench: + light_replacer: whitelist: tags: - - Wrench + - LightReplacer + trashbag: + whitelist: + tags: + - TrashBag + trashbag_blue: + whitelist: + tags: + - TrashBagBlue + wetfloorsign: + whitelist: + tags: + - WetFloorSign + soap: + whitelist: + tags: + - Soap + wire_brush: + whitelist: + tags: + - WireBrush + holosign: + whitelist: + tags: + - HolosignProjector + plunger: + whitelist: + tags: + - Plunger + golden_plunger: + whitelist: + tags: + - GoldenPlunger + cleaner_grenade: + whitelist: + tags: + - CleanerGrenade - type: Appearance - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/trashbag.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/trashbag.yml index 95e8fcd372..43a9ac228f 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/trashbag.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/trashbag.yml @@ -34,6 +34,7 @@ - type: Clothing slots: [belt] sprite: Objects/Specific/Janitorial/trashbag.rsi + equippedState: equipped-BELT - type: Item size: Normal @@ -50,6 +51,13 @@ heldPrefix: blue - type: StorageFillVisualizer fillBaseName: blue-icon + - type: Clothing + slots: [belt] + sprite: Objects/Specific/Janitorial/trashbag.rsi + equippedState: blue-equipped-BELT + - type: Tag + tags: + - TrashBagBlue - type: entity name: spell of all-consuming cleanliness diff --git a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml index 34dcd66b71..c7cd0efe7b 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml @@ -20,6 +20,9 @@ - type: ContainerContainer containers: light_replacer_storage: !type:Container + - type: Tag + tags: + - LightReplacer - type: entity parent: LightReplacer diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/canister_grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/canister_grenades.yml index f65869d9dd..3b4bbdf036 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/canister_grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/canister_grenades.yml @@ -41,6 +41,9 @@ reagents: - ReagentId: SpaceCleaner Quantity: 30 + - type: Tag + tags: + - CleanerGrenade - type: entity parent: SmokeGrenade diff --git a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml index 038ac70d82..4311cd389e 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Janitor/janicart.yml @@ -122,18 +122,18 @@ parent: MopBucket suffix: full components: - - type: Sprite - layers: - - state: mopbucket - - state: mopbucket_water-3 - map: [ "enum.SolutionContainerLayers.Fill" ] - - type: SolutionContainerManager - solutions: - bucket: - maxVol: 600 - reagents: - - ReagentId: Water - Quantity: 600 + - type: Sprite + layers: + - state: mopbucket + - state: mopbucket_water-3 + map: [ "enum.SolutionContainerLayers.Fill" ] + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 600 + reagents: + - ReagentId: Water + Quantity: 600 - type: entity parent: BaseWrappedCube @@ -154,224 +154,220 @@ parent: [BaseStructureDynamic, StructureWheeled] description: This is the alpha and omega of sanitation. components: - - type: Sprite - noRot: true - sprite: Objects/Specific/Janitorial/janitorial_cart.rsi - layers: - - state: cart - - state: cart_water-1 - map: ["enum.SolutionContainerLayers.Fill"] - visible: false - - type: Rotatable - - type: InteractionOutline - # Removing storage until OnInteractUsing logic resolved - #- type: Storage - # popup: false - # capacity: 80 - # blacklist: # there is exclusive item slots for that - # tags: - # - Mop - # - TrashBag - # - Bucket - - type: ItemSlots - slots: - mop_slot: - name: janitorial-trolley-slot-component-slot-name-mop - whitelist: - tags: - - Mop - insertOnInteract: false # or it conflicts with bucket logic - priority: 9 # Higher than bucket slot - plunger_slot: - name: janitorial-trolley-slot-component-slot-name-plunger - whitelist: - tags: - - Plunger - - GoldenPlunger - priority: 8 - wetfloorsign_slot4: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot3: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot2: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - wetfloorsign_slot1: - name: janitorial-trolley-slot-component-slot-name-sign - whitelist: - tags: - - WetFloorSign - priority: 7 - lightreplacer_slot: - name: janitorial-trolley-slot-component-slot-name-lightreplacer - whitelist: - components: - - LightReplacer - priority: 6 - spraybottle_slot: - name: janitorial-trolley-slot-component-slot-name-spray - whitelist: - tags: - - Spray - insertOnInteract: false # or it conflicts with bucket logic - priority: 5 # Higher than bucket slot - bucket_slot: - name: janitorial-trolley-slot-component-slot-name-bucket - whitelist: - tags: - - Bucket - insertOnInteract: false # or it also conflicts with bucket logic - priority: 4 # Higher than trash bag slot - trashbag_slot: - name: janitorial-trolley-slot-component-slot-name-trashbag - whitelist: - tags: - - TrashBag - priority: 3 # Higher than drinking priority - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.3 - density: 250 - layer: - - MobLayer - mask: - - MobMask - - type: Spillable - solution: bucket - spillDelay: 3.0 - spillWhenThrown: false - - type: SolutionContainerManager - solutions: - bucket: - maxVol: 800 - reagents: - - ReagentId: Water - Quantity: 600 # 3 quarters full at roundstart to make it more appealing - - type: DrainableSolution - solution: bucket - - type: RefillableSolution - solution: bucket - - type: ExaminableSolution - solution: bucket - - type: Damageable - damageContainer: Inorganic - damageModifierSet: Metallic - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 400 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 200 - behaviors: - - !type:EmptyAllContainersBehaviour - - !type:DoActsBehavior - acts: ["Destruction"] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak - - type: ItemMapper - mapLayers: - cart_plunger: - whitelist: - tags: - - Plunger - cart_goldenplunger: - whitelist: - tags: - - GoldenPlunger - cart_mop: - whitelist: - tags: - - MopBasic - cart_advmop: - whitelist: - tags: - - MopAdv - cart_garbage: - whitelist: - tags: - - TrashBag - cart_replacer: - whitelist: - components: - - LightReplacer - cart_spray: - whitelist: - tags: - - Spray - cart_sign1: # this is like stack of floor signs - minCount: 1 - whitelist: - tags: - - WetFloorSign - cart_sign2: - minCount: 2 - whitelist: - tags: - - WetFloorSign - cart_sign3: - minCount: 3 - whitelist: - tags: - - WetFloorSign - cart_sign4: - minCount: 4 - whitelist: - tags: - - WetFloorSign - cart_bucket: - whitelist: - tags: - - Bucket - sprite: Objects/Specific/Janitorial/janitorial_cart.rsi - - type: Appearance - - type: SolutionContainerVisuals - maxFillLevels: 3 - fillBaseName: cart_water- - - type: UserInterface - interfaces: - enum.StorageUiKey.Key: - type: StorageBoundUserInterface - - type: Edible - edible: Drink - solution: bucket - destroyOnEmpty: false - utensil: Spoon - - type: ContainerContainer - containers: - storagebase: !type:Container - ents: [] - mop_slot: !type:ContainerSlot {} - trashbag_slot: !type:ContainerSlot {} - bucket_slot: !type:ContainerSlot {} - plunger_slot: !type:ContainerSlot {} - goldenplunger_slot: !type:ContainerSlot {} - wetfloorsign_slot4: !type:ContainerSlot {} - wetfloorsign_slot3: !type:ContainerSlot {} - wetfloorsign_slot2: !type:ContainerSlot {} - wetfloorsign_slot1: !type:ContainerSlot {} - lightreplacer_slot: !type:ContainerSlot {} - spraybottle_slot: !type:ContainerSlot {} - - type: GuideHelp - guides: - - Janitorial - - type: DnaSubstanceTrace + - type: Sprite + noRot: true + sprite: Objects/Specific/Janitorial/janitorial_cart.rsi + layers: + - state: cart + - state: cart_water-1 + map: ["enum.SolutionContainerLayers.Fill"] + visible: false + - type: Rotatable + - type: InteractionOutline + - type: ItemSlots + slots: + mop_slot: + name: janitorial-trolley-slot-component-slot-name-mop + whitelist: + tags: + - Mop + insertOnInteract: false # or it conflicts with bucket logic + priority: 9 # Higher than bucket slot + plunger_slot: + name: janitorial-trolley-slot-component-slot-name-plunger + whitelist: + tags: + - Plunger + - GoldenPlunger + priority: 8 + wetfloorsign_slot4: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot3: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot2: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + wetfloorsign_slot1: + name: janitorial-trolley-slot-component-slot-name-sign + whitelist: + tags: + - WetFloorSign + priority: 7 + lightreplacer_slot: + name: janitorial-trolley-slot-component-slot-name-lightreplacer + whitelist: + components: + - LightReplacer + priority: 6 + spraybottle_slot: + name: janitorial-trolley-slot-component-slot-name-spray + whitelist: + tags: + - Spray + insertOnInteract: false # or it conflicts with bucket logic + priority: 5 # Higher than bucket slot + bucket_slot: + name: janitorial-trolley-slot-component-slot-name-bucket + whitelist: + tags: + - Bucket + insertOnInteract: false # or it also conflicts with bucket logic + priority: 4 # Higher than trash bag slot + trashbag_slot: + name: janitorial-trolley-slot-component-slot-name-trashbag + whitelist: + tags: + - TrashBag + - TrashBagBlue + priority: 3 # Higher than drinking priority + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.3 + density: 250 + layer: + - MobLayer + mask: + - MobMask + - type: Spillable + solution: bucket + spillDelay: 3.0 + spillWhenThrown: false + - type: SolutionContainerManager + solutions: + bucket: + maxVol: 800 + reagents: + - ReagentId: Water + Quantity: 600 # 3 quarters full at roundstart to make it more appealing + - type: DrainableSolution + solution: bucket + - type: RefillableSolution + solution: bucket + - type: ExaminableSolution + solution: bucket + - type: Damageable + damageContainer: Inorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 400 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - type: ItemMapper + mapLayers: + cart_plunger: + whitelist: + tags: + - Plunger + cart_goldenplunger: + whitelist: + tags: + - GoldenPlunger + cart_mop: + whitelist: + tags: + - MopBasic + cart_advmop: + whitelist: + tags: + - MopAdv + cart_garbage: + whitelist: + tags: + - TrashBag + cart_garbage_blue: + whitelist: + tags: + - TrashBagBlue + cart_replacer: + whitelist: + components: + - LightReplacer + cart_spray: + whitelist: + tags: + - Spray + cart_sign1: # this is like stack of floor signs + minCount: 1 + whitelist: + tags: + - WetFloorSign + cart_sign2: + minCount: 2 + whitelist: + tags: + - WetFloorSign + cart_sign3: + minCount: 3 + whitelist: + tags: + - WetFloorSign + cart_sign4: + minCount: 4 + whitelist: + tags: + - WetFloorSign + cart_bucket: + whitelist: + tags: + - Bucket + sprite: Objects/Specific/Janitorial/janitorial_cart.rsi + - type: Appearance + - type: SolutionContainerVisuals + maxFillLevels: 3 + fillBaseName: cart_water- + - type: UserInterface + interfaces: + enum.StorageUiKey.Key: + type: StorageBoundUserInterface + - type: Edible + edible: Drink + solution: bucket + destroyOnEmpty: false + utensil: Spoon + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] + mop_slot: !type:ContainerSlot {} + trashbag_slot: !type:ContainerSlot {} + bucket_slot: !type:ContainerSlot {} + plunger_slot: !type:ContainerSlot {} + goldenplunger_slot: !type:ContainerSlot {} + wetfloorsign_slot4: !type:ContainerSlot {} + wetfloorsign_slot3: !type:ContainerSlot {} + wetfloorsign_slot2: !type:ContainerSlot {} + wetfloorsign_slot1: !type:ContainerSlot {} + lightreplacer_slot: !type:ContainerSlot {} + spraybottle_slot: !type:ContainerSlot {} + - type: GuideHelp + guides: + - Janitorial + - type: DnaSubstanceTrace diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 6883133a29..1814fda380 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 @@ -1446,6 +1452,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/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 0000000000000000000000000000000000000000..4108cb11fd098eab9295cd85342b424a8fec6a5d GIT binary patch literal 260 zcmV+f0sH=mP)Px#zDYzuR9J=W)2|A`P!z}U?_?slDJC(9pur*-oF-x5J6H_jLs_h{;A04bHM_+J z5N&hAKZvufdI!gLKKcI6Io!hmrIb=i2|I2kS&{daWb^UV&zm2j(5SX8*?i=A)La+W zaR4%tAxcsM04#l*Tau8Zsc}Z^2LR&hiRYtLC?hsNwbx}ooHJVts9QGYbpTQLGbg%& zX1wF_c@bxCC`X$3LDgd|ayO->^hyQHy$kc6ZXQg9l3Z9V*$(QjHC6^RS7XQkgi# zv18qoKQHG9y(}p?n(}Y%!{2iIPe0#UQ+Y#%k;8$3NuYt@Kj(?o?6lHJPiHVcNw|5X z*Y;Nx>zYjrlj058dF8KM5eYPGyj%NmU*8QzqjySkmNLm~;-3CY^7?T`pd#J1P0u|Y z^yM9v+-nQ(N!<2!6LZf+$ByX}LQG^T8O8WsvPx$5lKWrR9J=W(?3eYKorOEZxTtVowo?s2q^?}h{1E1Xy?Dq!d6(DfPKgz5>!ZG z5s(}qO_C;?G=Y_8vFsM=s)eJ?e5!#N=KbdJ1`Ok$SvoF_qDTO`uETX*;yB(|XG=$b zX_~02iY&{tJn&1t?~CVo@|+j-gw`?6jyUj^~3IVm9%Zk z<;f#uz6{dO9h#;AG;@ap}@|2ag>Rj$mc^DAlx<<@SvVR>xwVi&gQJbU^jm$fu?Gca)|G&KBY^SQLrM62}3MW0G`m+plnKGHXp5C58Q zYKQf&j)Lusj6G%n<*c%=?ii*w6w55ywpvMp-J?qSf5Y3-r5CnWGq9cA`SvfgTe~DWM4fhOSFB literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0a191ee29286a5aa1dfadc8fffd9e69aee00235b GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}d7dtgArY;~ z2@}QH@e?9;2{GMN#4`dc@`eJzJ(|>=-7w~+lg;JH+g-0+ zPf8pxIc#w6{{K}eIUDx|tx3EydwIn5NwUsc7&MatRO?ANaU v|7zGi6x;A9-!-Y;Qv1N_uac6|rz-8=Q#>6%9u&$4I)%a0)z4*}Q$iB}`t(l8 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3e435fc30454588d6b56ff3cc269e6739b5e84bf GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}ZJsWUArY;~ z2@*MaGlcHvQ>0Gb6(v3 zQy(6@lig*>psw*G>f`?S?k44ofy`glX(f`m<9NRxE?rg z;Qv#GGY1%Ced=9ZU3+?ZjvP5sTU(o#mlqQgBbeM}yZ+hCtDhvNUH~ea(#oK+^s!j| z#(>2;1Arz7l?3?(GyF#faR=M%fU*lbT^vIqTHj7J-EPE@~k?gCSa_wgTbU}=XN(9vpeiUM;sraqikdgwEwR(K@)Y0l!Z$-hZlC;vFE=He%OAW`W3&327qTrcenRgiHkQ?a zmJ9+Zi^FoZvuYPf-mX3PtHDfp8>{^3BTXk+{Of!7S2CUrVht>su-PBzRt8U3KbLh* G2~7Z9@rd~V literal 0 HcmV?d00001 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 eda0e5aa7b..5f3145a7e7 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)", "states": [ { "name": "cart", @@ -15,6 +15,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 0000000000000000000000000000000000000000..0b2ba41f2ff4005e3398bf0d7d145fb7caf7db5d GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zc|BbmLn`LH zy|vMc*-+;A$NQe~5AGb<+JEWp$~?c?cIno2we5ZTnk}qvA3S-`F-Oyuqx`hvLG8k> zlEBaQ2FpJDbx?5r_i%pwroYOJK(m41ho5Ei4}N&bK5|&~IXI($B z>t{;U;;FAyJ)du%6f#rD=i#qP?UzbfYgaN%+ETQ4*R{yQleol19-rA&Y4K-a-R3s6 z>MQO}eaGj0>Bqafu%)z4*}Q$iB}4OyG0 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..cec43f8bd71efd15fa7a0bea1db16c98abec8ce2 GIT binary patch literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|9(cMqhE&XX zd+Q+QAp?fe}k=s$a_%%m@;mA`z_ zVgzaif*&d^0m}+2SJce@t-ty?m*SL{nHxFo#kjwJR(oIjxsIn*yozS!wi`#@%BjYR zpDh)2m{fGGm@(?<$?_FKtUpiaC^Pu6)g=tYhE%A5`V9yq={8F`I#*;cxav z*L^F){tHK(x+`1ve(zCI7j^mO%eS?83{1ORCgcjW*8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/meta.json b/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/meta.json index 6c11b86921..5ae1530e5d 100644 --- a/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Janitorial/trashbag.rsi/meta.json @@ -46,6 +46,14 @@ { "name": "blue-inhand-right", "directions": 4 + }, + { + "name": "equipped-BELT", + "directions": 4 + }, + { + "name": "blue-equipped-BELT", + "directions": 4 } ] } From 2308b134c78479a604bbb89e20ad5eb169283a45 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 10 Apr 2026 06:13:48 +0000 Subject: [PATCH 144/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 03e8db56d3..e0128b3a2d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4026,3 +4019,13 @@ 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 From 6b9ee56ce2ef6ec06d90b533671828a419b534d4 Mon Sep 17 00:00:00 2001 From: Sir Warock <67167466+SirWarock@users.noreply.github.com> Date: Fri, 10 Apr 2026 15:10:22 +0200 Subject: [PATCH 145/247] Predict IngestionBlockerComponent (#43543) * Predict IngestionBlocker * Remove redundancy * undo entity mask --- .../Nutrition/Components/IngestionBlockerComponent.cs | 9 +++++---- .../Nutrition/EntitySystems/IngestionSystem.Blockers.cs | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs b/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs index 931d47838b..65bc5519ed 100644 --- a/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs +++ b/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Nutrition.EntitySystems; +using Robust.Shared.GameStates; namespace Content.Shared.Nutrition.Components; @@ -9,12 +10,12 @@ namespace Content.Shared.Nutrition.Components; /// In the event that more head-wear & mask functionality is added (like identity systems, or raising/lowering of /// masks), then this component might become redundant. /// -[RegisterComponent, Access(typeof(IngestionSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(IngestionSystem))] public sealed partial class IngestionBlockerComponent : Component { /// - /// Is this component currently blocking consumption. + /// Whether this item currently blocks consuming something. /// - [DataField] - public bool Enabled { get; set; } = true; + [DataField, AutoNetworkedField] + public bool Enabled = true; } diff --git a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs index 3e7c9da122..a4d5d62391 100644 --- a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs +++ b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Blockers.cs @@ -43,9 +43,10 @@ public sealed partial class IngestionSystem args.Cancelled = true; } - private void OnBlockerMaskToggled(Entity ent, ref ItemMaskToggledEvent args) + private void OnBlockerMaskToggled(Entity entity, ref ItemMaskToggledEvent args) { - ent.Comp.Enabled = !args.Mask.Comp.IsToggled; + entity.Comp.Enabled = !args.Mask.Comp.IsToggled; + Dirty(entity); } private void OnIngestionBlockerAttempt(Entity entity, ref IngestionAttemptEvent args) From b1f11bbeaefa6798cff4ad5aa4ecc3ab42c57a0c Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 10 Apr 2026 13:26:08 +0000 Subject: [PATCH 146/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e0128b3a2d..ead430d572 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4029,3 +4022,11 @@ 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 From b8133b90ce12c932fede8787b49a0e3067d48004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=81da?= Date: Fri, 10 Apr 2026 11:23:18 -0500 Subject: [PATCH 147/247] Wire brush can scrub away webs and posters (#43537) * posters * cobwebs * poster nerf * testfail --------- Co-authored-by: iaada --- .../Structures/Decoration/cobwebs.yml | 7 +++-- .../Structures/Wallmounts/Signs/posters.yml | 10 ++++++- .../Entities/Structures/spider_web.yml | 5 +++- .../Graphs/structures/posters.yml | 26 +++++++++++++++++++ .../Recipes/Construction/Graphs/web.yml | 15 +++++++++++ 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 Resources/Prototypes/Recipes/Construction/Graphs/structures/posters.yml diff --git a/Resources/Prototypes/Entities/Structures/Decoration/cobwebs.yml b/Resources/Prototypes/Entities/Structures/Decoration/cobwebs.yml index 15897201b2..95aa115310 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/cobwebs.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/cobwebs.yml @@ -28,14 +28,17 @@ behaviors: - !type:DoActsBehavior acts: [ "Destruction" ] + - type: Construction + graph: CobwebsGraph + node: start - type: entity - id: Cobweb2 parent: Cobweb1 + id: Cobweb2 components: - type: Sprite sprite: Structures/Decoration/cobweb.rsi state: cobweb2 - type: Icon sprite: Structures/Decoration/cobweb.rsi - state: cobweb2 \ No newline at end of file + state: cobweb2 diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml index aceb8dc4f8..4c03c3107f 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/posters.yml @@ -1,7 +1,7 @@ - type: entity + abstract: true parent: BaseSign id: PosterBase - abstract: true components: - type: Sprite sprite: Structures/Wallmounts/posters.rsi @@ -34,6 +34,9 @@ min: 1 max: 1 offset: 0 + - type: Construction + graph: PosterGraph + node: start - type: entity parent: BaseSign @@ -45,6 +48,8 @@ drawdepth: WallTops sprite: Structures/Wallmounts/posters.rsi state: poster_broken + - type: Damageable + damageModifierSet: Card - type: Destructible thresholds: - trigger: @@ -56,6 +61,9 @@ path: /Audio/Effects/poster_broken.ogg - !type:DoActsBehavior acts: [ "Destruction" ] + - type: Construction + graph: PosterGraph + node: tornPoster # Contraband - type: entity diff --git a/Resources/Prototypes/Entities/Structures/spider_web.yml b/Resources/Prototypes/Entities/Structures/spider_web.yml index 1049129c7b..1b039be8ea 100644 --- a/Resources/Prototypes/Entities/Structures/spider_web.yml +++ b/Resources/Prototypes/Entities/Structures/spider_web.yml @@ -1,6 +1,6 @@ - type: entity - id: SpiderWebBase abstract: true + id: SpiderWebBase placement: mode: SnapgridCenter snap: @@ -45,6 +45,9 @@ additionalKeys: - walls base: web_ + - type: Construction + graph: CobwebsGraph + node: start - type: entity id: SpiderWeb diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/structures/posters.yml b/Resources/Prototypes/Recipes/Construction/Graphs/structures/posters.yml new file mode 100644 index 0000000000..ce7ca40ab7 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/structures/posters.yml @@ -0,0 +1,26 @@ +- type: constructionGraph + id: PosterGraph + start: start + graph: + - node: start + edges: + - to: tornPoster + steps: + - tool: Brushing + doAfter: 2 + completed: + - !type:PlaySound + sound: + path: /Audio/Effects/poster_broken.ogg + + - node: tornPoster + entity: PosterBroken + edges: + - to: removed + steps: + - tool: Brushing + doAfter: 2 + + - node: removed + actions: + - !type:DestroyEntity {} diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/web.yml b/Resources/Prototypes/Recipes/Construction/Graphs/web.yml index 4eb368f5ab..3ace442516 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/web.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/web.yml @@ -1,3 +1,18 @@ +- type: constructionGraph + id: CobwebsGraph + start: start + graph: + - node: start + edges: + - to: removed + steps: + - tool: Brushing + doAfter: 2 + + - node: removed + actions: + - !type:DestroyEntity {} + - type: constructionGraph id: WebStructures start: start From f2643d412cc306d40bc81e6964867135eac15d72 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 10 Apr 2026 16:39:13 +0000 Subject: [PATCH 148/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ead430d572..dc4cc2d91d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4030,3 +4023,10 @@ 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 From 0ebda38e931689508ec499c38a78ab23524aed3c Mon Sep 17 00:00:00 2001 From: Marchy <89603088+M4rchy-S@users.noreply.github.com> Date: Fri, 10 Apr 2026 19:35:17 +0200 Subject: [PATCH 149/247] Fix errors MobThresholdSystem.TryGetIncapPercentage when target does not have component (#43436) * Turn log off in MobThresholdSystem.cs * Add TryComp for MobThresholdsComponent --- Content.Server/NPC/Systems/NPCUtilitySystem.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 89b49cbbe9..1997945d55 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -30,6 +30,7 @@ using Content.Shared.Atmos.Components; using System.Linq; using Content.Shared.Damage.Components; using Content.Shared.Damage.Systems; +using Content.Shared.Mobs.Components; using Content.Shared.Temperature.Components; namespace Content.Server.NPC.Systems; @@ -304,12 +305,13 @@ public sealed class NPCUtilitySystem : EntitySystem } case TargetHealthCon con: { - if (!TryComp(targetUid, out DamageableComponent? damage)) + if (!TryComp(targetUid, out DamageableComponent? damage) || !TryComp(targetUid, out MobThresholdsComponent? threshold)) return 0f; + var totalDamage = _damageable.GetTotalDamage((targetUid, damage)); - if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, totalDamage, out var percentage)) + if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, totalDamage, out var percentage, threshold)) return Math.Clamp((float)(1 - percentage), 0f, 1f); - if (_thresholdSystem.TryGetIncapPercentage(targetUid, totalDamage, out var incapPercentage)) + if (_thresholdSystem.TryGetIncapPercentage(targetUid, totalDamage, out var incapPercentage, threshold)) return Math.Clamp((float)(1 - incapPercentage), 0f, 1f); return 0f; } From a486ae7a0df843e2e73b2207e60cf0a675e8aa2b Mon Sep 17 00:00:00 2001 From: ahandleman Date: Fri, 10 Apr 2026 15:08:05 -0500 Subject: [PATCH 150/247] Move Kudzu Removal from Shared Seed Shenanigans to Plant-B-Gone (#43385) * remove quantum kudzu removal but add that functionality to plant B gone * Fixed to not duplicate phal effect name --- .../Botany/Systems/PlantHolderSystem.cs | 1 + .../PlantRemoveKudzuEntityEffectSystem.cs | 16 ++++++++++++++++ .../Botany/PlantAttributes/PlantRemoveKudzu.cs | 10 ++++++++++ .../en-US/guidebook/entity-effects/effects.ftl | 6 ++++++ Resources/Prototypes/Reagents/botany.yml | 1 + 5 files changed, 34 insertions(+) create mode 100644 Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzuEntityEffectSystem.cs create mode 100644 Content.Shared/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzu.cs diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 5fb2461133..2a1fb1a3ea 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -429,6 +429,7 @@ public sealed class PlantHolderSystem : EntitySystem && component.WeedLevel >= component.Seed.WeedHighLevelThreshold) { Spawn(component.Seed.KudzuPrototype, Transform(uid).Coordinates.SnapToGrid(EntityManager)); + EnsureUniqueSeed(uid, component); component.Seed.TurnIntoKudzu = false; component.Health = 0; } diff --git a/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzuEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzuEntityEffectSystem.cs new file mode 100644 index 0000000000..f681afda5e --- /dev/null +++ b/Content.Server/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzuEntityEffectSystem.cs @@ -0,0 +1,16 @@ +using Content.Server.Botany.Components; +using Content.Shared.EntityEffects; +using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; + +namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes; + +public sealed partial class PlantRemoveKudzuEntityEffectSystem : EntityEffectSystem +{ + protected override void Effect(Entity entity, ref EntityEffectEvent args) + { + if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable) + return; + + entity.Comp.Seed.TurnIntoKudzu = false; + } +} diff --git a/Content.Shared/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzu.cs b/Content.Shared/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzu.cs new file mode 100644 index 0000000000..2ab68708cc --- /dev/null +++ b/Content.Shared/EntityEffects/Effects/Botany/PlantAttributes/PlantRemoveKudzu.cs @@ -0,0 +1,10 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.EntityEffects.Effects.Botany.PlantAttributes; + +public sealed partial class PlantRemoveKudzu : EntityEffectBase +{ + /// + public override string EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => + Loc.GetString("entity-effect-guidebook-plant-remove-kudzu", ("chance", Probability)); +} diff --git a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl index 3a453b3404..6e7fae92ae 100644 --- a/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl +++ b/Resources/Locale/en-US/guidebook/entity-effects/effects.ftl @@ -495,6 +495,12 @@ entity-effect-guidebook-plant-phalanximine = *[other] restore } viability to a plant rendered nonviable by a mutation +entity-effect-guidebook-plant-remove-kudzu = + { $chance -> + [1] Removes + *[other] remove + } kudzu weed growth from a plant + entity-effect-guidebook-plant-diethylamine = { $chance -> [1] Increases diff --git a/Resources/Prototypes/Reagents/botany.yml b/Resources/Prototypes/Reagents/botany.yml index 1b1ae04cd5..9c8d4e39a1 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: From 7629f40942cee3a2739ed1045b4c1bf28762a66f Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 10 Apr 2026 20:23:26 +0000 Subject: [PATCH 151/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index dc4cc2d91d..6a51639b9e 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- 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. @@ -4030,3 +4020,12 @@ 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 From 3eb1c38c198ec2ca77e6ade7ce07fd73a62e5061 Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Fri, 10 Apr 2026 13:41:52 -0700 Subject: [PATCH 152/247] Fix pneumatic valve solution convergance (#43542) --- .../PressureControlledValveSystem.cs | 138 +++++++++--------- 1 file changed, 71 insertions(+), 67 deletions(-) diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs index 38f3ca8a47..d9128f68ae 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs @@ -8,86 +8,90 @@ using Content.Shared.Atmos.Piping.Components; using Content.Shared.Audio; using JetBrains.Annotations; -namespace Content.Server.Atmos.Piping.Trinary.EntitySystems +namespace Content.Server.Atmos.Piping.Trinary.EntitySystems; + +[UsedImplicitly] +public sealed class PressureControlledValveSystem : EntitySystem { - [UsedImplicitly] - public sealed class PressureControlledValveSystem : EntitySystem + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + + public override void Initialize() { - [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; - [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnUpdate); + SubscribeLocalEvent(OnFilterLeaveAtmosphere); + } - public override void Initialize() + private void OnInit(EntityUid uid, PressureControlledValveComponent comp, ComponentInit args) + { + UpdateAppearance(uid, comp); + } + + private void OnUpdate(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceUpdateEvent args) + { + if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.ControlName, comp.OutletName, out PipeNode? inletNode, out PipeNode? controlNode, out PipeNode? outletNode)) { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnUpdate); - SubscribeLocalEvent(OnFilterLeaveAtmosphere); + _ambientSoundSystem.SetAmbience(uid, false); + comp.Enabled = false; + return; } - private void OnInit(EntityUid uid, PressureControlledValveComponent comp, ComponentInit args) + // If output is higher than input, flip input/output to enable bidirectional flow. + if (outletNode.Air.Pressure > inletNode.Air.Pressure) { - UpdateAppearance(uid, comp); + PipeNode temp = outletNode; + outletNode = inletNode; + inletNode = temp; } - private void OnUpdate(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceUpdateEvent args) - { - if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.ControlName, comp.OutletName, out PipeNode? inletNode, out PipeNode? controlNode, out PipeNode? outletNode)) - { - _ambientSoundSystem.SetAmbience(uid, false); - comp.Enabled = false; - return; - } - - // If output is higher than input, flip input/output to enable bidirectional flow. - if (outletNode.Air.Pressure > inletNode.Air.Pressure) - { - PipeNode temp = outletNode; - outletNode = inletNode; - inletNode = temp; - } - - float control = (controlNode.Air.Pressure - outletNode.Air.Pressure) - comp.Threshold; - float transferRate; - if (control < 0) - { - comp.Enabled = false; - transferRate = 0; - } - else - { - comp.Enabled = true; - transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate * _atmosphereSystem.PumpSpeedup()); - } - UpdateAppearance(uid, comp); - - // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. - var transferVolume = transferRate * args.dt; - if (transferVolume <= 0) - { - _ambientSoundSystem.SetAmbience(uid, false); - return; - } - - _ambientSoundSystem.SetAmbience(uid, true); - var removed = inletNode.Air.RemoveVolume(transferVolume); - _atmosphereSystem.Merge(outletNode.Air, removed); - } - - private void OnFilterLeaveAtmosphere(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceDisabledEvent args) + float control = (controlNode.Air.Pressure - outletNode.Air.Pressure) - comp.Threshold; + float transferRate; + if (control < 0) { comp.Enabled = false; - UpdateAppearance(uid, comp); - _ambientSoundSystem.SetAmbience(uid, false); + transferRate = 0; } - - private void UpdateAppearance(EntityUid uid, PressureControlledValveComponent? comp = null, AppearanceComponent? appearance = null) + else { - if (!Resolve(uid, ref comp, ref appearance, false)) - return; - - _appearance.SetData(uid, FilterVisuals.Enabled, comp.Enabled, appearance); + comp.Enabled = true; + transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate * _atmosphereSystem.PumpSpeedup()); } + UpdateAppearance(uid, comp); + + // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. + var transferVolume = transferRate * args.dt; + if (transferVolume <= 0) + { + _ambientSoundSystem.SetAmbience(uid, false); + return; + } + + // clamp to equalization so we don't overshoot (happens with silly euler) + var maxFrac = _atmosphereSystem.FractionToEqualizePressure(inletNode.Air, outletNode.Air); + var maxVol = inletNode.Air.Volume * maxFrac; + var clampedVolume = Math.Min(transferVolume, maxVol); + + _ambientSoundSystem.SetAmbience(uid, true); + var removed = inletNode.Air.RemoveVolume(clampedVolume); + _atmosphereSystem.Merge(outletNode.Air, removed); + } + + private void OnFilterLeaveAtmosphere(EntityUid uid, PressureControlledValveComponent comp, ref AtmosDeviceDisabledEvent args) + { + comp.Enabled = false; + UpdateAppearance(uid, comp); + _ambientSoundSystem.SetAmbience(uid, false); + } + + private void UpdateAppearance(EntityUid uid, PressureControlledValveComponent? comp = null, AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref comp, ref appearance, false)) + return; + + _appearance.SetData(uid, FilterVisuals.Enabled, comp.Enabled, appearance); } } From ed9335b0f02e974ca208cb85c789948ef322b49a Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 10 Apr 2026 20:43:01 +0000 Subject: [PATCH 153/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 6a51639b9e..63fa73809f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4029,3 +4022,11 @@ 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 From fadacc6c348a89d530b7366bd2d1d23683e3d276 Mon Sep 17 00:00:00 2001 From: Ben Dake <124413509+Buunie099@users.noreply.github.com> Date: Sat, 11 Apr 2026 06:35:22 -0400 Subject: [PATCH 154/247] Cool down surroundings (#43500) * Cool down surroundings * Rework to atmos api * Remove thing i forgot to remove * cleanup --------- Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> --- .../Revenant/EntitySystems/RevenantSystem.cs | 21 +++++++++++++++++++ .../Revenant/Components/RevenantComponent.cs | 13 ++++++++++++ 2 files changed, 34 insertions(+) diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index c5080c0b06..19b6a15530 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Server.Actions; +using Content.Server.Atmos.EntitySystems; using Content.Server.GameTicking; using Content.Server.Store.Systems; using Content.Shared.Alert; @@ -28,6 +29,7 @@ public sealed partial class RevenantSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly DamageableSystem _damage = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly GameTicker _ticker = default!; @@ -188,6 +190,23 @@ public sealed partial class RevenantSystem : EntitySystem } } + /// + /// Cools the area around the revenant. + /// The more essence they have, the colder it gets, up to a certain point. + /// + /// The revenant entity. + private void ChillArea(Entity ent) + { + var effectiveEssence = Math.Clamp(ent.Comp.Essence.Int(), 0, ent.Comp.ChillUpperBound.Float()); + // Parabolic curve based on essence, more essence = more delta q, flattening as upper bound is reached + var dQ = + 200 / ent.Comp.ChillScaling.Float() * MathF.Pow(effectiveEssence - ent.Comp.ChillUpperBound.Float(), 2) - + ent.Comp.ChillScaling.Float(); + + if (_atmosphere.GetContainingMixture(ent.Owner, true, true) is { } air) + _atmosphere.AddHeat(air, dQ); + } + public override void Update(float frameTime) { base.Update(frameTime); @@ -205,6 +224,8 @@ public sealed partial class RevenantSystem : EntitySystem { ChangeEssenceAmount(uid, rev.EssencePerSecond, rev, regenCap: true); } + + ChillArea((uid, rev)); } } } diff --git a/Content.Shared/Revenant/Components/RevenantComponent.cs b/Content.Shared/Revenant/Components/RevenantComponent.cs index e434bba1d9..846a28e6c7 100644 --- a/Content.Shared/Revenant/Components/RevenantComponent.cs +++ b/Content.Shared/Revenant/Components/RevenantComponent.cs @@ -214,4 +214,17 @@ public sealed partial class RevenantComponent : Component [DataField("harvestingState")] public string HarvestingState = "harvesting"; #endregion + + /// + /// The scaling for passively chilling surroundings. + /// + [DataField] + public FixedPoint2 ChillScaling = 7000; + + /// + /// The upper limit for essence when passively chilling surroundings. + /// Beyond this point, more essence will not cause more chilling. + /// + [DataField] + public FixedPoint2 ChillUpperBound = 500; } From 4a1c15f334ed27b2f61ff87e754426b1a3c7f743 Mon Sep 17 00:00:00 2001 From: Svist666s Date: Mon, 30 Mar 2026 12:39:19 +0400 Subject: [PATCH 155/247] banners --- .../Decoration/banner.rsi/banner-blue.png | Bin 960 -> 879 bytes .../Decoration/banner.rsi/banner-green.png | Bin 1019 -> 947 bytes .../Decoration/banner.rsi/banner-red.png | Bin 794 -> 864 bytes .../Decoration/banner.rsi/banner-yellow.png | Bin 639 -> 914 bytes .../Decoration/banner.rsi/banner.png | Bin 1577 -> 914 bytes .../Decoration/banner.rsi/banner_cargo.png | Bin 519 -> 888 bytes .../banner.rsi/banner_engineering.png | Bin 724 -> 883 bytes .../Decoration/banner.rsi/banner_medical.png | Bin 428 -> 704 bytes .../banner.rsi/banner_revolution.png | Bin 406 -> 1181 bytes .../Decoration/banner.rsi/banner_science.png | Bin 537 -> 1075 bytes .../Decoration/banner.rsi/banner_security.png | Bin 742 -> 962 bytes .../Decoration/banner.rsi/banner_syndicate.png | Bin 569 -> 861 bytes .../Structures/Decoration/banner.rsi/meta.json | 2 +- 13 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner-blue.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner-blue.png index 7a1e37693e1259af069088582d43f3208cccb6bc..c7b4de255382159f5adf214fb15722d69fa87494 100644 GIT binary patch delta 856 zcmV-e1E>7J2k!=uB!2;OQb$4nuFf3k0009iNklmdu0n}=$)E94?H zCRqp}$EJtyCNsOU*-5k?1U7Hpy!Xv}Z|2STV8g`2sis|apMSW#$ou&J4`fPOBF?^l ze*bYNhmW2-X5U7^bd38P5&*&A0Em`bh- zy7Wz`)ovaBq!FmqZheu+bQ1vTRf7?!R}Ivw22PHT(6GwFoRi}tkUr@GK!5)T0H9Ji zmoBzXCL{#H8h`sHgMqMy*r*17C@TC&HW*K$UNzWU-bU91>_h@#4S9VF3riV?&db+d zah6?mM8*qB1pJ{W03fe#u{<}dvcuVIY8!cd3xTl4=6FF`13Us@4F@}E0BPpDlkutK zIu1WB0sx*q)8P+AS^8v<76t$ue%f!wMl}?#4XkbEn1ADjRc1N16KPmwLH6!d0%T+1 z#j@0wD}hL4y2%$1Z7f8lbJ{W}Gj=g{2JBw{PK|2@v@0c(9XZ1<6N6wFeNC zYJgS~2)B+^08-~P=0;kEj=f-QGbe3lo&04BTh4VUI&VPXp5dhj9vEAR_bi8a(m;7?{p6SzK zdLqD+pxZD2OpHI55@}srT8_QiFhO}6HmovcQ`;z+Ixeq1OLh1LrFtN_vg2P!w>N!` z8}eX0=}MGz28RDKx&1{%=okTdA!!L9cPd`B`AWBM(R$Uu%-jgGBYP1k5#TX1H-g-0 z`%xN3&grpY*Hwip0WA?1-iRbK3ML~WBWa&QxV*?q<*qdl3=TAhV==eku5eQ1>Ac5p^IhsIiM5SJ@6P?|Nf`1CEm018MdlfRJ18}+} zu&pAv#XH=8bj?1hz?t4xDRz{U8P!4sB+(+X;QM~C&Eij6GrC{~`^3yb%Rdc1v%?6f zX~Mks4P+-WLIAy>qMe_o1|>ku4EN%@ZGfp{9DLscGdl+lt6)DQ@yB315xN)~DUiKn>=Korp@5Nn&5VO`VyQ0Wkgv0|;>8 zc&EELlnb~%$dPsh+{1k-mXbi`;w=hIj3WR>5s4QCmyr9uU4S?~F0N>i4mR{-8M}NnB9j^lg%ahB31#a!_cm(XF zRqD)L=l0>K5xh_+NC}k7We_BWh_nvCZI-13lvUt^KLi{_yubc0y3oe+J#0)T>w@^M z`B+LA7g>?Wg>?X>k!nLm3Uz5JxJsEQ3AnX4WcT}%u}MS?UN#W zxQ9BZYcJp9Iy%zvIUupdPPesPmii5MK_jO2!+*xwZe!(cP`CC@`x1taKI>MYU48}j zVgcMQl~|0}96+a91=(4UG+(7GiFKm&PuXB6rX5{E28maLcIgGTp;TYS+AF{aviUXY z*N;Y~;lW5Ip5NmNIEQ-y_*nwoMoH^GQ4;pM9q6(VJUsXDQe(3qig@?M$c7V8-;3{n z;(uzi427bdU#A8eQv07;Ln9jQhetLezX<(54d^!GlW%MSJkm?66mU|m{pR*zV4ko` zr=Z79%d5Xn99UvS!Pq*#!cKzat~Cp^`hzd0u3doMuO=Am1H`GyM=w=1j7>l|$}Zof zKHf7iflv>eTJorMrI~miKj9|*a2+cLCL;xz=<8T8n-o-ad@3@30Wxov-925$U;qFB M07*qoM6N<$g4sH*c>n+a diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner-green.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner-green.png index a06a48885bfb42ca95b03e739533ccd10a17f7c8..30e26f0ed45a42c9e3c620e00f34e9da1cf79caf 100644 GIT binary patch delta 925 zcmV;O17iI92eSu|B!2;OQb$4nuFf3k000ARNkl~3~j>-UnE*?BYHH*e<6dl7nY{C~u$POoe)vG(zO#QuMu zmr`Hz?LJo?3?bOy$oybY(2q+Ok~!JR`8RWks}$M>_y5qpgUvj5amH;+0mSVKyCeUcD$G`l1W) z?}hyUn9sj@E`QdOfM9TBKpB$X!I1&ti6k*I>d;j(4Qdrj&9&R`!aw~8*onyRQhI^Q z%hy~w*>aA8o_9qi2%!kXwC-x{&KqXaC2aZcJXLO_FkL|3VPn5Ti>iwG#|Tc)G8LoriQ6itld?CIJWQgoHh%<*upoU3(Snnn1&B1{x9)@kH|P z9>vfN`N~WwlzwjsdtpaaP^(yM=&OuPOezwx{~PP8WXrkGJzz%! zlg#%^z<#Y50(!xn5BF3UWo3RLwXa-sVp^11#ed>{>h|t>!Tr>2isoa7!7z{u3BcWx zx42oF_w5C~NoYR*8o61Tr=aJFX;E56J5+|-b%LBjh#XM;U59irLxZMoB&1bKj--p3 zK*GMv1k#q}HVp9Jb=~tmAZ`PR14z3+m^4=Gbpl<}!3u$|lcg9=C?bx2_y(QW*+MM1#1LNK4 zUN53h1nfAyeTH|&TSY*e%VVWiH!OS!B*#Y`Z$yA3qf|eCQAE5*_RbNkeSGgJ_sxN! zq2bPvc--&vj_|Es_k0&Zz!rR~*Y|`#KNkE3j6#dKNr}a~00000NkvXXu0mjfWMsfd delta 997 zcmV-S@ls!bcp*N3SH1vuzJj;Lh|j=q(&9*>mRm&d;1V)gXabmcX zh1_BR7MDG~e{ikSG{I{ZmDoX2Mo^0sIEm~54B3#sCil&J2LC&84)OQtsT2$g2WUt3 z8W;wHLHiu2B9c<=NQ#E3;YK=SO9tfe@93}ZZ4&VY9%PGY$W3R!Nb6|Jl;E`2NCX6c zhSYQhj8cU;X(@o|4nlxVY%T(M}Gn85kImFbO3v^cdtVKjf5S5;^xL z;JD7e2S||q;&Kltafi)xD1p!_oxfgSzZ}2#37A9_o(wMUyp1G8K?;5PyqGg!`{N%a zRwx06A~q*r-y@HEyLRo_h)1i9r+iJ2kcrcG4Mi1(P=5k5m$R&^`?(XxO@LP_m65&b z^*YETNI|6TLRtg6)*X+){8EvXDpX9zImS=&oWSqCd3c7v>)S8*vrIxngFb&^4Yj@; zjz_?%=Gm>ad#b1@*N8-rfKYt@cpdC|M@b<3e5g~Fn2@Zf>*1X|lz{!Yt9^K~9t%jG zixjAo-+w-O1z$h;`LES#{EzDU&iVtc9@+=!rR#=wKN*xlPa=v?CNhQHR7ka2dV4@I-2o&9GS(GhTlyqO3Ry)97k+56C14BuFCoOyGyxCe${5 z!){aE0R`E}A`FI}s(cOnZWsg&HX;4EjmM=gmVi|`z31u5U0uPJj3iX#XpOw@+ZkxQ z+lRRY{}PLmSOTgXs?NI%q8WbIO-;0WfG%|{G3^9vA?O(|JUa{ZjeTx2^v?DGRU~4^ zW`8m&-zc%Y-e6=J7K4d6C0(3+wl)tqcu8YefW6rL69fro;r@Wn1oGU+y*cg?rz4vW zP7{!eHc2Fwq1dFnJk4~RaCh3ynGucN%OjgLlZMWo1Ffxi@5weX0aCuSR6r6Mw7Pz7 zWng-q%^Mgk7rr-=fsajq4Cb!pnT|8NSzgp&x9#7S7N2am9Hj1>WS zlPfn%%DVP5Q%CY@R3rlY&h2>sTA9D2)XaR^Un93bI<(?K*8jb#I@RYo2VX@1ApSPW@n_wCwTj-@%cuC zFVe4g+dMw8yboA<0EELMJPS)#Edao$dm$`cwGh=b%~s@0wHQNdyDHg-7ADc!uEOkSk}cuj^4yjy=p=wh3FJ@@ zGE67N5Y;s4M}K|kA#f3@kG=({Li8X*GCzZFxi1!*(|ie$TncsOPB@T&sMd{s*~oJ8 zI%5{H(|ifbMizRI@o#P{Ko2rRaw)9s_litRy^4+WI+l$rBDs_#-U*IHn0@o?dO9%% z06glbrlHOlA7j;doymP9qEc>R{o@Z?F4tKg7Le$eM1KIi`ml@CXanh??!VJ4@Ej{Z?)IPHHT84cIs5549Bob1aYT-_o;yPofl$)G*Z@wUn zy}J6TQ-6410R))_(>LF3$spTQx*-DMU35X2-jr06BUu za=;}6K(7vx`56Gf=4#1x{HaFXen@e93T7YyqJIcF4FkaY*9BW4Gz~XbOP<>>!S|f0 zGlsqS0*p4Ww!dO)BOcYeL*eb-QJr{0dRQ|pC*<^yvrw}69n*QgdLtsZO5``B_yo-2 zu5>y!+M4AwB$5wj=0NmzHzHpGL=0!0||03tU#8qBurZa6dN*R8VvJw74b5di@4 zcS10aPq+{}k(O`%qk2@f?RTt!aCqeU(fGL2>%MT-XxQEh0goX#Ycy^}fE)e++je#| T);-4Q00000NkvXXu0mjf#utYf delta 770 zcmV+d1O5Eq2AT$tB!3BTNLh0L01m_e01m_fl`9S#0008aNklyS9l&RQxl%SkQxC#g9u5o~kKmwfJXvvF)Wn1jT~~HPD)<6tRN;hYBTux(dAo zX_}0A6K6X+J0EY;4lLQ7c^|*`dv9i5JRpoxsl*6k8Ie}2<$t;KG6F7VVI0G9Z5$rn z{0;3W;`VWF@ofYPx8r2arsjT(jgc7106;AP zLO4(@`N83gapsMr=44$2V@ikb%8|Uj)#*UBehlVsY{EnkBun`IcqWEtU&??oc?Jmp z8pUU&5y9NmKYuCb!Wn|NH?;|iuTOAUDiCc7gi&FnS}#JuE5PiPbmVHJ2x4KT3C|l7 z&tuG@0+zGh7 zRL6JPZFm)iTL0>r0D{}qRD@OlawVhjJVOR)+XdDdK!1li$7eTAb1dI4{N%stx2$Jk zO9bQG1ug?NRw%kg0GKn~X{I{VMK85k}npS0d&4xD;iHgA?rMdM#DXf7#SW+ zYSqi%DSxK?{$=7A&KkJ;paW0eM7dZP^bO-MTR8v#7(h6BD2c;XT=H6%Ib-8CNm7;T z{X70sF=0UmLN2@)>qWSKZE%IVhO=!(1LA6~*0CdsjeP-9zw|DGN4EyuF{LdhF@PYj z8A0dO)#I6#D{0x@ALY)ejf;z&)H1BCql1d<1kB!2;OQb$4nuFf3k0009_Nkl4{s z>{23xLJnR+$$~wE8E1EAf7JB5WHa+-zHjEudv7KJ2cA8jxqorC4HFmVXA%CtBr-}* zDD3;!kDm`BeDUff`_?a8uKtLt1wbq|%64IGX#@bU`SmZ>mPU}2C1mp&D&>rAOg67! zr=Z$w={Lpo^&c0XPyhgYd^2WqMLZEjx7`N-%q|qrtZvxsQ|daJ)eUspeVZ-C_EAU3 zC3Jy^l>WvOQGblfX;>ZmP}-K1CDgPY8!HmS7zwEMURacPBYcPlv zX3Dz>NXmoNt`@7z&jztvWR-FTtHmnfi6|Qrgg~o!A__$jKY1_-*g=&A6O~NpyDLlClJYSj@LhAqKH*ZGY0(C(t?G@2M_=hwFHysusT~o(=voQgmkq7 z@>`@Pr;wVQvb8yFh9ba8n$2sj_H15ri5a?pR&@o!0K$V3BztLy^sKdo6_lF(SNfRc<#XVQ73YGScIaMSV;|Y!g-q@ zav;PRINo2z{NgSY6*$<;0r1`lS|#7E?E$#8_k*W03R;%wZ5RMH-p>f{12hZ|Hgkbx zm?Ct{7{o%QoIy?NVg1XQ(1y=;B;_ZaiHOi;dnq86WrRz7wuYQ}`|f0$gxqnnMxbhkuFmikt!r RU&a6c002ovPDHLkV1n?d&E_*K>^!cVs7 z80X}C@8y`n5K3r3Pfksa&-2A3SqmE}Py+e-PHXv(4mOtPV@t3#u&HTT1+)V?<=#th z+DZ~2gO;dP%5zvI2RTopo0m~uY->#f{r5r7>WUAHGeuJ_~XMSv7rw_BthHA#Z=4# zI0;C}-W`w&INqFA3a;9M8d;;#)2EvWaVNZYeg!)iczE)8pOEz4=k+M9L2S8HSee#H*Z`F(5 z0z(SG316#Z0b56SrqqTU50Nree$5*nB))qsa%Kr-f+6TTU^4O}(sBKy9z0y!ca zs|EIABlI3>c;AwqqfiWN3ldSqB(N>0wPK6RFZH}9j!{h(NB{r;07*qoM6N<$g69bh A^8f$< diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner.png index 8daa67ae23ed6f061805be4e6ade14c8dc289260..0b7ff8bd30bffbbe2eeee291e304f960aaeb3617 100644 GIT binary patch delta 891 zcmV->1BCpk43Yp{q&f<=neB1HrdBx;PC6~dB$ z7V@(mvOU>DI@8(R+3c?7gN5DMH}8Ej^XC0{U_jsCqif?>D}QmC+4r#DcX>kLNQl1w z{Ql!s1|L6tLf@q`+4SFI8UY}R9dsAkI{N?se=bhZ*4YO^^dqx0fx+1=-JHzQ1j5s4 zoh*4LP}Scidt3qlaPR&@-BAd>0JK^e0I-&*V`{ToC%+miVrsJ+TCJ>;CAhjg=GwYr zMSvJ3APNOv0DqCu5CqY$d&pROFhofhb+{&ABqI0%IGh(S^iDI$j4XJum#jlmD^66p zBp~_$0Mbf=8rRf{$=Jw(7t%@sf-gYl+?s&k3t%rkBcqM$NLDR~{>IAxov~55rdD)ggv8k|8GqxS&$+gm-U8NvSjB_{!1xDe zrBy{ndoYBmB3s@9ZXjZDvb&6YE=#|Aqx}GY;mJ9j+`+~o03e^s;^muFK#^7 zF#+R>Hh;f;wUxCC!;T1OYK5!l?ZSrXCfo$BLNU(~jU@89EKYWpak9Hi1J|ty5ERa( zrX=#YES1UUvW{+vt4P=^3>!hha#d+mL9Xk8wS+mD2%?|*1IdQGtZ4iux?B;6r@dH> z6-`Qwg$OSzl02cQ6|5!dh^M{o30z$sd*bOvk$)kGerUC_qu|2Itj3Cnr@fZj4>(9h zL+>;k&I3RfqNx%9>l{yfB=?~U01!=CubjY=83A!dQVjz@@c9?6Ba-|dO_glZFoEq3 z5NK)zgR@(xDl(My8LkXlsyodEmh9LLsd!O0RmknZ5O*Fitkufs?)-lQ#*T=f=?;(% zDRdkGws($n)p1pkP1O)fv=njtH4!cCaK6j)ZhQ zA{mp;WJ-ib(m03UG_%hgxAXx~?6~gg>9P9l3Z+7U+ZO^hM^GvhT0)>1{sO69cHDaI RX#M~I002ovPDHLkV1gx2poIVc delta 1560 zcmV+z2Iu*b2dNB@B!3BTNLh0L01m_e01m_fl`9S#000HsNkliOfpaC<1t7-~I`7?OX&H{`5RvvN*EuI=1az0h`2tcCjqJGUr+U1?A55 zfNY#l^)SYiF7g=9zmLDCpd24Io`B=k0gjlaJ^tBn0xDMz6$h|JW5O(fu_<0y*TwSa z>YNdoGX?Tsc7M%stF3}^GwCTNnBlhz%YW_Vpp-_!`;O}%NoYhlKwPA8MW7Mf-2HI4 zQT8RlO^lAsosRMxpaJm~TC5uweycD-|A7&7^tLc0N{**x%kKo+-(HT-Ke>k19ZTT4 zS%QifAOhBcJ6G0Bc>pHnCx%?gH6u#UGL^g7x$COhISz ztPDmH#-=-%bRTiigyG-yIrmPf7wLqDBH zVS}7T=0^!k64Fbbq~yJC`q2DjH`61f=;OJb6*MkUoaFN(wE=J#e#JDD3YiBc3PV5i zr@w~2@5h=aI!HtpDxzhv&PZ6&V))xW6rS!PMSlWDg+vA~#$h=#vX6(QwT0%p$ zAb)f$tZ&8mucvY0%g<1FY75IQdpCMUlqpVA5YJ2^9RIUyPnfqEnJ9!`x0C`m4bfvU zE{WDuIb{u2tZl=kGsiJb8eWv<+v| zuxsYIu(1mlzCMD%ua4f`m=u|RMvWjXHGg8k0HmBO4J)!-Je~=rk!N+_mc}~*kf~|e z(2n7=r|xXib&FwUrbuFPLz>otBcP?#l0cMN={WgJ=`bsV6ygamg0NwXW^!Wq$1h6z z!Xg~UV=59-VP@d~Y!c8z=2Mq?e)C3pdgdgXl$!#Q-fyKa9g33m(>;X^9WahGXn!go z8(O7FW?lfn<#X!Tr_J_-_m-u#Y)S4WmY`IIc5aoHbV?y6E2VMWvuRqSdW{KiZ0}XH zb`H%u6;&L`GI{Zk5nyS_e4-h8%Sl7hkcbRx3N&?J?m1abNz?!>T{@=k+EhBeGJx*h z9FFcE#kO~v;D|KSpbR6@sx@0~3V%$P;_FoPY{a32DLT3T61MKh-#Y+lK>3WH_63=U zM4pBDN@O}IX?$98=_qE?$w;wO6HX#ld#JQ30+2&H-dcrIheoh@*D6LeF7d&ZN)u8A zMfM9$MJx%XNMqpOFgCxuiqA~mJAh#9ywaJ+Y}>1i=-)Spww(JmRN^T% zpuZHVSXBX#RUG_9Wttzgq<=fQUu#IG<`aj;(-Ez`XJtCRTh@MDyvER1XS^l=vV!5? zl;7~MwNyIz_2}#+(O8BmpGACqoCI_cjh?I8c+CK0g=2rD268RyY%)8QmZl1B+__%s zsu{rTHGA>r^c7CTzM7ZNdje4D?YP|i1-%_tYqXm6?=#AQ`3GlCkr4#&;6U{N0000< KMNUMnLSTaPsP||9 diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_cargo.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_cargo.png index ff808bbc8bbca4efe6e3184df645be9251be9340..4a6f70cea145c828d15b0f81c05cdf51d010cb16 100644 GIT binary patch delta 865 zcmV-n1D^bc1o#G!B!2;OQb$4nuFf3k0009rNklw&|Ou#$`ib zEnT|c#d_GzWM=jy8=L+wnSJKTKEHXMdD-cKpz9OUr=qQ!+JD|E^{~&Ef}BRjhH3lr zhmYq9c<0_-+TPvS_x1PqEC7;8g=XRQi1+(t&ueRnO#PnZ9>6G6u<_mi0Mwff=S$i| z$Hz@yyg&c|xN)tQD}|bpVA?Ki1Awmg0|2VkL+WSRF6T@1?Z-)AtV9^V(!vl{awFV} zNpMY(p(!#RE`Q#~|ILVEooiY9npZ*L46@27ib_+sdungO`}+fa~q+uLx<|%}ll2EKRFf%(&HzqN7|I{3{2i=Hb0kGS*pefLP@1MS3s9ctBVV6C05-Ep{7N!^E>DcHbbqMG)By(ShDZC1UCi#- z!|j!L&W;Z{-&b#3v~8d6cmOhG#5e`eaWWm%XQy2g2n+`DUP6Gm{ytO2Enc{FI#4wr z1%YJ-)NHsi;}Z!$HLzuaDl&iy68hI@fb*Mg(LT<%tq_>;i|arDJ&+5=)E6TsK)(7C zZJO-_8OZU$0Dq=HDom&;8en-Ji*|Zh1{uiljBz@^)3V-d99(QhZJ;iU+q4mELQO0p z0M?+d&;UhIkOC)3GAsb-Fdnnd-!a?Ws+?{4@q>;Tyb}&362N;|ZLSq-%Fx~p2B5A4 z?3}9dtpG28E^HxEV97NQ??uicY^DjX|5bz_SYqxX9A+4xPa*mW5Cx#iSBO+d6o8%u z8-z5+EI$%}2?8WR)W4>flK@l*xYug}jN3kVk!zqe;I;WZO~LyB7|OFB<`CKoz;`;f jeGfVvV@)wqW7uHU@n!(8+)>(%00000NkvXXu0mjfhqKd+ diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_engineering.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_engineering.png index da76b044c1e1b33761127befeda670a74cd0ad44..0780db8b74657e18dca373516a2b5c09359f9f8f 100644 GIT binary patch delta 860 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 diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_revolution.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_revolution.png index fd97a4b39d661cfec813d67ba9d65dc4169db970..5e5cd4c8d71aa22bf627472da9458af5974f47de 100644 GIT binary patch delta 1161 zcmV;41a|wD1Dy$wB!2;OQb$4nuFf3k000D6NklYwFG``{gweq|hcE_%4F`p>33FEws!W;0uG__K zvcg)WY13#@C7|7hABZ14cy^JV<2lJWC&%s&f%m-6o9Fqz&wu-Tysv{CgikfgxpP*y z_$&dy_iu!3t&cy9Ikew`(V|dYtvbFpJ7=lzGmg>pVxNkq3I2HPSf2cywpMkVc#_PM zJMv}#pRZK)LQ#1bfW6f?Mde{!zG5DJ)559u?*h;sj{-1q?-N435dgX~_W_uwJk8!} zT-Cd|`8>~_A%6h?Z`oTiK<{>nagT`0A7FRmA9}Y_^!1l>AzHEU=Z)N-5ym~@s}q>)*?-~Bk6ZwBM%;8p+&cNKbevo1 zxH^6>IiE-7@#mGQE;E=OYG=faE4s@yvY7yo0$N8$2%a!5I+uQ)AibDi?#c~BL7@J6 zhQXJ*%wX#Xw83X&d?*-ZC>YL@E5xl}FRWjZC=^t`eEBs?Q?s_S;=>eq*j(bD~_f|+r zl5V$8A3q|!m>@cR2Y~bKT{`1-V7e;ittM0;h)&iwaI(I^mLW4(X2C?|X#iaQ0Fd3) z?SZS73WtJWUBUX)@0Xjm%uBPSpPnavbBKPw?0@nV!&-LkxYNn)fvYS{%>vl~j85NS z?#d0`>}b=uK5-y`Bw3oV?1o!w3y#vzS-CrNU#B=X78GcA4~Jdxe|1A6h=RaSFih*{ z2uoA5syB|3{|YR70f5U_Y*>U8WAe;hHANLDYn9Bg>IJ3*IwNi}V{K~wDkQW-v}vbB z;(y>U2>bfW`S`oPO$nIB0A%A}vS)`M##U7Vi35R^_8?S>v4%x9RzVjb?Nq zTE)%LGc*PQYJZ;&p9iu9N)!Y-%F5^{D^qtJ^Hu{{FIuG_6z~8XUQU(sOdJTry!wl$ zV)*9h8S1ZR*jig~w7C6p%xfO~N5*B3M}Mwq>XKXBetA9ig`62{lP`Nba%QYe{jO>1 zI?|&CEIg3=Lb~`Yq4U7;np)c9QS~?gb~pc}#AO`uxi2CGmWiu{vyE!dLAF1LfozJAxhVR0H1k7YjFZR`!uy(AK1G;%L b0{;P1UDm;~K}8P$00000fhdY36=wpB!3BTNLh0L01m_e01m_fl`9S#0003wP_>$3u=nO&_Fn;(rKq>^*lL8zN4T65S5X)58qH^LO8Fsm6#z=2szS5ih~9GHM2?Lrrm2D%OU zsuYqw69XYU30Ou5DW%<{i}YPzlJ}D5pZOqgbKbevWZ66@Y`cNz=uC zVYQlIm&B82D}NWOtEj9lZ_}A~;FV<4-^W)UIA1$Qsi=bFlV>XgyCf?&<`HOw+vDTm z?|13UJJ?-MvAdq)ho5HZbkgx70GuHg4=3Gblw3rKpipGj%7SG3@UT-nS&s zwi}ib8qSbQ5b#PeAEO`eN;1Lzl#r)iT@WbbvvlSi{Q3P(%kZ}(@c9Z=oe7OmN@)E0 z$6s|Fu(9sb@gvglqgqrdD#0#^Z@wEq*;FmBV{IbHbG{7HXki zNq;f*;0{yMv%J`Q4x6p*w=Bbhx5adJ}Z*YJ{ z0=~fkvd1T4OB&HP(c3gf$D7ytrgF;&ba(ew``m7;&zHh!p-4^rWK62|^JmXIL~VM*8vE>p#26NKqVn z?EgG7w&IZxR+eQgJ#4*R7df96KuYZV_u3`?dHfb#D})-w9Dj6TOz{5arup7(w^CKr z&e&?TlJR)lv>&D~Eno$TMI@Sn#<7t4>itwsW;5ArHqD~?F%a!TQ*j$NpUGq*-^-=U zFBi#lbk=~0w(U3G3xfedaRf6#H1$o*p#fg&$3mu3 z4-L8Izgf!*fR+k5I!=xEdL6T(jNuw7m_OHID>1BuEV2Lq002ovPDHLkV1nuj B;939x diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_security.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_security.png index 42114c515b2dc35dabb5fb643166d85680fb8525..0fc82598d5fc1d683236581c1a5fbcf3f6afc78c 100644 GIT binary patch delta 940 zcmV;d15^Cw1;PiAB!2;OQb$4nuFf3k000AgNkla<6vn@#gJ6&?f*NZI zE;VZxN(oYLMrvC_)q@0ju{RImr5-#~dMP=G7jGUsl^lc~+CvXPidv%8Ql%G538JKK zgcYi-)*_~mlrY8~)|qCL{ijj-L0~dF``&Nny`6dELXqU#t$!!QeHW?Z+N!JYKQS64 zH8=70hxq%l9NrDx=WS(ktK_+>qz8Z?)bds6JI(+AUSIK`?>GaI5OmHCVZM7(_fF^R z5Tc<8-B{LUN~eF8_%j3mfP0l!b)k^l6iQxOut3<4%yd#W-qR~1Go6HzC%Umri`gSX zUv4u1=9GyUlz-e5Ev@ws3DNynU#J2cqS(fn0g6E+H$~fM2Ua3WC1Z9*m*I;9p;Bvy z=K>IkHv572Fn1oPtK-gI-t31;DRxGeF%TbyJFg)b7s_=1*XN}ga)mf;ly zos*BZ1`y8y(a;12;=}Mof;xCJnI_@z<2xrUt@QxFXMe5a37%fBgPJ7}D=8BD`v9~R z0D#Ql0d9PIi%^ATgF$>*{eg*BpACIkeg%aIG$_lwy_Zfy%@Vx%wF}8j@vb2ZPj!v% z{r2@Jl)RRd@tpZrU>m4GS;k&Et?Tm&0=xon^jkAg>>SWSvcL5SA^O$QzM~{mv(6Ig zJaPnZnSZ)8fXv~+{{?U=TYwiyZO<=GL@Tk`Kl-y?J^%oSmCmX4NnNJbN&yt9n$bwb}VU^?cd(7-n*O%&C-OzIzh!95B8x zWPdoOuvFLb#@@UsUEvMsT1qLYkXhAZ1j@#I_e;FL_(sHJiO6qA%}r2d=5*C@Jom5k zE(`^8Ue(hD=HBQ=Cm)0iZ+7?l*|%@zz0p9x^7*_23V+saw^O`p#sGL?hHXQ2 ze-hSj|Aek-2EMH~CTuL&fiLMf7=w?CNbDyd z5aeM|OY3LHd`>D83rt{=K8H{a5bN~&P;Xp>%G@`|>3ZPD@xw(Mc0WzQ%8l?G&x8O_ z7_HVcp;EA81%J?o;#;q-z{cWX;5!un5w15Z&{7(d3MX&3)a}9x!|=wRuRVtAUpBot z6o1yt!BE$rJaecHuv^W*@6lyAXN=sCABEls#iK?;lnNcsw|!tjx|eZtW(3nWB>`5} zhu-p{g_FeNAkZEZ{rl;qDGVSZ9ECumL0XLKxk)H0%zpx585aN(O%;MC&IIr_4#s9A zYOGC&3GlD%KY$Q`N+OCRpb!9V$GDzlAc#2G%9Q~yd*8mCavt6NE>+=(cy-@`+4--` zu813ftdS1JB2fs1K0V?+o)2(;tq)IMo3MGu7LJ17H%!9P@?QWzGqPubF)Ir6+YcR# z9KV+p0)HqM-5USFg3|0!VnWi1_xz&;_h#G@&ZvUI0Hi-e@p{AUi0147B2IjJ&-BEx z1S_@WNrs{*UVnE9Dz|M7 z1IyF?a^)2uX@DriTG4O0YRx>P27MRmq-B#4mq!AiA#jW!tA%z*xCJ03E)5Vspi!qM z6=7un8iX9-RKcn8*``BN=J)gyV*Gm>Eb0OjgJU;|({1^@s607*qoM6N<$f*9&f AegFUf diff --git a/Resources/Textures/Structures/Decoration/banner.rsi/banner_syndicate.png b/Resources/Textures/Structures/Decoration/banner.rsi/banner_syndicate.png index c8424057d020c4eb817f2fce1561f02e4ec84538..5f7c2cfeefdf186d0f5103ddf7c2840b55bb144a 100644 GIT binary patch delta 826 zcmV-A1I7Hg1lzmWI0JcFgl-3 z@B8lEyZ7!46!78G=S8vaHF0-yZDjwy)eZ7`+p0aioVwYwNcA%B8YmfpQsfNL%VoY|Hl4^mko8@0PQvn_ZQQ{SwDb{AyfSq$SS1m|1@ zS)K8NIKi21L5L7LFyNZyc*vqM2rbK6Ft#M3S-U1fkV>`2MIk{dbA96|1OP}WktB(h zt9BP;11M|w>+hcc0LO74gis{M8=E;BlSp9D=9om{d4Id0NKE98bli5AFM3^Sk71o z0U<=Kh*uS$OFD$22pYR84L1@Hq_WbG&J+JN6$hy-Z8e=IXgnpZ7YiWJcBBkfC$=dX zZNqGeoqx(WCxQ}D73%5$P~!n2%Ff6&8IC$#NGT!Z(lEsU0Ch?#x}9w4P+JS8GRE=7 zrZU*ny^~0&z&)LZ=reo$T5vfRMxWUjN1-aIO(HxyW)(TmAO;RC`(Mx{^`4+oeT4txMuT)D*ylh07*qoM6N<$ Ef{z$?Z~y=R delta 532 zcmV+v0_*+V2Dt=~Fn<69XF*Lt006O%3;baP0005WNklyW>9pg@B)<%##OCw4t8|P;BUkMcnu;cXiP>z{)PupGxqsety47{B-_#{u*Q@HK z{%-2!a@kzR<8d^RWD7RJ_3iLYc;ug#aAmGRBBcJ_A0usLn#FoEDOMlUT45HtA1Y%2IuVw(D*aDyp zt~61KAJi>i4zyVIsuFM0Gb(0000EWmrjOO-%qQ0000800000 W0002eQw@^<0000 Date: Sun, 29 Mar 2026 11:36:49 +0400 Subject: [PATCH 156/247] headsets --- .../Clothing/Ears/Headsets/base.rsi/icon.png | Bin 402 -> 412 bytes .../Clothing/Ears/Headsets/base.rsi/meta.json | 2 +- .../Ears/Headsets/base_syndicate.rsi/icon.png | Bin 379 -> 374 bytes .../Headsets/base_syndicate.rsi/meta.json | 2 +- .../Ears/Headsets/brigmedic.rsi/icon.png | Bin 430 -> 409 bytes .../Ears/Headsets/brigmedic.rsi/meta.json | 2 +- .../Clothing/Ears/Headsets/cargo.rsi/icon.png | Bin 397 -> 409 bytes .../Ears/Headsets/cargo.rsi/icon_alt.png | Bin 538 -> 592 bytes .../Ears/Headsets/cargo.rsi/meta.json | 2 +- .../Ears/Headsets/centcom.rsi/icon.png | Bin 390 -> 401 bytes .../Ears/Headsets/centcom.rsi/icon_alt.png | Bin 529 -> 583 bytes .../Ears/Headsets/centcom.rsi/meta.json | 2 +- .../Ears/Headsets/command.rsi/icon.png | Bin 390 -> 405 bytes .../Ears/Headsets/command.rsi/icon_alt.png | Bin 534 -> 589 bytes .../Ears/Headsets/command.rsi/meta.json | 2 +- .../Ears/Headsets/engineering.rsi/icon.png | Bin 394 -> 411 bytes .../Headsets/engineering.rsi/icon_alt.png | Bin 537 -> 594 bytes .../Ears/Headsets/engineering.rsi/meta.json | 2 +- .../Ears/Headsets/freelance.rsi/icon.png | Bin 431 -> 410 bytes .../Ears/Headsets/freelance.rsi/icon_alt.png | Bin 538 -> 599 bytes .../Ears/Headsets/freelance.rsi/meta.json | 2 +- .../Ears/Headsets/medical.rsi/icon.png | Bin 379 -> 399 bytes .../Ears/Headsets/medical.rsi/icon_alt.png | Bin 508 -> 576 bytes .../Ears/Headsets/medical.rsi/meta.json | 2 +- .../Ears/Headsets/medicalscience.rsi/icon.png | Bin 390 -> 419 bytes .../Headsets/medicalscience.rsi/meta.json | 2 +- .../Ears/Headsets/mining.rsi/icon.png | Bin 416 -> 487 bytes .../Ears/Headsets/mining.rsi/meta.json | 2 +- .../Clothing/Ears/Headsets/ninja.rsi/icon.png | Bin 490 -> 371 bytes .../Ears/Headsets/ninja.rsi/meta.json | 39 ++++++---------- .../Ears/Headsets/robotics.rsi/icon.png | Bin 400 -> 418 bytes .../Ears/Headsets/robotics.rsi/meta.json | 2 +- .../Ears/Headsets/science.rsi/icon.png | Bin 387 -> 407 bytes .../Ears/Headsets/science.rsi/icon_alt.png | Bin 534 -> 588 bytes .../Ears/Headsets/science.rsi/meta.json | 2 +- .../Ears/Headsets/security.rsi/icon.png | Bin 387 -> 396 bytes .../Ears/Headsets/security.rsi/icon_alt.png | Bin 521 -> 582 bytes .../Ears/Headsets/security.rsi/meta.json | 2 +- .../Ears/Headsets/service.rsi/icon.png | Bin 397 -> 419 bytes .../Ears/Headsets/service.rsi/meta.json | 2 +- .../Headsets/servicesecurity.rsi/icon.png | Bin 385 -> 419 bytes .../Headsets/servicesecurity.rsi/meta.json | 2 +- .../Ears/Headsets/syndicate.rsi/icon_alt.png | Bin 500 -> 553 bytes .../Ears/Headsets/syndicate.rsi/meta.json | 2 +- .../Ears/Headsets/wizard.rsi/icon.png | Bin 399 -> 402 bytes .../Ears/Headsets/wizard.rsi/icon_alt.png | Bin 505 -> 588 bytes .../Ears/Headsets/wizard.rsi/meta.json | 44 +++++++++--------- 47 files changed, 54 insertions(+), 63 deletions(-) diff --git a/Resources/Textures/Clothing/Ears/Headsets/base.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/base.rsi/icon.png index 7fca8dc1d3f87288640c0d161b44c8c8f710cf1a..abfb0746e70f1c8346a7b7d15ff6b4abebedf2a5 100644 GIT binary patch delta 386 zcmV-|0e$|G1DpepBYyw^b5ch_0Itp)=>Px$R!KxbR9J=Wlrc-gKpe$it%D@R(c~5- z;0F){2Zxfqlh`JZMJK<7I0zzjaCLHUD73n|WeC|c1azrO5M4~A8GCMW4sxX-t<8mC zm-~(H?%sP`{y9J*kw_LyCGSSh^8f(tiz~*pF!0gsb!K{)rhhrtcR6nX9=@?|q%_-d z*k2_|E5O6IceYY`82ClA0RT&)2VA!b*R7_sX3JqSwwG7n{jHDS;TgMU4MZ>32p^x| z`}Y6QWRV_t%2`dH`Tq5~;hV6wzC gy@;PgB9SbVA6>3_ui2nd2LJ#707*qoM6N<$f(2W)EdT%j delta 376 zcmV-;0f+va1Cj%fBYy!|NklPoH0N%h0 z2al?Z`AuzIvZYoh_2woPQ($aL@C&U6LeBqG=k+ z^PI`7B{4}rJV+eJ!ezUK{AhIZtLsRRrYXI?z0=Fn<4DHxZD|FbwQH*DdO6Zar4)gm zz?i*Q_#)PhLW+QG+jRGEPp}|!yWca1wcuzXMF5}z97UhUBg`gil0cSa+Q;EA%pX4X zHbL*3NH-EEVR75GR1^i*8HT|Jk%*U&np8kE#R~`UuIqRU;Ev-^5CkNMh}=1L4gn3| zx~}5{~3Md8sw*udP W?|hSLXJ;D#0000xsBYyw^b5ch_0Itp)=>Px$FiAu~R9J=Wl(A03Fcd{k5esprB9T~Z zkd=`=9|0X$U_$%^V(5ZcSowl3t;8>6Wp2la4V(z*6qm6K$W=oMiPMTMK9ZL!=f11w zmj@1q!?9snbuY$o48Y0h*=$iC4*I`(L{U`fyHz)UDIOk#n13TgyA;TlZQJ6V&Tc_Z zo|9GT+jRh-I~vm+jZ5MFCBW5-;&|_f>~qRw@`1`y0AwH=hp_IzGK43Pfo$|`?HK@g z*q3C<6aJ1@&{vrQs{krXc?=Ir`d`0d6}6vWIOw;u&QV!9KkHzvi|G8~a;9~T5N)*1 zd4GGY^lvnTW>pFOEH3?k+q-+NZ+f-88g)Ph65)x$Ge|!`PcirWsPb%4bX%k(CG4u`|BQN94RpLu%Yf*4r<0000Q5c3)6a{&nCwXU65vwNE^i*C00000NkvXXu0mjf;+>=e diff --git a/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/meta.json index b612633d44..b9e6f63cf3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/base_syndicate.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/icon.png index ce11edf23630d1b184bfe9f65af1bddb6c7c4575..b946c90645eef397369a2f7394355bd504eccb38 100644 GIT binary patch delta 383 zcmV-_0f7Fl1DOMmBYyw^b5ch_0Itp)=>Px$Q%OWYR9J=Wl)XyBP#A`vii7lIYQW|U zT}p-y9d#^r@ebOhAPR~IUV(!;7^D=Wqc>2xq<0`fFTDz|bAOlO3Pk0;oyWKHxLFrB z>rM!%5?dfD&*UJJgN9rom9qcRttJprusjNuHzD^g0+lt7;9{NN<%)+J==I(KkdD-r zP`-g_h@?O|QtMqMfyV9tw$72On|xlLuy=3>KbhN1nd^^eduM+bhJA#v2*W;auTL}cwU*G$x(H!Sl*_b3wN~f& z^elFk)(uEUB9emOxjT9;(+=TBI9kS*-%P~$6)P=a6dXy&v@@DV<*Ey*EHExHZLX7N dFc=IA z^*$R1cVOGLxSh?KO^hvzKGKx(Jcj^K=tY7>;N@*apUwlpD;om8sPx$Q%OWYR9J=Wl(9?0Kp4eeq*Eh0DCBN( zk%D`>IAwQ`V6$|II0$Z89mGKpk)qwY33UiU3jG%{IfU$j6v4$*ja#p493&yZ)aH&z zm-}sbxqI(%+E0Bb5HJceVz{BI0Xf^;~g?B^OX`$=1a$4K$#B-KcTfluj!S3ZA#!q*64FdRn z1OT9f3YXwMfg;qp3QDNZUakUWqmO&z0NXo9Ieq*xMmPzD3UIvw-;Z#*zX_n9(}%9} z|F2*$>M1`nAb1kZDx}guWE~#Ij7_1ct6*%3=<{u%KldeMwGYt78n8b56;3sRtLs~w zonMsp<3545*@-EkQ0pqf_euJnY6NV?(#tq>9i{S7oERr1s76vf3Fp0#+y$~FigXb_ di9{k zFK`cF;RD#GvAZfyU>UgDF2zBJ7Xl&0nSWr8Gb-724+Pn5W_OvH@85ryC9=ahU>*2l z2OL#?6h(XFwrzVT8RyFNhoZ^ke6s#_3Ha0f4P9L?>Gt87Jb%xVyuGQ2*%Js{h#~?` z_`c7Mn20!&#&dD?Qh!P@fgU$f5&(FbriA#mc|EQ-o+}9eCC_u}y3QClZJ;FJCotZD z=~XBRSlwMxS(b!YZ9BSHEchU%a5Pa80I0xG^uF66oMg-jsH)0&TyOd0Ao6_NI|+Sk zqTEP+!oKgRu54?r3xa@uM_K2xTnuepv6-ZyL2+K`u*Miw(>wtB@I`Hou_yAwag4+#i Rbawy%002ovPDHLkV1i8VsGk4; 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 1e23bc37364115102b142d5f695c11cf48905808..0ff64526d56c97f9237c4d5919e0b3f7b119dbcb 100644 GIT binary patch delta 568 zcmV-80>}ND1kePKBYyw^b5ch_0Itp)=>Px%3Q0skR9J=Wmc38gKp4iK6eb_Z;0O^* z;X^2kH-=7~vN3du_!t=Kh#^@L5rNd9e*i;wopV5CA+%(LOvFJ5RA# zd~^O!NdV)_iAF#)0%I-*t6T|H=1~IR=3Fdbs@`e<0HEtx=z10`VDKnKCY@GZ1t|cl zTnXp%3jlzJhkF&j_W?x3vuDhQ<2qo>S6T&O0ISppfqv%0*&8_nh2?@$Lka;{rAFAX zS7R}cQgoUn*nc$>-k+yIGs;3F0*o?`YSH|RpN(t`LPo+RxanQ>>wW9Oxpl#qkDK0= zwBfL(Ma`?N9+u`;2C-QksJ5_q1 z=J6xg^j~RnK;k~Y|Ad+R6o^LP{PtpyerjaK_4;1OPe_ka zAR$TVUBH+R7BCQvygG*YU14o=3p=~th2~SblC5uSg+|T*(MW9H^Jm9(K*$W*hX<(- zD?wCer!gm~C)2^Iq#L&B@8VBgV#AfQgC!~Hr#{atDRdScV(Q~=7Q61k&*J=^C;)bIBRy-ADxO-zzNgD;*x zI_Ho>0CfBO9s4UL0;p6f;cw-6-byUyX0sXHU7j+){d(Joz6OI84NqvdSb^wa&}cNM z*=$m`+ofKwM*@Kp5W0LmPlZB(Lre}K2orq+VvZhddGyX5$01y(0FGc&bL23t0L1DR z$kB`UzVD+GfJ8YHiP>d&Q(00000NkvXXu0mjf D@&NOX diff --git a/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/meta.json index f13d472e81..168f7c29d3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/cargo.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi, alt icon modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/centcom.rsi/icon.png index 9184e71509a045f2e24818db48eb6a7259923e13..71cd9e9624c1b62d8d4a3636dedceb59f59c6487 100644 GIT binary patch delta 375 zcmV--0f_#F1CaxeBYyw^b5ch_0Itp)=>Px$OG!jQR9J=Wl(9;~P#A^3mQIN%6ePJt zC@wxnMpvO-y3{VtzJNHmh>LxNE?pG5I_3fN4T@4INozx=-b|7>NJ48Xy*C!R+z;+@ zlALq+^Cw_17z}%+qZ>?yJW~=ZNvtUE}=bvjm0b* z;fg}fD(?V*^v2YyWsXm4NXEH7d>KRd6ch0^2@7$_3FZ*y^vngtGF9I6H)+cr9EwF82?Q+}z$N`)N<0(QKu{ z71)+dIGyCzh2tV)NjM3CpS@7|6>BYFW}KA}j+^UQxb8w~7g#Q_NiX7OFc=JbBZ$Ste_)Pqx!`P82!vhG*80pC3sGEX~-qKJ|t;b-czsIy7R$$!}$2wdvbSw`vI8#@6p zaKyz4l>>ku9&bbgM;GYVLMj1(*LBSzvMDavR004D=$87vUy{wWR04hiyPiK3s03Wx zztYw92O&1kE81>0tbU2_KqUZBfrI?M-?M-vUb6y>F~R!fl6MXu`j}e+jvDGhauW{2 zKuyzdUL41K5oO8vH=<@8kfQkD03XMZrvT6MoXWB!5h81CqK$w6@GuN<9rMnMqM)j( zYPx%0ZBwbR9J=WmN8G;Kp2MKG@g7ULM9P0 z3Q#H*wo2^}$X`Hh86rhamrO(;kXZN=%$bNHWdW%xV}3x9m{}lF6(^+i;PZJS?qDw1 zA*t_Hs(@_ zo&7cBD>=Bo9)HKpC@YaDFfBaF=9&Th`zR(MgK!DDJtwU9pbvY{2V)+(Jx4ll5=}IG z*!qq4>mSEsc>}m_-2fH9w?|OzXX(3!cRl>P9!$OFQYu{rX@Rbj#-{Nod)rKeN)YAq zr&VBUZuw#YzCA*|k_&4dAz#To)juSg$t3QMlO&d)`G4aqg)40=0fZl(SooC&raef9 z0}|%}{w2&7(;zy5ee1?0XOMIr5bgmUVpVGa(FwA}^wbsC>&F4#Azey=ghfif1&n!M zJ_FIoi*1;n6~641ad31ZG@sIu?CZC3U=~aeoy6XI{@d=`AY=*6^G0fJC5Uo)(N0Ar xI6VCFq9`rgh)M!c2||{h1(+ulzF@(B!C&zS?E5U)YgPaN002ovPDHLkV1j>2{dWKW delta 504 zcmVDT4mx;*J~|Sg}dGEo@cRGh;d#csRkf!w%g<`m!yhdj(^SMHIir`NqiqcI8cqK zMrEuzPBnl9ARG(^;{37GIDxz-jvC+?#)TQ+=zZU(3f&T_ zPAklSj#wO~5CiS=b8=pHRA&z#bankrs5&k29nc|Q3~oFwoptvkKEaUxazVCjCu-D~ zK&M)kwf|c=j&ne~*=)#rYKAnsv#-5fhT@+Gm^h780C6vZ_-Hhu@pw$r>6B)(8L%IPvKc-KdA=N%36u%wCh!Xi63^d4EvkC}00000fhdB1C;}iBYyw^b5ch_0Itp)=>Px$Pf0{UR9J;$U>F6XU=)mk0S1i3Dm-xD z00RR914DdZ!GC1&h3hBaG{eEcfuOl8#5w?GxkpYeoPYc5J%;$eg8%p&K{W@!ESIrR zL=nGxoLs9RfQeKMX8rjLX8rjnVj-bX|8Xd%t^qP=96Ee4T;e$%*?6x6j^V zU|?Wikd%-l+Yuy(05-tQ#myioAxV~Ak{qzkvVfsJ{x*ZJCaU^b!V0Kb5XC2?{r~#)3j;Sd55w25Ul`uJ zen!xIk`v)qmULGPl4^Vm4E*0PESD6QVCd-TXQ-`jCfYobLm(tH>c6CfBm*}WH^cLn zFW~Z$;t~vBzkP)##x<*#F;cxKh8YZVgrv9xTpVUOsR@!~2Ov8Fn>ab*-+z}Svco!H9r$Ah994c4 zMSJA7ZF?*^%$4hpMJJQI2`#w8j zBH~OMPsP`l?WYtI=(v&66#!lo1;ueZuAODB?0~b`P}g;{K*vp#1pEZXJ21TpB>^|L zE4o^*39+dwx>zju5TCz)|$xGzhbdNdaY9I&ZsYJ~@b(V{aDvp^0)M`3d{J zr>d&BE(ikt9c5+x8&Q)ENK-sGz=vVrEx?l`p)AWtgh;MV)DhqS?z%31Kt6ezrj+No z4)05@@)bxW;9(f@vaV~6CRZSRwIVDxv0V#hldS{R0qelOci;ov+I}VNE1cB;0000< KMNUMnLSTXzoTkG7 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 9b67d0e8814c8438a535ad97b5ec4f13b0cbe8f7..5240eaed45406b898098eb9731a25b3937398bd5 100644 GIT binary patch delta 565 zcmV-50?PfC1kD7HBYyw^b5ch_0Itp)=>Px%2T4RhR9J=WmN7^pK@^7noK`y~!pfby zkcb`@Vs+(}DX$Gt3oVjDB!!D|f?X=H72%p-Wx%u1U}c|98(R@YQyADN7;tA_DrGHX z;;ts{&UoBik-yuwJM({U-kY5Pdg|#PD;D-awOZ{Mx2zQp0Dm0LQQqF_I!`*Ces}&) zVF2CCna03026NazF2CTZ%%cP#%!Q~$s@`e<0HEtb(Dflui{NmML_Ds%3Q_>M{DPNC zjsO6f%{vvpj{*24vL|w3H*65ON~;hVAeUM8falEH+23nm4_{y!CdR*xD>Y;!fLvzT zo8Q?8#2n5szkjoVABz^8`eVBpWg$WVI+^>G{66*uLPn8GP(Q9c*ZZac`=$XR7xm+s zwBc*@M>VgMiWp6OYsWt8K&4bf7HhaTxl#s@-UaLWH4c8Wj_(}KrOO}3hI1R(Wy0WMCikS(mCQYr@G*+Q`XiFiEpbboxU&=QmnHe;7ZhiwP|Fby+c zetC2V>c5pX2PEzT;!lXd7R+G-_PG;)>^45^J`lMFgo*geC6av|Gq$W1Pn?hr=Rio3 z(z}4j1+@rFV{eXOaaYLB%wp;1YNYv;u4Gfwvz}>~U>XbTd+}^HY%n%}^7dBjkCni$ z{k`3oA1*>jnD6iHMs7qU06zp{11|vNN}2c6(|@5S5q|V>$Nx{y00000NkvXXu0mjf Dw`2u3 delta 509 zcmVE@-n?#b{U2tpwH*?G@VWl=I30Y z0wiRiYQ*mW!&NDjN~_H!rZ(os1F%V0&ybd73EAz#BmE9OssH>$qtPgIfe1JWORh7A z%J7!3IpVI`_kUEY)q)Bh%5^jV5-%1DlFh&0UxnXA^Cl7^6##j4Fn3*-7($-gH%<)z z`;Io7%_!IHihiG=`Yts9HL>nfq_4t^`o^LAn}w_Gl%d3`4Ueg=K|8g>qMG-5)zMG9CCgGQr4tyYVA zy&gG^Lkxiv5V}I4K*eHFgyPx$RY^oaR9J=WlrK-iKp4iKlBt$}qj%_q zgoICkswK=cY-XklVrHfWH-aD`{1Hr&31El`wxqJQf{)My(;#n;*w4+6R;Pf&*48vIITYt$LATIv)x|583m%%da z=&ul^1&E8Q)!jrpI0EoAD_R=>upzpk)2*S?t);YjqZwr^FE_y1a~p?O#|YjZ5e5_Z zE&~8iLJNlwzJX7Ep}j%@?pl4km?x<2pQiNSBLJJSLIH$cf$uUnHxxiWrH@C$ z|9=Gor>9t^9eO>W*h#Y}^&_%c?TGU^G;I@{&++{=S?Mp_guF5X-P;1T!(YXzPH=sD zkMoPm+i{1N)pN^He9`Ay1ldJQ^ycf5owJ9D?d3(c|Kx4JjO8F~l;x fh@V6vk*t*;IK6tQTLuT`00000NkvXXu0mjfOwq4G delta 368 zcmV-$0gwKh1BwHXBYy!=NklO6{AUL82d zgIHW~ZLHF!v%Hi6J28r2EH3^VMlO=z-tC1z*llK4cINx{-*t%0Fb)_8{@4Lal^+Db z4ttxX8B>P2a{ib!nVe77-!1`vdbpyS#RXm8zLIU*yl>NcQ)=5*8#pbXB;Y47-hu8_C<)js z1?728h>y>LE?rk1L>G=CO1~kXnINr;8~wg3xoAp|<0Jb4sF zlx5j5+%ZS_3OECB-}mLRswx?Eu7K+v6k)lJ?FYfkX5)Zyz&P;l9ryrS6?v-A!i;GE O0000%BA1kwbMBYyw^b5ch_0Itp)=>Px%3`s;mR9J=WmcMV>KorNnluW)rB}WCj zf~u-mB4r3`C-zJzNe2%l>VQ~KqekkGKcI8xEP)Ol3Kdht$`ECQ3`|wVDkhZ@DL$X4 zR_;KL*!+m^E|J=S@8+}beLwHso$molE%lF;i2I<|>&+dn*MAQJ0B|WqvvIUAPN`J7 zH~y12fJNp+BOn@qnKe+^s|G4@lmNIn7xS5_w;BKd==vITeGSZKa4AJPl~P^>DS*me zHOS{)003@ou2uXV1`rm{o-q&3$N^)X(kci8RLc7SFwT6E{jCP_D+ALoQFv8Q+K@s3 zmGXYj?{7xNTz^W@?{DJtYXQK3lR%2&pa%x@O%bBM#3fVFD}RRz8b-~8i6qn z{>7!V;j~sn&5PC$FY|XR@k|Gb);R_?Fzh-2l1c#5r=YO@4sSc(=Du?&l`exc!0S(2 zI6FL%owiUyB?#+FdHd1Kg#m_L2LpQpMe95=KCn0b)_;GjbSf2lJ5DRM1kKLpMC~{^ zfdBy0FeB!*<0RP1Pib>Nq95RYLOk_hW(_#s-3a8O`EmV#@E+hM{4bZzCFf>buO9^b zhIA|T1&n!MJ_FIn%r(q=h3z*M-hcQg6i=xo+u5}O(=b6a5;^z$*%>(?WEIWE zQR2}`5HnW0WhcTAT-?0fvV}LI5F(^xswc&%X7xKgPEc?N@l7$*gZ1c13+EPs+W8j)&(Ifh9J5(*Gz z9+Y4?P(f5sS*wmC0T6(MwOUQ=KUTWQASuLF0ABrpWf7N`>E*J^Fbn~GE|;V6c)T?~ z=L!`dAq!O_eh(O~Nu>7!`=JX&z$g6{SqtPISkmojy zQv<-hqfI6gYPDLT-(;x1OASCxtoxh_K(SDu=hwe|Z{Hf^x-OyDX_3E)P7-MF#p9zp zKlMcjfNmZ?BY(wM0HsnX_^pP+;aV)_eBY<$)rkOTK6(AnTd%jK5fjQSQowo`IF3X0 zdYw9*4t2X-VhEgo&>4n7rfG^0okIx1SlUh~+Kd zqZjWz&qK!mv2w-|3zyYRhT*M{=d*EEfvf_$75D|t)6y&Zq^O1f0000Px$R7pfZR9J=Wl)X#CKmf&Gq*G6HOl}v6 z2>t=$kX4+5>Cz=4)HzZd#X%4eB;BNoP=_uJbaaypLZ&PMsY7ry^<(UHjZ;E{sm+B* zm-}t^?(V(Ead&_~AP_7Wspvx2bpZgTZpE{DZ`hgZp=sJe-+x-s0!g@YM$)n82QYOj z{;Nb~1(I-eZ$CTn#L8v^09JT6w7M!r{q?@kpZgNB+9&Ad8n8b76;5RZ*EcrK zFD?uFai4(EXvS2bAa#Q1W1P-YSpl1}^fEewzEt^FoERr1sH~)V63%-exeLse$kWUC e2?PScQuzUs-+1$hV77h$00003EkHKW z`r(?;7))qakmc+06&cw6GlPvZ z@{DGX@@xc$1jsR<5TPU_hzbFa&1q}Cq4n%ASf8MvAh;lc@rkhzT@6uD0MZ0Xa3ITn|Nad&17x|GnHfVx zMFj)QA?St^!zaZ7j3CQdSy@5FG1%u$PEHK__U$9a9-L~3jslnoAj?5@0SJ8j_z_G~ z5(QubNF_i)PD!wnYTu~((GVC7fzc2c-XQ=0B9?l4a1XnOEdT%j07*qoM6N<$f&)je A!2kdN 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 cce4bdcef59497a8d22a5fada059492e929499ff..e94f49c3233c8c3393183933077a1ab82e30e34e 100644 GIT binary patch delta 575 zcmV-F0>J&61lI(RBYyw^b5ch_0Itp)=>Px%5lKWrR9J=Wma$LUKorKm0FxidU{n!H z0X3C}b}Dn#A#21GK^Yiu)FE3`QUpW)fUZGeVbolv4EZ)c`|kU_d+&S)j5N{{E0y#?yWJibuYXq8LICg?Lt|%q=sbl& z;h*!5B>@aGCxbu+!J5v(DOWn@K?tbd{{U1P7B74pQcpVR%2U@Ej4d9ekL!g&=fA&_^g8j!BG{Arb)wnMd{deG$!dib1GIx&&9JZm-_A9eB4La30|5 z)Kxa@*MzKjv3`S>Gjk8I7bZ}wpTmWVld}r|wibZ$F0kKx#7gU6;5(l&D+O6zk`)xLeEL*MGmKOgf!-I`%8E1dY~aYU|j( zhsXoy{dl70TgNu|#1Cb2K;b?h{)Cy_ILIJ)*M1Cg*!ZOTfOHQC6Q^7W$slBM;{!9U zR@XvtLi&sW2}vsN0?q^QE(aO(*D);a3JXii`1E;0YCf$i+2V)g(8^jMgJSz$JbN7v zM3ZRjY&fSLtprgu_xDm!2q9tK+~1RKL@j_Q1kvOl03Ilrk2KP=&~F&7??=fECwKq= N002ovPDHLkV1hjF1R4MU delta 513 zcmV+c0{;Ei1eyepBYy$mNklqC-D-ge|+gOfutr@8sM+rFbv{&*}WXQY=%t$pUdUQb={Tx z%%uc?kbz+#o`)Gu#WYQS*2U6HT+0CSB(5-tUaQqa|9-#qU$6JYSci072c*Iv89tJ) z_66DPmViH=ZGRI?srF*+&jvs;nM_C)KYj2pP3H%0q={4v2({n`;L*jZ-tg z(T~Sta-XjVRi~w9Ku2sDCKUsvVv(NSdV>G{c$4-{T7;_8l<$BJ0b_9Eap?|scf=G7 z-MpC8@As7&%0(vUS}vF8f2-kexUd&%)9I8hkABg2y?QPB`21YJFppd#0StyfqtT#d zvq_y!hq~P^u?f6@z~%FKDijK0OVpP$Z>&4O;;3lB)9Y6%6+%Y>c!G7s!J(Z1!157T zmW3(N9LEtgFc6C~7C6uoMK;4nA+KlSEP*Tm-2{FB0e;rWEjtwK00000NkvXXu0mjf DDJAoo diff --git a/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/meta.json index f13d472e81..cb808c9473 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/freelance.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/icon.png index 79291740531d567ff420482910bd6a595c948b80..9dec6a4aebadcc17a71f975e555608d82cce8818 100644 GIT binary patch delta 373 zcmV-*0gC?n0*?cbBYyw^b5ch_0Itp)=>Px$Nl8ROR9J;$U>F6XU=)mk0S1i3Dm-xD z00RR914DdZ!GC1&h3hBaG{eEcfuOl8#5w?G`Mh;g;Qad!o-)J-7W~KO2&y>%X1SJ@ z2Dx@&1x%!BC@ea_P*`*TMJzll>OXeXRCd7ZIWHKj+PoR=Tz{X(aP8U+hWihmGB7YO zFc_QKk?jbQ(f}?XFQ-VhUXmP;nj*na9vIA^qM3vuzHaS8C@X`u4!Hl|DMM#(6^8iA zRm;fE1w^NTh3h9UHW#%s+<)*Cj;R5tS`cMt=ly^4`VE7Ks0hQG*KZhJzIsN`e3G-_ z57t)}Mz}sV3SyvU0)0NB0@*?7SJWv-9AgVQ1&f@bJ-n zc!FHHY8fNdi(;6;usm;P=Z)g1v!@S{oFGX~12BNC;6b;Xm=G8RqhJ(_f&m8rL(P3F TWDtJx00000NkvXXu0mjfZF#7% delta 353 zcmV-n0iOPk1N#DyBYy!xNkl7NoE z(pw-Y0U$qrT?nyl+uMHavy=d^R8>XeIBFAa0i*=nBgQ>=)+)Bb)*P3xX__U9A`$aE zUp`MK@t0hSG!9Y@6h%S&f$RHv)u4Q!6%%9fXHO3)yYB$&<(F`pChEFQ^trApR>3*f zszj+301y`r>$B z7ir)3BI>Mwag`!0*RfrR%oH03j047jw|C$NNEv`yPRGhy00000NkvXXu0mjf@!y&B 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 53a11ba7e138b8ac90ac9edaf87b530b95f83854..044825fd5e786c7c1ba9e34f31010d0d0518f1c6 100644 GIT binary patch delta 552 zcmV+@0@wZg1Hc53BYyw^b5ch_0Itp)=>Px$`bk7VR9J=Wma$92KorKms#DHZsGtc{ z5$T{#&Q9H(ikdx$=<49qL0SjFT6J=B=p_9MN^#LaNfi{#^*SkWkgJKd z=I&b1q2HF5+GY-p05+w_=YO(o=ZVE)zs}$12hh%( zXaqzfFh+HxR@NMqd6WRSITvd%Rc|!_00@VN5e^T7H5hD45ef#CS3wFOwX)`nMaBUD zFE7t3ew_ih#j|Iu4y$H?vAWVK2m_?z8xHW9c{BT04diVEhHfA_8CBYlLIA1whEuLy zd15xDC|9p=cz=EX`~JPT8D$}S0a}^6H8V5y142f^C8$)YpY?vM!FsGUW1VV1)I4FD zm>8Sp28lR^QvcMr+{(x)Igy^7_FvzG5{N~Oyn4KTMmkL&GA*~B&Akh!- zKOu%1Fh+G)H?{||-FUx#KzI*u6aJSAMfzK2oK9~#{D!nC1$>f}J_U@`!5R!iBR{TT z-YYDaNvvnKgyvIf$rhKAj-eYM8u6Ta{%qAO5Hg5-E*t1t3EV249tYeIT->~PdMvyV ql>pojgdq%m0kFE1c~3q43w;1`CG`juvXj^V0000t|c)&f-4iA1JYHQWSr;BoB#iKFhkU}t^)tP0#9`cU>HWC z=EredOGDkSwOKWG94FV=@Ao2Z3Q`FGb8|eV=yW301Zxb_6n~@?pv*ikK^mwaDyW>R z#+3lr072Jv#rwyT78x{!xC_8f|6&-#@o~PAivk^(fNG7tIt}c2@vE&)rjW- z!&Mm$he_JSQfjURz&edbK%!436VX%9WV_vpSV#0(C#ft4mEl*i*=*==IEeK1dQE^T z+l#e_0U&a{UVlk8_dE~Nv{)=EAzA?tZrKma-#)*HA>_59xHSMS{pE6@)oMi;by{lx zI%Y*VtpJQiBl3M;=wtl;c+dYAnf}QfzWC*GDUKkHA!40VPz%Pgtc1I|5%ai+V2u7c zomL(}Xq4WBvIz`kvl-3ja|(ih!Z4)c@hJAg7`gp^pIiomf!Ma&?Xy%;QTJMZ91 diff --git a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/meta.json index f13d472e81..168f7c29d3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/medical.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi, alt icon modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/medicalscience.rsi/icon.png index e98878e7de8d0c387eeb3deb7cc00645a1c9c590..2d44b1891992f9fa25a91d679679ded4c9e99809 100644 GIT binary patch delta 394 zcmV;50d@X{1ET|wBYyw^b5ch_0Itp)=>Px$T}ebiR9J=Wl)p>EKpe$iOBWN-NpiP} z6vY3)DWl*NY?m$(sm?_QK@f+6zd-C?AnoF!(m$b@I<{j$LYk$fvDT?q$2e$0q^-?` zK$rVKhP>Rp_qco$Kp+qZ7ECJYLc=fs0J>K4!g{SVI6{Ow3$ycp*TENB zt_1*~gvKtxeFD>vB^i{^SbMpK@UTd5s_kK8^8i~#2a^4XMjJ3KGgdfvfv(-d&E46A z{`~Y9`zwh23V&+#N~(N(g|6KTt|UnGN2Ko`d5n!9%gbPFgpa}7Tz~E%WSMu+>Q|B8KMzXVlCp!?yx o1Ib+=TwG*)Cx2jYMr0CD!t*?>hzW@^ zZaih5)b+O%6VTm8A_4%ve|{y{x7DO*yZtA9(H%gSvMj0VI$HPoE>1RtT>gCNjh(=^(etbqF-Wg)qV>_>swWUGKxz$)Px$p-DtRR9J=Wma$6$Q5?s=hD%;li`+eP zi6UrliCUVZtr|j72#1V@=7tCdqalchC^ww~9Zc{*`UftdHuqX2w>!;-2G53wYlwR( zD!z9e1bJV$<@?_6_xs%4@AvKiN5d``%cfGP1OSMI_6-2Y6n~RUalv44;2t+iB2(UJ zAG}jDJR$Z1G^(nt#Q|W%_Kg1;;7@zNoPb7EMeO0;Fn!jE=qIM&#f3E^J6gx~yNGl= zKz%u{;O6e5F9gdNGQWb=$4{i=0ZjS`y~0~7tl_@{xVSu|cbG`|kx2PF*29sg(G_GT z00p^S!t&`lDt}MccvCgVc?AGKBw`uDmN67O0*FK`#c}~0Y?P4=ZDMwQx5KYISD@82 zO8{I)v7A?sj;{bHclw2# zyf)N<`MH}=cfWwljRO;{uHr6{1rwNxlJOEcuK)l507*qoM6N<$ Eg2lYpd;kCd delta 390 zcmV;10eSxC1E2$tBYy#BNklU0@%koAUH6> z)hMK>rjMlR`@2giMKv!9=(g$P(wkNB}|^j^wZHmIN&E z5(QLMPx$ElET{R9J=Wlrc^MK@^5Rj1@ycu-KUd8$-h-$R0uyW22RYcQ6(brFSAnuNdyW>K*odlR1Khr$F zVfG5s=@&_s0-&U-9KyK+>kxH;lB&|T)-wRme(N#mKNa*%=D;aHlBKli74-jp#WJj) zU^E=mw9ZMgbaA%9Ru|Fb)%9HK93dKLo%8weUg}?I2+b1uU0el{hyEkCclXv_l{%oL zLe#~|GpInKXPP8UfS=CTtYW7jG{z=`3gkjJ#a#!|Ilyv=b$S&)kH_QLE8q5ZcJx29 RuvY*8002ovPDHLkV1hVmrC001Be1^@s6m49>f00001b5ch_0Itp) z=>Px$q)9|URCt{2*xyRRU>wHrXH7v?lL}-5X$3{#9dsmgwoA|>yPfW$9oZ##>``~n zNu5uM6x>Ac&peRHf~dXv&KKXF&)LNBeK&toH-LzUh=_=Yh=_=EXNbJnb={z#}RfJU;oAc@cbX3Y~5Q9P8kkUT2Bue3ky z^_2Fr>rmOR_`bCM)&uw-r58YU5cetVW-r7;caQ_9Y5-EI20-=T6>;8$25`}tVEXWa zMP~v4sM@yl1^v~>^C`;RrqaIZ<5$%$cbn4txgWy}KR*BflSdDAZ@nGGbwj^k`@%Ljvj)Mr%e>)Fr!6C8%2k>2(E6wi`< z@a*f`7y1uawQY=}uU~)ri#T}p+26>1&E|QXefA4q#9rg$1o{yzH}2L_|bH gL_|bHL_{RT3fCI0dPBU*1^@s607*qoM6N<$f*l?BUjP6A diff --git a/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/meta.json index 5264ee019b..b9e6f63cf3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/ninja.rsi/meta.json @@ -1,27 +1,18 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428 and recolored by Alwayswannahunt(github)", - "size": { - "x": 32, - "y": 32 + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" }, - "states": [ - { - "name": "icon", - "delays": [ - [ - 0.5, - 0.35, - 0.3, - 0.3, - 0.35 - ] - ] - }, - { - "name": "equipped-EARS", - "directions": 4 - } - ] + { + "name": "equipped-EARS", + "directions": 4 + } + ] } diff --git a/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/robotics.rsi/icon.png index eade7edf28ec652848f7289d6b8df8d18d70428c..702f28427117addc2d23ed8768a1b67a5a812a59 100644 GIT binary patch delta 393 zcmV;40e1e71EK?vBYyw^b5ch_0Itp)=>Px$TuDShR9J=Wl)p>EKpe$iY8M;PNs?Pd z3;qjE83m_6yL3r$5ZnUJ;vk4W20KcJ(k@+E$=I=(GPa8#l#tOh)^5Ez#z7NOOl>Yi zy4+{W%iVjA%QpvbI2_KJ5t1%6O%nj1sk$H4TSjfEhoUGeeSh;w3k2a8$Is#Oz;mFf zy8l;+%nAhI#of(_x`%$+>r*&t)Anc=b}H{fbH0+!kG)a^jsKs1VDX3 z?{?b%{};53nt!0Fy8i?>ZB{IqUPM~;%%^k)QQQKhGki?mR{Ap!AVn5~+D3vQdkqN|t2-h2j?};RX&MDmS_b2Lh+Ct`6abO&jAd(#FK{)F` nG8c%Jh||;faX1{#TKNL4*nO&m3oe=f0000Sv$wuS z+ehd#NG0?XnsW~6vEZR#Y(%g^Q~!k#8wzf+hCTt3WcQ&seye=0-!4Wt~XS}>25 zIpb9M{Lx5CKomu^d72Sonx=F|)2G{(aNc+)h319%Vw6vr{KkW&jV0Jkg) zFQC|Y7>1N2$uZonM*0Z20&vgs#Iz_15mkPx$QAtEWR9J=Wl(9?0Kp4eeOBZ{hlO(r_ zR2;i@$|yJm+ogjM2j_sZI0zy-Sa6rt!9}Hif|)YLQIL?aX{_CPb!!|nF~!v8j!2jL zZFzV1-sAXkfJ7pZESN&pjh1Br01Vv>lX|Dyn(3iw+FajK)_(#~_|f)b{OtrD4BZU> zDv?`(D7><^lu+04B%1t9k@rA*Xrn!}r?h&b8P3?AZvngbjM~`_+?N}?4nE)n9sq!< zRJa894WvQJ1XQI$XSs&3HmTxP-^Iq(0WkT|UC$K?=Puv`9`-h`r@{f?;r{Ob|AJ1p zRWNijMDw%sFXWgn z_=0>uA&`^#Mr4m-Rs``d*&1lsEMcd2#}EiJAl;4KUDegY5V_+Na0>jf0xKzg9LEZ| zec#U|J6~Ks7acg42l0;~z@M%+^l+!>{^g2%-{(AKOTst-gMTw3lYkOI5NJhgNL+AZ zlYLgV-%@Nq_Zx`_0K6y)f_;5Ainib1=!@CzHQjG z-v8X_n1f5$G>uAK=yOHJ;r(Yf0!7PFFie&imx=kgyT3; z+qSwc3`4yNj$+RlU9I z5wCzN03Jn=4!f??)@B9V_aqC+ZDcEDUY700000NkvXX Hu0mjff;Oc= 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 410b18ba09ac5ca93eeaad8a72eb83e48099392f..476c8bea62d82ba56b002bf999539d393639d6c8 100644 GIT binary patch delta 564 zcmV-40?YlD1k41GBYyw^b5ch_0Itp)=>Px%21!IgR9J=WmN87*Koo|57bahlNUr|Gj&6z6Z=S(-X^xd!f_mOpMp-4u1y#pHj3Anp5W~m&^a0 z|3w_YG;?AS5R<@qoq@eu<2v(b0SI#;He$NodH?{BPA?#xUH}_0_>>}*Olq%!5`ev1 zkJC1!IBMDo6v^l|2sJXFksUw+V9d3RcEKu~5|7kWv74 zWsi5O=aHCCDSx`vb9^oQgx|j%Z$?#!Sb$09VXdw32M{WfEMIQCf0 z%bGjAES47EM&eE{iv}*x_q6{*>ZhRi<~?@WClkkfN|no?46yob9cQ0URX>|5sST8@mXm?{H=O~hX=m7ANGalP(vaYOo)0x?aG@+n{} z02?t7lRUVFMX#{2wT%xSKS|A})sns2+~!us0x?PC+>2*-;DV3@S_e%dt|e1Gh5&GU zbZCSjge`>m@zJ65p3njaLlBbq1Hb|$^OY-uHd^CK;l1qz?Rd z2TtM`VB5CP>zB)AEDXKhs97g2m&-ex>2xadq(UMEfZQn*$bX&BNhiS?i%BXZR6rQ{ z&H_Kth3G=nT0M^xfCd0ot5v!Gc+(()q#_Oq;I}`wZDM;_y=uEEhDD%0lgZFxu{em& zSfK>~nmw54q%%8wp^X#CM29R4Nr2AGhD)sV-!00(xpv6jlRgcuU@{ zNA&W(G4c4t(+L zc3TXGLm9|T{(@K{i^Zbg&>Jz2g9yZkt6DAc7X(J=Np`4`fS}Q6P_x;jUav>}exGKu znd}EKYO~oaS(YW+CXPuB)j7eYoCce00000NkvXXu0mjf5Q6KG diff --git a/Resources/Textures/Clothing/Ears/Headsets/science.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/science.rsi/meta.json index f13d472e81..168f7c29d3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/science.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/science.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi, alt icon modified by _Svist_. (Discord)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/security.rsi/icon.png index cc301f1a04c7b9fefc1e03d650c76f90d276bdc1..27d66e602d5d35b6c7d261580a8605ccb42354b5 100644 GIT binary patch delta 370 zcmV-&0ge8H1B?TZBYyw^b5ch_0Itp)=>Px$MoC0LR9J=Wlrc-gKp2MKN4gg*lKKZN2JR= z+kMHs@5^`J9iY)@G#jQ;^v56w0055bC5!d0-}zI+w(YgLMt{)&!rZSNXX{=xgX4P1 zst~0G5a#y$0o4b7o}=5RUp&tv(w09U4g=O=>6+X(Od^auceQKlS1 zx&wI-lYmjCv{z~f@8UU5PhPNdum{BRtUh=hD49zK=tVO;TDwc;!0M}5T7a(KsW`5e zAUag_%B9qY=xhAqGU0p*LaN|=iqFYtt-jI_HtG!!QvGGlEQ(h99@jUwg|(DAz$gPT z3Br$Y_MKT2Jmv_;A%L_qr9Z_&ScG5}&FaEjb|9q#EQZLdtLSMo8qG%e0$4tF{=}$( Q`v3p{07*qoM6N<$f_fsdbpQYW delta 361 zcmV-v0ha!Z1A_yQBYy!(Nkl-MbSTD~OB6|G`KaB6(&kg))N9n_=Gj{`qHAB6ArBj01n{fFqhbvHURevuGLyF^=oq!lPqT__f z0li0Z4u_v2mgcV+7dt@HX7zo)rnYFVNWcweFx+u|IE1OTewAb+0EEMSYr!$Vltwe@j2&F}jZ6UMfHqlUPU`VtPq zKuyzdp6j|SOk(w)5hdCJ6vlxAd>lvS03HMZWm!f_$Zrd<0Jm)$pJQHm6h%~)Px%07*naR9J=WmOo4DP#DIaUWE&JZH0uZ z4YVTS;LyQuaQhX?wGKL@Q@5fh2!4XQ-r1=g6fuIEo6wKoaEIa=Ayuf*oSs6V5{GbV zqOHj}6?Et`B_}!W^Lx*G{sA-1^uBmJ-*fj(a~o;5jcENd>2&Pr_*StcXk7hEU4IO_BM2bac*5ov!!G#zqqI37 zaUT#nVJ0^R(g^OI7lEAAKkhz|xR}Dks#JZ_2$|g6*o+b!nfa!Vp5j w{L(&4JSP#A7qoyNT6hKEJt^~ApRH2?qr delta 496 zcmV82nsU+(NYoLOpuiJ&Mq);FI_WK8BAX zeF#y!h#MP;%jH5zaevo!F-^T*FV)1mKos2 z%z%d0Q%E5OnvDi677HOT8ja}d^M}yue9Ct~hk!A-@wjw@!9YyG(002e&-0WTnJ!_g z)r$XChr{84_?(V%d(h*eBT%2yMaucnZU84 mWQLC-xn7RT1j+<-6Zi#oFVm@M?ycbf0000Px$T}ebiR9J;$U>F6XU=)mk0S1i3Dm-xD z00RR914E2=)_-L2C0nQAG{eEcfuOl8#5w?G`RsMm;QYJy9y7#vXZ^?L2&y>%X1T7O z7P)p|1x%!BC`>!RP?&ZAMa(}S^glMWRCmCUXtYi(n4VU6NXC@wHYok97Yk|xM4NzbHS3W(->pCv;IFJ*Crf*ss&MGRKovn z-@Y<%ad9(z`+xS8;p2zb1kEQo8-8Ye%^<46!|<2?8;0c)qGAlK?R^Z@warAEM{)@G z2Za6?7ZYdT=Hy~{@%j~9UP4ri;p_Kr@Wi-r!)ivV7sW7xVUCax6@!bzEGIQVl2s1C o9Dz-o9P>uOC>RB!U_b!?H@SYzT)*h@00000NkvXXt^-0~g69OWm;e9( delta 371 zcmV-(0gV2m1C0ZaBYy!@Nkl|^5>6s9#s#?}7&!q%_^7vj--}jWJsWNogKuQ3>cP}@h&iDPf9`8W) zDx?I&aZDet4}@6PHQnxZLWC+DO{4?>%5W5Y9u5d+8LI-yvNYbNKL8>yFvs3m=u;Ev zM*I^F!$3{bh-^B?am4S)>%WLvb%2}V!2v#wqu2sG3`5GYj93WgZi!n27=W9mi62m$ zJc=U9^Lz{MN3Qf0a3$dT{a!5FwiQw33b^m42+LJ$-vrai+5zo=cHrMT@CDVbf$;q$ R9CQEx002ovPDHLkV1me$uB`w7 diff --git a/Resources/Textures/Clothing/Ears/Headsets/service.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/service.rsi/meta.json index b612633d44..f0616e50f3 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/service.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/service.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by Hqlle and SonicHDC (github)", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/servicesecurity.rsi/icon.png index b010229b037f572d37d3f8720948852847d42cdd..0e9f881d000339337742dc1578f229e8508d3c10 100644 GIT binary patch delta 394 zcmV;50d@X?1ET|wBYyw^b5ch_0Itp)=>Px$T}ebiR9J;$U>F6XU=)mk0S1i3Dm-xD z00RR914E2=)_-L2C0nQAG{eEcfuOl8#5w?G`RsMm;QYJy9y7#vXZ^?L2&y>%X1T7O z7P)p|1x%!BC`>!RP?&ZAMa(}S^glMWRCmClb=Yl0$r!mHOXZ?Rdu1z=qRSTlXsD%ID zzI|oj;^Job_J8dw!^aP=37SuGHvG)`nn6^Bhv6^(Hw?=qM8z0d+xr-*YnzESkK_>W z4+#A)E+)>v&B?{^;`J-Iyo9J2!`JWM;E8eLhSiKzFN$FX!yF+YDh3yaSx#z#B&!^N oIRcwFIp&RmQ7{Td!GHn)!;62-NF8Mo00000NkvXXt^-0~f=qp{p8x;= delta 359 zcmV-t0hs=y1AzmOBYy!%NklCC3T@kxqz;GtA2sq)ou77YuN5m61-ivvje@W4S zZW~Dn0Q`Km2(fM3ZN2thN`RoOmo-fj6-A*9-8PUC05Gp8ig>Gi|7av7Aj>j(d;K89 zuIuRKbP^(t_z$E60LpNXFW0LOaKvL*K+`nV`#J+40tPx$<4Ht8R9J=Wmc4Go60N3r~x_#6}vCeZ&dOhnZ7y*o%n!5o1|HIvRt?z-U=)Pqx3Ay##@2bx>6cTaFSixAYerLu zMu0l=s$94K0DrrWDM|<=FoBp(wimHm&8d-p#JX0r7!o^|D@Mz zyd4kKSb^)S%Z~1T=}X}L;HmBiXug{3)T#p4SC^ep5Pz0&65|a#gd>QPc<;Z7sWCv+ zWE@RO;Rw8eSDW#4dRFQi@@`i=31{4dN)@P$LO9~VHLTwXKYoV%`u(R>JgXTVFgfy5Tbhzpo(WT1wltQS3z(UoLuiOz17J- zz{%Bf_jHm$8?M00kLn3UMAVbfw*H-?C3F zW#X>`P{h^~G7Li%Hk-{Pj$_$uHgY%|tQ$nayRCSYH8{f~p*Zr!udPq@zZ73QcVMByWN&vMJIqLit=l9I-MSJv39*)Wipv4fS>(d zzIVHa7#*17(f|fRU^<=3d_I@eY9;ISS_pv`5OKcmOH`}XQYlp)Ap|=80~AM(c7J+r zyu?+mixB8yL<2D^6P=YU@$D0 z#Jn3F#{s}DR{c@E-mFdaC=?1aeUtMR2*T~{#H(@N!vKV^Ho1r8qe*}NBYKRY~iTew~u0BOm{CA4o~5+Ws#mW*^( zYY1<5@96I2$S&t-(Ft|;*^LygUBLC)u*X<9z|Hm5|NjN`W-ama4TytSA0qqk$X9BB z5GyD(!25We>0_@wgev)tTwjH?;jeI+u{giD74ob+373x8_?m%i67%eeLkK$)A7z_*L3xu6_JYD^M@c;k-07*qo IM6N<$f)zTl6951J delta 359 zcmV-t0hs=h1CIlcF@H%(L_t(oh3%9*OT$0_Kwrc{16Lu^p^k1+aIixM2Zu}@#Mxiq z?3mp@;2`vWNG694ZZ7R)aS%FmXi8H;>`@vrq*IPM2-hUV<=&Lb2k&v0J1`gw|BR*k zVyo540Hl=ao|IDi`@cBWl|NiQ(LTT8@%5H^y`DeH%0?XN$ba3ldjQ6h5zlu%o6UwW z3=u*ggvbsWkmE1=zs6NezjnZv_0Gr38~(D-crvQOyI%l6bbZQty{=TQ9k46@4nNhk zl(O_vXa#ia9yb?lI(CowV#Z0Mkq4Ci53~a2i;Ncm5++GP6ktUbfa5qBpvFbe-UU)hk|ZHbQ*wE?+r{%d6>{hRD@{{swVFEpT-RkV z7!-8_dn`eY5F%?fn*hXd43t}fLO70-m6>*f!C)}#vIQ)PfP~<&(GLIs002ovPDHLk FV1hv7q~QPn 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 962130247f8413e1af8456e93e6e012d8cf0c223..7c010f179a3bcee305f3342d80716bdd1244a584 100644 GIT binary patch delta 551 zcmV+?0@(fe1Iz@FFn*laHjTk-(OqYEdSpfLLJ4zku3w z$bgv`LZhN0b&VL=7*Gb3Az{b@5({GXLc2j0I-p1}RC1_jP<^>fATval+K}M8OC%Wb zZGQIM_j~v5d=D6Cpchso>;v1jJI1rw4G#cZN>M8AcAY1gOn*K*|4toRMF|a0sODST}sPZaE0az=m-qhqf06@L|SH-V4 z0Ka(lj5XlY95B{US_NSMYiZpBTA8=A|EGc6jl(od#HZs*8&U|sT3YwEQiVXwr4(DK z0<8HpxWDh)n}1OjA{3yLxnDCMW}iUFNVo*QF3(!^zOKQ!uC-&mYDCmLU&-P9R3Z@1 zS8~W;6V;n@WdP|@aC~}%pF4XU-?@}Zmq8lf?T#50Lt{bSK+=^1}dn16;DFh9An!AAZ_n*$R40RI!> zO%vvX0q4REKz17+)(;5p0dB(oa$QT}pwFB&AOQV-2t-1JTHnYnb;6 zA3vw@_1kx$`IK6+PpPzL8YYNF0_UDTJ2eM{jG|QBjr6SqejV(WBYp@jZho*|7T$N!P~|B*@SB@pDH9X^Zm@f zAAy>h|D8sW#zF`^`!0&2LSrl>FdN+Ibdu*F2ug`$b`nBp+kdi{yuZ(d2qCnQSyUvo zaawqqND+xl0r;F%x7*ce0gFnc&dRndmW4=`*YI7CkqCl-X0wS>DpMlY3IKCKUuBGl zlu}D60Z>Yn7s!K=v27cGv;98z&o}(OJ!Lc+Wsbj8Rv?O^hS?Gm4sg8l6VLPJ>PF=P z0MqI8ukilcIe!5D+F_oSqZqA~$9`!Ea{@2(Pt;lCAFlu87wl@*>| z?*KShKVffki-Yx(TzyMlLxbme+Fv_l!`%kp>Gh7m%SGl@EF|DKPI98h<8fp5TO7yi z|2P8RTK(bS@v*u>F zQoF8;>$=2oj8ZChIF#4|Q+fd=lL>$ksP2CtugvUusHv$b!UvwB(;tg@dfEU0002ov JPDHLkV1iB=-^>62 diff --git a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/meta.json b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/meta.json index d5510a5d8b..cb808c9473 100644 --- a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/meta.json +++ b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/meta.json @@ -1,25 +1,25 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Sprite recoloured from Freelance Headsets by Entvari (Github). Sprite modified by PursuitInAshes (Github) for SS14, original sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428, resprited by @mishutka09", - "size": { - "x": 32, - "y": 32 + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Paradise at https://github.com/Henri215/Paradise/blob/27087670280de99e2fceb47194aad29a7b99b280/icons/obj/radio.dmi and modified by _Svist_. (Discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" }, - "states": [ - { - "name": "icon" - }, - { - "name": "icon_alt" - }, - { - "name": "equipped-EARS", - "directions": 4 - }, - { - "name": "alt-equipped-EARS", - "directions": 4 - } - ] + { + "name": "icon_alt" + }, + { + "name": "equipped-EARS", + "directions": 4 + }, + { + "name": "alt-equipped-EARS", + "directions": 4 + } + ] } From 15588ff867f54b28c87d98e6065f702f48b97d40 Mon Sep 17 00:00:00 2001 From: Svist666s Date: Sun, 29 Mar 2026 11:53:44 +0400 Subject: [PATCH 157/247] headsets --- .../Ears/Headsets/brigmedic.rsi/icon.png | Bin 409 -> 402 bytes .../Clothing/Ears/Headsets/wizard.rsi/icon.png | Bin 402 -> 394 bytes .../Ears/Headsets/wizard.rsi/icon_alt.png | Bin 588 -> 578 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/brigmedic.rsi/icon.png index b946c90645eef397369a2f7394355bd504eccb38..63cca39c10a560a5625653cf91ec97b42158b1ef 100644 GIT binary patch delta 362 zcmV-w0hRul1Cj%fF@H=+L_t(oh3%9vOT$1M#a~1adf5bXH@HYa2S*)?SzMfxB_Iky z2fu-XIvAt`q@$yYWJzWh5%MuQ6ct2MwcUDx9gIUstTfu(5!&T`TV8VSJud${K%r15 zW{g(!!zhXX0G90}qk0(le|ngvInj5%Xn{05SX|GZZ+f4wY=0;DuS97D((vWAA8n_# zfUBNt^pH8x1vTA8O?Su2O{IXd;Ud1+MErJ(r`GTn005&*x`gl!E>E!P9RdK__ie`8%%Es^O&p(|750+a0Y(`}g&=SnP71@yp5kJ{-OT3wnNYjv;>w_4&U8Ba(%P`48F~O`xNIU(DIv!Qn@m$*6O-l zQ<>|JXnSXW7;1)ngs=$1K5ws2GxN2U(9OCCVNI0Fv_rL4=lJw2c9zx+NJk=)g5bG3 zdM?ur;YT=H#+Khq#Q7B~EnyTKNyxM_nn&fT3#lwHE-`JclV>m(3=8E8*RXas;zO~x P00000NkvXXu0mjfkjk)f diff --git a/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon.png b/Resources/Textures/Clothing/Ears/Headsets/wizard.rsi/icon.png index 60d693c0d06d37f3c08561f1ba2bf79de1750ec6..93bf29acb7b89875a770dfc4e6ae672d43279b69 100644 GIT binary patch delta 354 zcmV-o0iFJm1BwHXF@Ho!L_t(oh3%9fPs2bQ#ox%(j(~uqcM^lZkzjDub4@lgK~b0? z_5}z6Lonw-UD;pz1eJdFSVjIzolv~QpY(jZ`zRXVFR zgv|{P`^N`4oxku(;o1dw8lk!A<@CGToB#g{20_2}^9?Y8(TB)CJd#38K*%bDm>~Ul zTj;MngrYtM+gn}()~C5}=1`npT;k;PthAr@4Yb-F$tVM95G3!TY@In2Vk(e~5&++G zVf-mp#;OE!Xr`-h-GS6DFk7NXui|I1SS(BB3x*zd3lA4K761SM07*qoM6N<$f;CsE A761SM delta 362 zcmV-w0hRuW1Cj%fF@H=+L_t(oh3%BFPr^VPg+C07oY9HU8wrUaI+-{b2NvBiu&~t0 z#f5*sIJg)W`4u?+mixB8yL<2D^6P=YU@$D0 z#Jn3F#{s}DR{c@E-mFdaC=?1aeUtMR2*T~{#H(@N!vKV^Ho1r8qe*}NBYKRY~iTew~u0BOm{CA4o~5+Ws#mW*^( zYY1<5@96I2$S&t-(Ft|;*^LygUBLC)u*X<9z|Hm5|NjN`W-ama4TytSA0qqk$X9BB z5GyD(!25We>0_@wgev)tTwjH?;jeI+u{giD74ob+373x8_?m%i67%eeLkK$)A7z_*L3xu6_JYD^M@c;k-07*qo IM6N<$f(|IM4gdfE 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 7c010f179a3bcee305f3342d80716bdd1244a584..2421c4ad3f0b8a08194ec11b56a4ee9256c21efc 100644 GIT binary patch delta 541 zcmV+&0^3z$kw|OD>=~=V zF4 zr80o@DTs|M;C%a_;X8*?=`u(I9A*-j+p%PaHI+~ayqZ{=|21=AfSbolq~jaN7jwRN zI_|H3BpeRB9X~6u1clSB(Ak|;hX4RWH+<%2cNSR3gMYL+Akh!-KOwqmFnV>^*NzXe z+4!J-KzI*u6aJTr^tCt4IGbH_`3>n%3Irr6eF_+>g4Gy^M!sCbyjPf-nZ@$zy3l+| zE!lK()-`kkL?ga)&!6p*4MI9m*xwEPS_!;5IX((`A-K5t$?=i!MpOduLJ-pV0l=zK f<}J1KFBbF$!;kP;*Tq|-00000NkvXXu0mjfq-g}y delta 551 zcmV+?0@(e+1k41GFn*laHjTk-(OqYEdSpfLLJ4zku3w z$bgv`LZhN0b&VL=7*Gb3Az{b@5({GXLc2j0I-p1}RC1_jP<^>fATval+K}M8OC%Wb zZGQIM_j~v5d=D6Cpchso>;v1jJI1rw4G#cZN>M8AcAY1gOn*K*|4toRMF|a0sODST}sPZaE0az=m-qhqf06@L|SH-V4 z0Ka(lj5XlY95B{US_NSMYiZpBTA8=A|EGc6jl(od#HZs*8&U|sT3YwEQiVXwr4(DK z0<8HpxWDh)n}1OjA{3yLxnDCMW}iUFNVo*QF3(!^zOKQ!uC-&mYDCmLU&-P9R3Z@1 zS8~W;6V;n@WdP|@aC~}%pF4XU-?@}Zmq8lf?T#50Lt{bSK+=^1}dn16;DFh9An!AAZ_n*$R40RI!> zO%vvX0q4REKz17+)(;5p0dB(oa$QT}pwFB&AOQV-2t-1JTHnYnb;6 zA3vw@_1kx$`IK6+PpPzL8YYNF0_UDTJ2eM{jG|QBjr6SqejV(WBYp@jZho*|7T$ Date: Sat, 11 Apr 2026 10:50:51 +0000 Subject: [PATCH 158/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 63fa73809f..15e0b03840 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4030,3 +4022,11 @@ 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 From 399eb887b882276adb5e4574008d613e1437bc6a Mon Sep 17 00:00:00 2001 From: Samuka <47865393+Samuka-C@users.noreply.github.com> Date: Sat, 11 Apr 2026 15:50:13 -0300 Subject: [PATCH 159/247] Fix engi xenoborg sprite (#43557) 7 pixels --- .../Silicon/chassis.rsi/xenoborg_engi.png | Bin 1558 -> 1546 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Mobs/Silicon/chassis.rsi/xenoborg_engi.png b/Resources/Textures/Mobs/Silicon/chassis.rsi/xenoborg_engi.png index ac544fb5bd8077ea342244d0c39f8b3fb5ad85ae..28f2c33515f62c1cd529473446540e370c2e8c00 100644 GIT binary patch delta 1515 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 Date: Sat, 11 Apr 2026 20:51:02 +0200 Subject: [PATCH 160/247] Fixed the stacks of astro grass types and concrete (#43547) * Astro Grass stack fix * fix concrete splitting * CRITICAL FIX --- Resources/Prototypes/Entities/Objects/Misc/tiles.yml | 4 ++-- Resources/Prototypes/Stacks/Tiles/concrete.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 97865aef32..3e9a89c0d2 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -1658,7 +1658,7 @@ - Plating - FloorMowedAstroGrass - type: Stack - stackType: FloorTileAstroGrass + stackType: FloorTileMowedAstroGrass - type: entity id: FloorTileItemJungleAstroGrass @@ -1675,7 +1675,7 @@ - Plating - FloorJungleAstroGrass - type: Stack - stackType: FloorTileAstroGrass + stackType: FloorTileJungleAstroGrass - type: entity parent: FloorTileItemBase diff --git a/Resources/Prototypes/Stacks/Tiles/concrete.yml b/Resources/Prototypes/Stacks/Tiles/concrete.yml index 108a7f56e8..a32f03ed8d 100644 --- a/Resources/Prototypes/Stacks/Tiles/concrete.yml +++ b/Resources/Prototypes/Stacks/Tiles/concrete.yml @@ -4,7 +4,7 @@ parent: BaseTileStack id: FloorTileConcrete name: stack-concrete-tile - spawn: FloorTileItemGrayConcrete + spawn: FloorTileItemConcrete - type: stack parent: BaseTileStack From 9bf150c9f491c38e0ecfc9c0a30c36e8c0d8bb9c Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 Apr 2026 19:06:15 +0000 Subject: [PATCH 161/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 15e0b03840..fc5464b973 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4030,3 +4023,11 @@ 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 From 0d7ed2966846a336a3e359914de6561e6d3e653a Mon Sep 17 00:00:00 2001 From: BeatusCrow <141257446+BeatusCrow@users.noreply.github.com> Date: Sun, 12 Apr 2026 00:06:07 +0300 Subject: [PATCH 162/247] NPCs no longer target invisible entities (#43562) * Some changes for ninja * Apply suggestion from @Princess-Cheeseballs Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> --------- Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> --- .../NPC/Queries/Considerations/TargetIsVisibleCon.cs | 3 +++ Content.Server/NPC/Systems/NPCUtilitySystem.cs | 12 ++++++++++++ Resources/Prototypes/NPCs/utility_queries.yml | 2 ++ 3 files changed, 17 insertions(+) create mode 100644 Content.Server/NPC/Queries/Considerations/TargetIsVisibleCon.cs diff --git a/Content.Server/NPC/Queries/Considerations/TargetIsVisibleCon.cs b/Content.Server/NPC/Queries/Considerations/TargetIsVisibleCon.cs new file mode 100644 index 0000000000..387c9e9809 --- /dev/null +++ b/Content.Server/NPC/Queries/Considerations/TargetIsVisibleCon.cs @@ -0,0 +1,3 @@ +namespace Content.Server.NPC.Queries.Considerations; + +public sealed partial class TargetIsVisibleCon : UtilityConsideration; diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 1997945d55..b30f813a66 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -32,6 +32,8 @@ 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; @@ -57,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; @@ -286,6 +289,15 @@ public sealed class NPCUtilitySystem : EntitySystem return Math.Clamp(distance / radius, 0f, 1f); } + case TargetIsVisibleCon: + { + if (!TryComp(targetUid, out StealthComponent? stealth)) + return 1f; // If there is no StealthComponent, we see it. + + // Checking the visibility level + var visibility = _stealth.GetVisibility(targetUid, stealth); + return visibility >= 0.5f ? 1f : 0f; // Visibility threshold 0.5 + } case TargetAmmoCon: { if (!HasComp(targetUid)) diff --git a/Resources/Prototypes/NPCs/utility_queries.yml b/Resources/Prototypes/NPCs/utility_queries.yml index bb2bb08cc4..3c1d3fe5f2 100644 --- a/Resources/Prototypes/NPCs/utility_queries.yml +++ b/Resources/Prototypes/NPCs/utility_queries.yml @@ -88,6 +88,8 @@ considerations: - !type:TargetIsAliveCon curve: !type:BoolCurve + - !type:TargetIsVisibleCon + curve: !type:BoolCurve - !type:TargetDistanceCon curve: !type:PresetCurve preset: TargetDistance From a0156613d8ca15bf157f4d22e01b3295cfc4bf62 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 Apr 2026 21:21:42 +0000 Subject: [PATCH 163/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fc5464b973..eaf0a84c84 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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! @@ -4031,3 +4024,10 @@ 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 From a929269beb9dab0bd8fb2ec8acd02226fea32558 Mon Sep 17 00:00:00 2001 From: AndrewFenriz <78079974+AndrewFenriz@users.noreply.github.com> Date: Sun, 12 Apr 2026 00:19:40 +0300 Subject: [PATCH 164/247] Fix Cutter Machine rotation (#43494) cutter --- Resources/Prototypes/Entities/Structures/Machines/lathe.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index b740396326..a34bab04da 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -666,8 +666,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 From a9c72242f02628c48e4a653fa0202fae6a047334 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 11 Apr 2026 21:36:56 +0000 Subject: [PATCH 165/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index eaf0a84c84..530e2be409 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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! @@ -4031,3 +4024,10 @@ 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 From 376f16504c114a5ca45b6063665cbf713507e5d1 Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Sat, 11 Apr 2026 14:30:36 -0700 Subject: [PATCH 166/247] `Content.Shared` `EntityQuery` dependency injection refactor (#43498) * part 1 * second pass * final fix * last fix i swear * an even bigger genius * regex missed this * should be all of em? * oops * yeah * Revert "should be all of em?" This reverts commit 4dd54d1b9a76977b4cf8d6008358e4dacdee6eb1. --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../EntitySystems/MultipartMachineSystem.cs | 2 +- .../ExplosionSystem.Processing.cs | 1 - .../EntitySystems/MultipartMachineSystem.cs | 8 +-- .../Shuttles/Systems/ShuttleSystem.Impact.cs | 4 +- Content.Server/Weather/WeatherSystem.cs | 2 - .../ActionBlocker/ActionBlockerSystem.cs | 9 +--- .../Actions/ActionContainerSystem.cs | 4 +- Content.Shared/Actions/SharedActionsSystem.cs | 10 ++-- Content.Shared/Alert/AlertsSystem.cs | 4 +- .../Effects/SharedGravityAnomalySystem.cs | 16 +++--- Content.Shared/Anomaly/SharedAnomalySystem.cs | 5 +- .../EntitySystems/SharedAtmosphereSystem.cs | 4 +- .../Audio/SharedAmbientSoundSystem.cs | 4 +- Content.Shared/Bed/BedSystem.cs | 4 +- Content.Shared/Blocking/BlockingSystem.cs | 22 ++++----- Content.Shared/Body/BodySystem.cs | 7 +-- .../EntitySystems/SolutionTransferSystem.cs | 7 +-- .../Climbing/Systems/ClimbSystem.cs | 11 ++--- .../EntitySystems/AnchorableSystem.cs | 4 +- .../Damage/Systems/DamageContactsSystem.cs | 5 +- .../Damage/Systems/DamageableSystem.Events.cs | 3 -- .../Damage/Systems/DamageableSystem.cs | 4 +- .../Damage/Systems/SharedStaminaSystem.cs | 8 +-- .../DeviceLinking/SharedDeviceLinkSystem.cs | 5 +- .../DoAfter/SharedDoAfterSystem.Update.cs | 5 +- Content.Shared/Emp/SharedEmpSystem.cs | 5 +- .../Examine/ExamineSystemShared.Group.cs | 2 - Content.Shared/Examine/ExamineSystemShared.cs | 4 +- Content.Shared/Flash/SharedFlashSystem.cs | 8 ++- .../EntitySystems/SolutionDumpingSystem.cs | 5 +- Content.Shared/Fluids/SharedPuddleSystem.cs | 12 ++--- .../Friction/TileFrictionController.cs | 18 +++---- .../Friends/Systems/PettableFriendSystem.cs | 7 +-- .../Gravity/SharedGravitySystem.Shake.cs | 5 +- Content.Shared/Gravity/SharedGravitySystem.cs | 10 ++-- .../Implants/SharedImplanterSystem.cs | 8 +-- .../Interaction/SharedInteractionSystem.cs | 31 ++++-------- .../Item/ItemToggle/ItemToggleSystem.cs | 12 ++--- .../SharedMultipartMachineSystem.cs | 9 ---- Content.Shared/Maps/TurfSystem.cs | 9 ++-- .../Materials/OreSilo/SharedOreSiloSystem.cs | 4 +- .../Medical/Cryogenics/SharedCryoPodSystem.cs | 13 ++--- .../SuitSensors/SharedSuitSensorSystem.cs | 12 ++--- .../Metabolism/MetabolizerSystem.cs | 7 +-- Content.Shared/Mobs/Systems/MobStateSystem.cs | 3 +- .../Movement/Systems/SharedJetpackSystem.cs | 6 +-- .../Systems/SharedMobCollisionSystem.cs | 6 +-- .../Movement/Systems/SharedMoverController.cs | 49 ++++++------------- .../NPC/Systems/NpcFactionSystem.Exception.cs | 15 +++--- .../Ninja/Systems/SharedSpaceNinjaSystem.cs | 4 +- .../EntitySystems/ExaminableHungerSystem.cs | 5 +- .../EntitySystems/IngestionSystem.Utensils.cs | 4 +- .../Systems/SharedObjectivesSystem.cs | 13 +---- Content.Shared/Paper/PaperSystem.cs | 5 +- .../Controllers/SharedConveyorController.cs | 12 ++--- .../Pinpointer/SharedNavMapSystem.cs | 5 +- .../Power/EntitySystems/PowerStateSystem.cs | 9 +--- .../Systems/ProximityDetectionSystem.cs | 8 +-- Content.Shared/Random/Rules/NearbyAccess.cs | 8 ++- .../Random/Rules/NearbyComponents.cs | 5 +- .../RepulseAttract/RepulseAttractSystem.cs | 8 +-- Content.Shared/Rootable/RootableSystem.cs | 7 +-- .../Shuttles/Systems/SharedShuttleSystem.cs | 13 ++--- .../Silicons/Borgs/SharedBorgSystem.Module.cs | 4 +- .../StationAi/SharedStationAiSystem.cs | 7 +-- .../StationAi/StationAiVisionSystem.cs | 6 +-- Content.Shared/Slippery/SlidingSystem.cs | 4 +- Content.Shared/Slippery/SlipperySystem.cs | 10 ++-- .../Station/SharedStationSpawningSystem.cs | 17 ++----- .../Station/SharedStationSystem.Tracker.cs | 2 +- Content.Shared/Station/SharedStationSystem.cs | 6 +-- .../StatusEffectAlertSystem.cs | 4 +- .../StatusEffectNew/StatusEffectsSystem.cs | 7 +-- .../StepTrigger/Systems/StepTriggerSystem.cs | 13 ++--- .../Storage/EntitySystems/DumpableSystem.cs | 4 +- .../EntitySystems/MagnetPickupSystem.cs | 3 +- .../EntitySystems/SharedStorageSystem.cs | 12 ++--- .../Stunnable/SharedStunSystem.Knockdown.cs | 14 ++---- .../SubFloor/SharedSubFloorHideSystem.cs | 4 +- Content.Shared/Tag/TagSystem.cs | 4 +- .../Systems/SwapTeleporterSystem.cs | 6 +-- .../Systems/SharedTemperatureSystem.cs | 4 +- Content.Shared/Throwing/CatchableSystem.cs | 7 +-- Content.Shared/Throwing/ThrowingSystem.cs | 15 ++---- Content.Shared/Tiles/FloorTileSystem.cs | 10 ++-- .../Tools/Systems/WeldableSystem.cs | 5 +- .../Systems/HitscanBasicRaycastSystem.cs | 4 +- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 6 +-- .../Systems/SharedGunSystem.Revolver.cs | 3 +- Content.Shared/Weather/SharedWeatherSystem.cs | 12 +---- .../Whitelist/EntityWhitelistSystem.cs | 8 +-- .../Artifact/SharedXenoArtifactSystem.Node.cs | 7 +-- .../SharedXenoArtifactSystem.Unlock.cs | 4 +- .../Artifact/XAE/XAEShuffleSystem.cs | 10 +--- .../Artifact/XAT/BaseQueryUpdateXATSystem.cs | 10 +--- .../Artifact/XAT/BaseXATSystem.cs | 10 +--- .../Artifact/XAT/XATDeathSystem.cs | 4 +- 97 files changed, 250 insertions(+), 520 deletions(-) diff --git a/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs b/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs index 4919a5e8f2..0f583898de 100644 --- a/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs +++ b/Content.Client/Machines/EntitySystems/MultipartMachineSystem.cs @@ -58,7 +58,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem var entityCoords = new EntityCoordinates(ent.Owner, part.Offset); var ghostEnt = Spawn(_ghostPrototype, entityCoords); - if (!XformQuery.TryGetComponent(ghostEnt, out var xform)) + if (!TryComp(ghostEnt, out TransformComponent? xform)) break; xform.LocalRotation = part.Rotation; diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index ab35f729db..ab65c04633 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -494,7 +494,6 @@ public sealed partial class ExplosionSystem dir, physics, xform, - _projectileQuery, throwForce); } } diff --git a/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs b/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs index 04903241e3..7435a7706e 100644 --- a/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs +++ b/Content.Server/Machines/EntitySystems/MultipartMachineSystem.cs @@ -75,7 +75,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem public bool Rescan(Entity ent, EntityUid? user = null) { // Get all required transform information to start looking for the other parts based on their offset - if (!XformQuery.TryGetComponent(ent.Owner, out var xform) || !xform.Anchored) + if (!TryComp(ent.Owner, out TransformComponent? xform) || !xform.Anchored) return false; var gridUid = xform.GridUid; @@ -210,7 +210,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem { // If anchored, perform a rescan of this machine when the component starts so we can immediately // jump to an assembled state if needed. - if (XformQuery.TryGetComponent(ent.Owner, out var xform) && xform.Anchored) + if (TryComp(ent.Owner, out TransformComponent? xform) && xform.Anchored) Rescan(ent); } @@ -240,7 +240,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem private void OnPartConstructionNodeChanged(Entity ent, ref AfterConstructionChangeEntityEvent args) { - if (!XformQuery.TryGetComponent(ent.Owner, out var constructXform)) + if (!TryComp(ent.Owner, out TransformComponent? constructXform)) return; _lookupSystem.GetEntitiesInRange(constructXform.Coordinates, MaximumRange, _entitiesInRange); @@ -283,7 +283,7 @@ public sealed class MultipartMachineSystem : SharedMultipartMachineSystem // We're anchoring some construction, we have no idea which machine this might be for // so we have to just check everyone in range and perform a rescan. - if (!XformQuery.TryGetComponent(ent.Owner, out var constructXform)) + if (!TryComp(ent.Owner, out TransformComponent? constructXform)) return; _lookupSystem.GetEntitiesInRange(constructXform.Coordinates, MaximumRange, _entitiesInRange); diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs index d50eea53ee..cbc48b3cca 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.Impact.cs @@ -256,7 +256,7 @@ public sealed partial class ShuttleSystem if (direction.LengthSquared() > minsq) { _stuns.TryCrawling(ent.Owner, knockdownTime); - _throwing.TryThrow(ent, direction, ent.Comp, Transform(ent), _projQuery, direction.Length(), playSound: false); + _throwing.TryThrow(ent, direction, ent.Comp, Transform(ent), direction.Length(), playSound: false); } else { @@ -414,7 +414,7 @@ public sealed partial class ShuttleSystem else { var direction = throwDirection * tileData.DistanceFactor; - _throwing.TryThrow(localEnt, direction, physics, localEnt.Comp, _projQuery, direction.Length(), playSound: false); + _throwing.TryThrow(localEnt, direction, physics, localEnt.Comp, direction.Length(), playSound: false); } } diff --git a/Content.Server/Weather/WeatherSystem.cs b/Content.Server/Weather/WeatherSystem.cs index 58e1eb2b98..b3a0bea882 100644 --- a/Content.Server/Weather/WeatherSystem.cs +++ b/Content.Server/Weather/WeatherSystem.cs @@ -10,8 +10,6 @@ public sealed class WeatherSystem : SharedWeatherSystem public override void Initialize() { - base.Initialize(); - SubscribeLocalEvent(OnCompInit); SubscribeLocalEvent(OnCompShutdown); } diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index 485dc89580..18c85c3aaa 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -23,14 +23,7 @@ namespace Content.Shared.ActionBlocker { [Dependency] private readonly SharedContainerSystem _container = default!; - private EntityQuery _complexInteractionQuery; - - public override void Initialize() - { - base.Initialize(); - - _complexInteractionQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _complexInteractionQuery = default!; // These two methods should probably both live in SharedMoverController // but they're called in a million places and I'm not doing that diff --git a/Content.Shared/Actions/ActionContainerSystem.cs b/Content.Shared/Actions/ActionContainerSystem.cs index 534f0d3ee7..642131a365 100644 --- a/Content.Shared/Actions/ActionContainerSystem.cs +++ b/Content.Shared/Actions/ActionContainerSystem.cs @@ -23,14 +23,12 @@ public sealed class ActionContainerSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedMindSystem _mind = default!; - private EntityQuery _query; + [Dependency] private readonly EntityQuery _query = default!; public override void Initialize() { base.Initialize(); - _query = GetEntityQuery(); - SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnEntityRemoved); diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 642d942fb6..db7b019e55 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); diff --git a/Content.Shared/Alert/AlertsSystem.cs b/Content.Shared/Alert/AlertsSystem.cs index 834a22b8de..5c751b938f 100644 --- a/Content.Shared/Alert/AlertsSystem.cs +++ b/Content.Shared/Alert/AlertsSystem.cs @@ -11,15 +11,13 @@ public abstract class AlertsSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - private EntityQuery _alertsQuery; + [Dependency] private readonly EntityQuery _alertsQuery = default!; private FrozenDictionary, AlertPrototype> _typeToAlert = default!; public override void Initialize() { base.Initialize(); - _alertsQuery = GetEntityQuery(); - SubscribeLocalEvent(HandleComponentStartup); SubscribeLocalEvent(HandleComponentShutdown); SubscribeLocalEvent(OnPlayerAttached); diff --git a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs index 4c3cdb0146..0b2c4b7a16 100644 --- a/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs +++ b/Content.Shared/Anomaly/Effects/SharedGravityAnomalySystem.cs @@ -17,6 +17,8 @@ public abstract class SharedGravityAnomalySystem : EntitySystem [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly EntityQuery _physQuery = default!; + /// public override void Initialize() { @@ -30,17 +32,15 @@ public abstract class SharedGravityAnomalySystem : EntitySystem var range = component.MaxThrowRange * args.Severity * args.PowerModifier; var strength = component.MaxThrowStrength * args.Severity * args.PowerModifier; var lookup = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic | LookupFlags.Sundries); - var xformQuery = GetEntityQuery(); - var worldPos = _xform.GetWorldPosition(xform, xformQuery); - var physQuery = GetEntityQuery(); + var worldPos = _xform.GetWorldPosition(xform); foreach (var ent in lookup) { - if (physQuery.TryGetComponent(ent, out var phys) + if (_physQuery.TryGetComponent(ent, out var phys) && (phys.CollisionMask & (int) CollisionGroup.GhostImpassable) != 0) continue; - var foo = _xform.GetWorldPosition(ent, xformQuery) - worldPos; + var foo = _xform.GetWorldPosition(ent) - worldPos; _throwing.TryThrow(ent, foo * 10, strength, uid, 0); } } @@ -64,16 +64,14 @@ public abstract class SharedGravityAnomalySystem : EntitySystem var range = component.MaxThrowRange * 2 * args.PowerModifier; var strength = component.MaxThrowStrength * 2 * args.PowerModifier; var lookup = _lookup.GetEntitiesInRange(uid, range, LookupFlags.Dynamic | LookupFlags.Sundries); - var xformQuery = GetEntityQuery(); - var physQuery = GetEntityQuery(); foreach (var ent in lookup) { - if (physQuery.TryGetComponent(ent, out var phys) + if (_physQuery.TryGetComponent(ent, out var phys) && (phys.CollisionMask & (int) CollisionGroup.GhostImpassable) != 0) continue; - var foo = _xform.GetWorldPosition(ent, xformQuery) - worldPos; + var foo = _xform.GetWorldPosition(ent) - worldPos; _throwing.TryThrow(ent, foo * 5, strength, uid, 0); } } diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index b63b25fa5d..03aead97c5 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -38,6 +38,8 @@ public abstract class SharedAnomalySystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly EntityQuery _physQuery = default!; + public override void Initialize() { base.Initialize(); @@ -409,7 +411,6 @@ public abstract class SharedAnomalySystem : EntitySystem if (tilerefs.Count == 0) return null; - var physQuery = GetEntityQuery(); var resultList = new List(); while (resultList.Count < amount) { @@ -437,7 +438,7 @@ public abstract class SharedAnomalySystem : EntitySystem var valid = true; foreach (var ent in _map.GetAnchoredEntities(xform.GridUid.Value, grid, tileref.GridIndices)) { - if (!physQuery.TryGetComponent(ent, out var body)) + if (!_physQuery.TryGetComponent(ent, out var body)) continue; if (body.BodyType != BodyType.Static || diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.cs index 04f0221994..47e3219efc 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.cs @@ -14,7 +14,7 @@ public abstract partial class SharedAtmosphereSystem : EntitySystem [Dependency] private readonly SharedInternalsSystem _internals = default!; [Dependency] protected readonly SharedTransformSystem XformSystem = default!; - private EntityQuery _internalsQuery; + [Dependency] private readonly EntityQuery _internalsQuery = default!; /// /// The length to pre-allocate list/dicts of delta pressure entities on a . @@ -25,8 +25,6 @@ public abstract partial class SharedAtmosphereSystem : EntitySystem { base.Initialize(); - _internalsQuery = GetEntityQuery(); - InitializeBreathTool(); InitializeGases(); InitializeCVars(); diff --git a/Content.Shared/Audio/SharedAmbientSoundSystem.cs b/Content.Shared/Audio/SharedAmbientSoundSystem.cs index 0a52b7c58e..fd5a474a70 100644 --- a/Content.Shared/Audio/SharedAmbientSoundSystem.cs +++ b/Content.Shared/Audio/SharedAmbientSoundSystem.cs @@ -6,15 +6,13 @@ namespace Content.Shared.Audio; public abstract class SharedAmbientSoundSystem : EntitySystem { - private EntityQuery _query; + [Dependency] private readonly EntityQuery _query = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(GetCompState); SubscribeLocalEvent(HandleCompState); - - _query = GetEntityQuery(); } public virtual void SetAmbience(EntityUid uid, bool value, AmbientSoundComponent? ambience = null) diff --git a/Content.Shared/Bed/BedSystem.cs b/Content.Shared/Bed/BedSystem.cs index 6f62d7b34e..e8f8ffef29 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 sealed class BedSystem : EntitySystem SubscribeLocalEvent(OnStasisEmagged); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnStasisGetMetabolicMultiplier); - - _sleepingQuery = GetEntityQuery(); } private void OnHealMapInit(Entity ent, ref MapInitEvent args) diff --git a/Content.Shared/Blocking/BlockingSystem.cs b/Content.Shared/Blocking/BlockingSystem.cs index 2e17485bde..d0e3bef52b 100644 --- a/Content.Shared/Blocking/BlockingSystem.cs +++ b/Content.Shared/Blocking/BlockingSystem.cs @@ -33,6 +33,11 @@ public sealed partial class BlockingSystem : EntitySystem [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly TurfSystem _turf = default!; + [Dependency] private readonly EntityQuery _blockQuery = default!; + [Dependency] private readonly EntityQuery _handQuery = default!; + [Dependency] private readonly EntityQuery _mobQuery = default!; + [Dependency] private readonly EntityQuery _userQuery = default!; + public override void Initialize() { base.Initialize(); @@ -91,10 +96,7 @@ public sealed partial class BlockingSystem : EntitySystem if (args.Handled) return; - var blockQuery = GetEntityQuery(); - var handQuery = GetEntityQuery(); - - if (!handQuery.TryGetComponent(args.Performer, out var hands)) + if (!_handQuery.TryGetComponent(args.Performer, out var hands)) return; var shields = _handsSystem.EnumerateHeld((args.Performer, hands)).ToArray(); @@ -104,7 +106,7 @@ public sealed partial class BlockingSystem : EntitySystem if (shield == uid) continue; - if (blockQuery.TryGetComponent(shield, out var otherBlockComp) && otherBlockComp.IsBlocking) + if (_blockQuery.TryGetComponent(shield, out var otherBlockComp) && otherBlockComp.IsBlocking) { CantBlockError(args.Performer); return; @@ -170,10 +172,9 @@ public sealed partial class BlockingSystem : EntitySystem if (playerTileRef != null) { var intersecting = _lookup.GetLocalEntitiesIntersecting(playerTileRef.Value, 0f); - var mobQuery = GetEntityQuery(); foreach (var uid in intersecting) { - if (uid != user && mobQuery.HasComponent(uid)) + if (uid != user && _mobQuery.HasComponent(uid)) { TooCloseError(user); return false; @@ -271,17 +272,14 @@ public sealed partial class BlockingSystem : EntitySystem if (component.IsBlocking) StopBlocking(uid, component, user); - var userQuery = GetEntityQuery(); - var handQuery = GetEntityQuery(); - - if (!handQuery.TryGetComponent(user, out var hands)) + if (!_handQuery.TryGetComponent(user, out var hands)) return; var shields = _handsSystem.EnumerateHeld((user, hands)).ToArray(); foreach (var shield in shields) { - if (HasComp(shield) && userQuery.TryGetComponent(user, out var blockingUserComponent)) + if (HasComp(shield) && _userQuery.TryGetComponent(user, out var blockingUserComponent)) { blockingUserComponent.BlockingItem = shield; return; diff --git a/Content.Shared/Body/BodySystem.cs b/Content.Shared/Body/BodySystem.cs index 4cd5a4e792..3f01b4e2ba 100644 --- a/Content.Shared/Body/BodySystem.cs +++ b/Content.Shared/Body/BodySystem.cs @@ -18,8 +18,8 @@ public sealed partial class BodySystem : EntitySystem { [Dependency] private readonly SharedContainerSystem _container = default!; - private EntityQuery _bodyQuery; - private EntityQuery _organQuery; + [Dependency] private readonly EntityQuery _bodyQuery = default!; + [Dependency] private readonly EntityQuery _organQuery = default!; public override void Initialize() { @@ -33,9 +33,6 @@ public sealed partial class BodySystem : EntitySystem SubscribeLocalEvent(OnBodyEntInserted); SubscribeLocalEvent(OnBodyEntRemoved); - _bodyQuery = GetEntityQuery(); - _organQuery = GetEntityQuery(); - InitializeRelay(); } diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs index 8fe17368cc..3928ff7179 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs @@ -23,8 +23,8 @@ public sealed class SolutionTransferSystem : EntitySystem [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - private EntityQuery _refillableQuery; - private EntityQuery _drainableQuery; + [Dependency] private readonly EntityQuery _refillableQuery = default!; + [Dependency] private readonly EntityQuery _drainableQuery = default!; /// /// Default transfer amounts for the set-transfer verb. @@ -40,9 +40,6 @@ public sealed class SolutionTransferSystem : EntitySystem SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnSolutionDrainTransferDoAfter); SubscribeLocalEvent(OnSolutionFillTransferDoAfter); - - _refillableQuery = GetEntityQuery(); - _drainableQuery = GetEntityQuery(); } private void AddSetTransferVerbs(Entity ent, ref GetVerbsEvent args) diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index 9cc0a55ce1..257817394e 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -42,21 +42,18 @@ public sealed partial class ClimbSystem : VirtualController [Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + [Dependency] private readonly EntityQuery _climbableQuery = default!; + [Dependency] private readonly EntityQuery _fixturesQuery = default!; + [Dependency] private readonly EntityQuery _xformQuery = default!; + private const string ClimbingFixtureName = "climb"; private const int ClimbingCollisionGroup = (int) (CollisionGroup.TableLayer | CollisionGroup.LowImpassable); - private EntityQuery _climbableQuery; - private EntityQuery _fixturesQuery; - private EntityQuery _xformQuery; public override void Initialize() { base.Initialize(); - _climbableQuery = GetEntityQuery(); - _fixturesQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - SubscribeLocalEvent(OnMoveAttempt); SubscribeLocalEvent(OnParentChange); SubscribeLocalEvent(OnDoAfter); diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index b290aae404..495dddfe69 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -34,7 +34,7 @@ public sealed partial class AnchorableSystem : EntitySystem [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - private EntityQuery _physicsQuery; + [Dependency] private readonly EntityQuery _physicsQuery = default!; public readonly ProtoId Unstackable = "Unstackable"; @@ -42,8 +42,6 @@ public sealed partial class AnchorableSystem : EntitySystem { base.Initialize(); - _physicsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnInteractUsing, before: new[] { typeof(ItemSlotsSystem) }, after: new[] { typeof(SharedConstructionSystem) }); SubscribeLocalEvent(OnAnchorComplete); diff --git a/Content.Shared/Damage/Systems/DamageContactsSystem.cs b/Content.Shared/Damage/Systems/DamageContactsSystem.cs index 9a53d83eb2..820fc3c90b 100644 --- a/Content.Shared/Damage/Systems/DamageContactsSystem.cs +++ b/Content.Shared/Damage/Systems/DamageContactsSystem.cs @@ -14,6 +14,8 @@ public sealed class DamageContactsSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly EntityQuery _damageQuery = default!; + public override void Initialize() { base.Initialize(); @@ -45,13 +47,12 @@ public sealed class DamageContactsSystem : EntitySystem if (!TryComp(otherUid, out var body)) return; - var damageQuery = GetEntityQuery(); foreach (var ent in _physics.GetContactingEntities(otherUid, body)) { if (ent == uid) continue; - if (damageQuery.HasComponent(ent)) + if (_damageQuery.HasComponent(ent)) return; } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.Events.cs b/Content.Shared/Damage/Systems/DamageableSystem.Events.cs index c9cf625cbc..36f997c5a9 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.Events.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.Events.cs @@ -23,9 +23,6 @@ public sealed partial class DamageableSystem SubscribeLocalEvent(DamageableHandleState); SubscribeLocalEvent(DamageableGetState); - _appearanceQuery = GetEntityQuery(); - _damageableQuery = GetEntityQuery(); - // Damage modifier CVars are updated and stored here to be queried in other systems. // Note that certain modifiers requires reloading the guidebook. Subs.CVar( diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 2d707568f0..fd8564cf3c 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -23,8 +23,8 @@ public sealed partial class DamageableSystem : EntitySystem [Dependency] private readonly SharedChemistryGuideDataSystem _chemistryGuideData = default!; [Dependency] private readonly SharedExplosionSystem _explosion = default!; - private EntityQuery _appearanceQuery; - private EntityQuery _damageableQuery; + [Dependency] private readonly EntityQuery _appearanceQuery = default!; + [Dependency] private readonly EntityQuery _damageableQuery = default!; public float UniversalAllDamageModifier { get; private set; } = 1f; public float UniversalAllHealModifier { get; private set; } = 1f; diff --git a/Content.Shared/Damage/Systems/SharedStaminaSystem.cs b/Content.Shared/Damage/Systems/SharedStaminaSystem.cs index ca030d5e5d..450acdd81a 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 @@ public abstract partial class SharedStaminaSystem : EntitySystem if (ev.Cancelled) return; - var stamQuery = GetEntityQuery(); var toHit = new List<(EntityUid Entity, StaminaComponent Component)>(); // Split stamina damage between all eligible targets. foreach (var ent in args.HitEntities) { - if (!stamQuery.TryGetComponent(ent, out var stam)) + if (!_stamQuery.TryGetComponent(ent, out var stam)) continue; toHit.Add((ent, stam)); @@ -351,14 +352,13 @@ public abstract partial class SharedStaminaSystem : EntitySystem { base.Update(frameTime); - var stamQuery = GetEntityQuery(); var query = EntityQueryEnumerator(); var curTime = Timing.CurTime; while (query.MoveNext(out var uid, out _)) { // Just in case we have active but not stamina we'll check and account for it. - if (!stamQuery.TryGetComponent(uid, out var comp) || + if (!_stamQuery.TryComp(uid, out var comp) || comp.StaminaDamage <= 0f && !comp.Critical) { RemComp(uid); diff --git a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs index 74bbc259c6..9e4e964f0a 100644 --- a/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs +++ b/Content.Shared/DeviceLinking/SharedDeviceLinkSystem.cs @@ -18,6 +18,8 @@ public abstract class SharedDeviceLinkSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly EntityQuery _deviceLinkSinkQuery = default!; + public const string InvokedPort = "link_port"; /// @@ -82,10 +84,9 @@ public abstract class SharedDeviceLinkSystem : EntitySystem /// private void OnSourceRemoved(Entity source, ref ComponentRemove args) { - var query = GetEntityQuery(); foreach (var sinkUid in source.Comp.LinkedPorts.Keys) { - if (query.TryGetComponent(sinkUid, out var sink)) + if (_deviceLinkSinkQuery.TryGetComponent(sinkUid, out var sink)) RemoveSinkFromSourceInternal(source, sinkUid, source, sink); else Log.Error($"Device source {ToPrettyString(source)} links to invalid entity: {ToPrettyString(sinkUid)}"); diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index 0b5902b5e3..5e4b742e10 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -17,7 +17,8 @@ public abstract partial class SharedDoAfterSystem : EntitySystem [Dependency] private readonly SharedGravitySystem _gravity = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] EntityQuery _handsQuery = default!; + + [Dependency] private readonly EntityQuery _handsQuery = default!; private DoAfter[] _doAfters = Array.Empty(); @@ -26,8 +27,6 @@ public abstract partial class SharedDoAfterSystem : EntitySystem base.Update(frameTime); var time = GameTiming.CurTime; - var xformQuery = GetEntityQuery(); - var handsQuery = GetEntityQuery(); var enumerator = EntityQueryEnumerator(); while (enumerator.MoveNext(out var uid, out var active, out var comp)) diff --git a/Content.Shared/Emp/SharedEmpSystem.cs b/Content.Shared/Emp/SharedEmpSystem.cs index e1ef05c8ce..0c902798b1 100644 --- a/Content.Shared/Emp/SharedEmpSystem.cs +++ b/Content.Shared/Emp/SharedEmpSystem.cs @@ -18,8 +18,9 @@ public abstract class SharedEmpSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EntityQuery _resistanceQuery = default!; + private HashSet _entSet = new(); - private EntityQuery _resistanceQuery; public override void Initialize() { @@ -30,8 +31,6 @@ public abstract class SharedEmpSystem : EntitySystem SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnResistEmpAttempt); - - _resistanceQuery = GetEntityQuery(); } public static readonly EntProtoId EmpPulseEffectPrototype = "EffectEmpPulse"; diff --git a/Content.Shared/Examine/ExamineSystemShared.Group.cs b/Content.Shared/Examine/ExamineSystemShared.Group.cs index e220d3ef77..7c231fe157 100644 --- a/Content.Shared/Examine/ExamineSystemShared.Group.cs +++ b/Content.Shared/Examine/ExamineSystemShared.Group.cs @@ -13,8 +13,6 @@ namespace Content.Shared.Examine base.Initialize(); SubscribeLocalEvent>(OnGroupExamineVerb); - - _ghostQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 7c063f6ce9..8af9a91e5c 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -21,6 +21,8 @@ namespace Content.Shared.Examine [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] protected readonly MobStateSystem MobStateSystem = default!; + [Dependency] private readonly EntityQuery _ghostQuery = default!; + public const float MaxRaycastRange = 100; /// @@ -43,8 +45,6 @@ namespace Content.Shared.Examine protected const float ExamineBlurrinessMult = 2.5f; - private EntityQuery _ghostQuery; - /// /// Creates a new examine tooltip with arbitrary info. /// diff --git a/Content.Shared/Flash/SharedFlashSystem.cs b/Content.Shared/Flash/SharedFlashSystem.cs index da43284bee..30f11efdeb 100644 --- a/Content.Shared/Flash/SharedFlashSystem.cs +++ b/Content.Shared/Flash/SharedFlashSystem.cs @@ -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. @@ -63,9 +64,6 @@ public abstract class SharedFlashSystem : EntitySystem SubscribeLocalEvent(OnTemporaryBlindnessFlashAttempt); Subs.SubscribeWithRelay(OnFlashImmunityFlashAttempt, held: false); SubscribeLocalEvent(OnExamine); - - _statusEffectsQuery = GetEntityQuery(); - _damagedByFlashingQuery = GetEntityQuery(); } private void OnFlashMeleeHit(Entity ent, ref MeleeHitEvent args) diff --git a/Content.Shared/Fluids/EntitySystems/SolutionDumpingSystem.cs b/Content.Shared/Fluids/EntitySystems/SolutionDumpingSystem.cs index a6f4a8b31f..07e2938585 100644 --- a/Content.Shared/Fluids/EntitySystems/SolutionDumpingSystem.cs +++ b/Content.Shared/Fluids/EntitySystems/SolutionDumpingSystem.cs @@ -28,7 +28,7 @@ public sealed class SolutionDumpingSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedSolutionContainerSystem _solContainer = default!; - private EntityQuery _dumpQuery; + [Dependency] private readonly EntityQuery _dumpQuery = default!; public override void Initialize() { @@ -39,9 +39,6 @@ public sealed class SolutionDumpingSystem : EntitySystem SubscribeLocalEvent(OnDrainableDragged); SubscribeLocalEvent(OnDrainedToDumpableDragged); - - // We use queries for these since CanDropDraggedEvent gets called pretty rapidly - _dumpQuery = GetEntityQuery(); } private void OnDrainableCanDrag(Entity ent, ref CanDragEvent args) diff --git a/Content.Shared/Fluids/SharedPuddleSystem.cs b/Content.Shared/Fluids/SharedPuddleSystem.cs index 56ba5e8441..18cae762f8 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.cs @@ -44,6 +44,10 @@ public abstract partial class SharedPuddleSystem : EntitySystem [Dependency] private readonly TileFrictionController _tile = default!; [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly EntityQuery _stepTriggerQuery = default!; + [Dependency] private readonly EntityQuery _reactiveQuery = default!; + [Dependency] private readonly EntityQuery _evaporationQuery = default!; + private ProtoId[] _standoutReagents = []; /// @@ -57,10 +61,6 @@ public abstract partial class SharedPuddleSystem : EntitySystem // loses & then gains reagents in a single tick. private HashSet _deletionQueue = []; - private EntityQuery _stepTriggerQuery; - private EntityQuery _reactiveQuery; - private EntityQuery _evaporationQuery; - public override void Initialize() { base.Initialize(); @@ -75,10 +75,6 @@ public abstract partial class SharedPuddleSystem : EntitySystem SubscribeLocalEvent(OnPrototypesReloaded); - _stepTriggerQuery = GetEntityQuery(); - _reactiveQuery = GetEntityQuery(); - _evaporationQuery = GetEntityQuery(); - CacheStandsout(); InitializeSpillable(); } diff --git a/Content.Shared/Friction/TileFrictionController.cs b/Content.Shared/Friction/TileFrictionController.cs index 71c51c55f1..71e269b98c 100644 --- a/Content.Shared/Friction/TileFrictionController.cs +++ b/Content.Shared/Friction/TileFrictionController.cs @@ -28,14 +28,14 @@ namespace Content.Shared.Friction [Dependency] private readonly SharedMoverController _mover = default!; [Dependency] private readonly SharedMapSystem _map = default!; - private EntityQuery _frictionQuery; - private EntityQuery _pullerQuery; - private EntityQuery _pullableQuery; - private EntityQuery _gridQuery; + [Dependency] private readonly EntityQuery _frictionQuery = default!; + [Dependency] private readonly EntityQuery _pullerQuery = default!; + [Dependency] private readonly EntityQuery _pullableQuery = default!; + [Dependency] private readonly EntityQuery _gridQuery = default!; // For debug purposes only - private EntityQuery _moverQuery; - private EntityQuery _blockMoverQuery; + [Dependency] private readonly EntityQuery _moverQuery = default!; + [Dependency] private readonly EntityQuery _blockMoverQuery = default!; private float _frictionModifier; private float _minDamping; @@ -50,12 +50,6 @@ namespace Content.Shared.Friction Subs.CVar(_configManager, CCVars.MinFriction, value => _minDamping = value, true); Subs.CVar(_configManager, CCVars.AirFriction, value => _airDamping = value, true); Subs.CVar(_configManager, CCVars.OffgridFriction, value => _offGridDamping = value, true); - _frictionQuery = GetEntityQuery(); - _pullerQuery = GetEntityQuery(); - _pullableQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - _moverQuery = GetEntityQuery(); - _blockMoverQuery = GetEntityQuery(); } public override void UpdateBeforeSolve(bool prediction, float frameTime) diff --git a/Content.Shared/Friends/Systems/PettableFriendSystem.cs b/Content.Shared/Friends/Systems/PettableFriendSystem.cs index 6e41f4bdea..104a08c7b5 100644 --- a/Content.Shared/Friends/Systems/PettableFriendSystem.cs +++ b/Content.Shared/Friends/Systems/PettableFriendSystem.cs @@ -14,16 +14,13 @@ public sealed class PettableFriendSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - private EntityQuery _exceptionQuery; - private EntityQuery _useDelayQuery; + [Dependency] private readonly EntityQuery _exceptionQuery = default!; + [Dependency] private readonly EntityQuery _useDelayQuery = default!; public override void Initialize() { base.Initialize(); - _exceptionQuery = GetEntityQuery(); - _useDelayQuery = GetEntityQuery(); - SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnRehydrated); } diff --git a/Content.Shared/Gravity/SharedGravitySystem.Shake.cs b/Content.Shared/Gravity/SharedGravitySystem.Shake.cs index 41cf616cc4..a1dbed382c 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.Shake.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.Shake.cs @@ -2,20 +2,21 @@ namespace Content.Shared.Gravity; public abstract partial class SharedGravitySystem { + [Dependency] private readonly EntityQuery _gravityQuery = default!; + protected const float GravityKick = 100.0f; protected const float ShakeCooldown = 0.2f; private void UpdateShake() { var curTime = Timing.CurTime; - var gravityQuery = GetEntityQuery(); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp)) { if (comp.NextShake <= curTime) { - if (comp.ShakeTimes == 0 || !gravityQuery.TryGetComponent(uid, out var gravity)) + if (comp.ShakeTimes == 0 || !_gravityQuery.TryGetComponent(uid, out var gravity)) { RemCompDeferred(uid); continue; diff --git a/Content.Shared/Gravity/SharedGravitySystem.cs b/Content.Shared/Gravity/SharedGravitySystem.cs index d9a0a70d94..50eb674817 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.cs @@ -19,9 +19,9 @@ public abstract partial class SharedGravitySystem : EntitySystem public static readonly ProtoId WeightlessAlert = "Weightless"; - protected EntityQuery GravityQuery; - private EntityQuery _weightlessQuery; - private EntityQuery _physicsQuery; + [Dependency] protected readonly EntityQuery GravityQuery = default!; + [Dependency] private readonly EntityQuery _weightlessQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; public override void Initialize() { @@ -43,10 +43,6 @@ public abstract partial class SharedGravitySystem : EntitySystem // Impulse SubscribeLocalEvent(OnShooterImpulse); SubscribeLocalEvent(OnThrowerImpulse); - - GravityQuery = GetEntityQuery(); - _weightlessQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); } public override void Update(float frameTime) diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index 82cc4924fb..9622e814c5 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -29,6 +29,8 @@ public abstract class SharedImplanterSystem : EntitySystem [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly EntityQuery _implantCompQuery = default!; + public override void Initialize() { base.Initialize(); @@ -193,13 +195,11 @@ public abstract class SharedImplanterSystem : EntitySystem if (_container.TryGetContainer(target, ImplanterComponent.ImplantSlotId, out var implantContainer)) { - var implantCompQuery = GetEntityQuery(); - if (component.AllowDeimplantAll) { foreach (var implant in implantContainer.ContainedEntities) { - if (!implantCompQuery.TryGetComponent(implant, out var implantComp)) + if (!_implantCompQuery.TryGetComponent(implant, out var implantComp)) continue; //Don't remove a permanent implant and look for the next that can be drawn @@ -234,7 +234,7 @@ public abstract class SharedImplanterSystem : EntitySystem } } - if (implant != null && implantCompQuery.TryGetComponent(implant, out var implantComp)) + if (implant != null && _implantCompQuery.TryGetComponent(implant, out var implantComp)) { //Don't remove a permanent implant if (!_container.CanRemove(implant.Value, implantContainer)) diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 88d42b8a28..f5deabdf7e 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -75,16 +75,16 @@ namespace Content.Shared.Interaction [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; - private EntityQuery _ignoreUiRangeQuery; - private EntityQuery _fixtureQuery; - private EntityQuery _itemQuery; - private EntityQuery _physicsQuery; - private EntityQuery _handsQuery; - private EntityQuery _relayQuery; - private EntityQuery _combatQuery; - private EntityQuery _wallMountQuery; - private EntityQuery _delayQuery; - private EntityQuery _uiQuery; + [Dependency] private readonly EntityQuery _ignoreUiRangeQuery = default!; + [Dependency] private readonly EntityQuery _fixtureQuery = default!; + [Dependency] private readonly EntityQuery _itemQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + [Dependency] private readonly EntityQuery _handsQuery = default!; + [Dependency] private readonly EntityQuery _relayQuery = default!; + [Dependency] private readonly EntityQuery _combatQuery = default!; + [Dependency] private readonly EntityQuery _wallMountQuery = default!; + [Dependency] private readonly EntityQuery _delayQuery = default!; + [Dependency] private readonly EntityQuery _uiQuery = default!; /// /// The collision mask used by default for @@ -103,17 +103,6 @@ namespace Content.Shared.Interaction public override void Initialize() { - _ignoreUiRangeQuery = GetEntityQuery(); - _fixtureQuery = GetEntityQuery(); - _itemQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _handsQuery = GetEntityQuery(); - _relayQuery = GetEntityQuery(); - _combatQuery = GetEntityQuery(); - _wallMountQuery = GetEntityQuery(); - _delayQuery = GetEntityQuery(); - _uiQuery = GetEntityQuery(); - SubscribeLocalEvent(HandleUserInterfaceRangeCheck); // TODO make this a broadcast event subscription again when engine has updated. diff --git a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs index c260529ded..2315961d97 100644 --- a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs @@ -26,14 +26,12 @@ public sealed class ItemToggleSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - private EntityQuery _query; + [Dependency] private readonly EntityQuery _itemToggleQuery = default!; public override void Initialize() { base.Initialize(); - _query = GetEntityQuery(); - SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(TurnOffOnUnwielded); @@ -121,7 +119,7 @@ public sealed class ItemToggleSystem : EntitySystem /// Same as public bool Toggle(Entity ent, EntityUid? user = null, bool predicted = true, bool showPopup = true) { - if (!_query.Resolve(ent, ref ent.Comp, false)) + if (!_itemToggleQuery.Resolve(ent, ref ent.Comp, false)) return false; return TrySetActive(ent, !ent.Comp.Activated, user, predicted, showPopup); @@ -144,7 +142,7 @@ public sealed class ItemToggleSystem : EntitySystem /// public bool TryActivate(Entity ent, EntityUid? user = null, bool predicted = true, bool showPopup = true) { - if (!_query.Resolve(ent, ref ent.Comp, false)) + if (!_itemToggleQuery.Resolve(ent, ref ent.Comp, false)) return false; var uid = ent.Owner; @@ -191,7 +189,7 @@ public sealed class ItemToggleSystem : EntitySystem /// public bool TryDeactivate(Entity ent, EntityUid? user = null, bool predicted = true, bool showPopup = true) { - if (!_query.Resolve(ent, ref ent.Comp, false)) + if (!_itemToggleQuery.Resolve(ent, ref ent.Comp, false)) return false; var uid = ent.Owner; @@ -322,7 +320,7 @@ public sealed class ItemToggleSystem : EntitySystem public bool IsActivated(Entity ent) { - if (!_query.Resolve(ent, ref ent.Comp, false)) + if (!_itemToggleQuery.Resolve(ent, ref ent.Comp, false)) return true; // assume always activated if no component return ent.Comp.Activated; diff --git a/Content.Shared/Machines/EntitySystems/SharedMultipartMachineSystem.cs b/Content.Shared/Machines/EntitySystems/SharedMultipartMachineSystem.cs index 7185d60305..ff0693a257 100644 --- a/Content.Shared/Machines/EntitySystems/SharedMultipartMachineSystem.cs +++ b/Content.Shared/Machines/EntitySystems/SharedMultipartMachineSystem.cs @@ -8,15 +8,6 @@ namespace Content.Shared.Machines.EntitySystems; /// public abstract class SharedMultipartMachineSystem : EntitySystem { - protected EntityQuery XformQuery; - - public override void Initialize() - { - base.Initialize(); - - XformQuery = GetEntityQuery(); - } - /// /// Returns whether each non-optional part of the machine has a matched entity /// diff --git a/Content.Shared/Maps/TurfSystem.cs b/Content.Shared/Maps/TurfSystem.cs index f5477441d9..005b05b4d0 100644 --- a/Content.Shared/Maps/TurfSystem.cs +++ b/Content.Shared/Maps/TurfSystem.cs @@ -20,6 +20,7 @@ public sealed class TurfSystem : EntitySystem [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitions = default!; + [Dependency] private readonly EntityQuery _fixtureQuery = default!; /// /// Attempts to get the turf at or under some given coordinates or null if no such turf exists. @@ -77,8 +78,7 @@ public sealed class TurfSystem : EntitySystem if (!Resolve(gridUid, ref grid, ref gridXform)) return false; - var xformQuery = GetEntityQuery(); - var (gridPos, gridRot, matrix) = _transform.GetWorldPositionRotationMatrix(gridXform, xformQuery); + var (gridPos, gridRot, matrix) = _transform.GetWorldPositionRotationMatrix(gridXform); var size = grid.TileSize; var localPos = new Vector2(indices.X * size + (size / 2f), indices.Y * size + (size / 2f)); @@ -90,14 +90,13 @@ public sealed class TurfSystem : EntitySystem tileAabb = tileAabb.Translated(localPos); var intersectionArea = 0f; - var fixtureQuery = GetEntityQuery(); foreach (var ent in _entityLookup.GetEntitiesIntersecting(gridUid, worldBox, LookupFlags.Dynamic | LookupFlags.Static)) { - if (!fixtureQuery.TryGetComponent(ent, out var fixtures)) + if (!_fixtureQuery.TryGetComponent(ent, out var fixtures)) continue; // get grid local coordinates - var (pos, rot) = _transform.GetWorldPositionRotation(xformQuery.GetComponent(ent), xformQuery); + var (pos, rot) = _transform.GetWorldPositionRotation(ent); rot -= gridRot; pos = (-gridRot).RotateVec(pos - gridPos); diff --git a/Content.Shared/Materials/OreSilo/SharedOreSiloSystem.cs b/Content.Shared/Materials/OreSilo/SharedOreSiloSystem.cs index 33168db1db..ad7aeff7dd 100644 --- a/Content.Shared/Materials/OreSilo/SharedOreSiloSystem.cs +++ b/Content.Shared/Materials/OreSilo/SharedOreSiloSystem.cs @@ -10,7 +10,7 @@ public abstract class SharedOreSiloSystem : EntitySystem [Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - private EntityQuery _clientQuery; + [Dependency] private readonly EntityQuery _clientQuery = default!; /// public override void Initialize() @@ -27,8 +27,6 @@ public abstract class SharedOreSiloSystem : EntitySystem SubscribeLocalEvent(OnGetStoredMaterials); SubscribeLocalEvent(OnConsumeStoredMaterials); SubscribeLocalEvent(OnClientShutdown); - - _clientQuery = GetEntityQuery(); } private void OnToggleOreSiloClient(Entity ent, ref ToggleOreSiloClientMessage args) diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs index ac1a00d579..f3b8024d6d 100644 --- a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs +++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs @@ -54,10 +54,10 @@ public abstract partial class SharedCryoPodSystem : EntitySystem [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; [Dependency] private readonly StandingStateSystem _standingState = default!; - private EntityQuery _bloodstreamQuery; - private EntityQuery _itemSlotsQuery; - private EntityQuery _dispenserQuery; - private EntityQuery _solutionContainerQuery; + [Dependency] private readonly EntityQuery _bloodstreamQuery = default!; + [Dependency] private readonly EntityQuery _itemSlotsQuery = default!; + [Dependency] private readonly EntityQuery _dispenserQuery = default!; + [Dependency] private readonly EntityQuery _solutionContainerQuery = default!; public override void Initialize() @@ -78,11 +78,6 @@ public abstract partial class SharedCryoPodSystem : EntitySystem SubscribeLocalEvent(OnEjected); SubscribeLocalEvent(OnBodyInserted); - _bloodstreamQuery = GetEntityQuery(); - _itemSlotsQuery = GetEntityQuery(); - _dispenserQuery = GetEntityQuery(); - _solutionContainerQuery = GetEntityQuery(); - InitializeInsideCryoPod(); Subs.BuiEvents(CryoPodUiKey.Key, subs => diff --git a/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs b/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs index 75dbc4a7c8..57be19a776 100644 --- a/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs +++ b/Content.Shared/Medical/SuitSensors/SharedSuitSensorSystem.cs @@ -43,7 +43,8 @@ public abstract class SharedSuitSensorSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - private EntityQuery _sensorQuery; + [Dependency] private readonly EntityQuery _sensorQuery = default!; + public override void Initialize() { base.Initialize(); @@ -59,8 +60,6 @@ public abstract class SharedSuitSensorSystem : EntitySystem SubscribeLocalEvent(OnInsert); SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnSuitSensorDoAfter); - - _sensorQuery = GetEntityQuery(); } /// @@ -401,18 +400,17 @@ public abstract class SharedSuitSensorSystem : EntitySystem status.TotalDamage = totalDamage; status.TotalDamageThreshold = totalDamageThreshold; EntityCoordinates coordinates; - var xformQuery = GetEntityQuery(); if (transform.GridUid != null) { coordinates = new EntityCoordinates(transform.GridUid.Value, - Vector2.Transform(_transform.GetWorldPosition(transform, xformQuery), - _transform.GetInvWorldMatrix(xformQuery.GetComponent(transform.GridUid.Value), xformQuery))); + Vector2.Transform(_transform.GetWorldPosition(transform), + _transform.GetInvWorldMatrix(transform.GridUid.Value))); } else if (transform.MapUid != null) { coordinates = new EntityCoordinates(transform.MapUid.Value, - _transform.GetWorldPosition(transform, xformQuery)); + _transform.GetWorldPosition(transform)); } else { diff --git a/Content.Shared/Metabolism/MetabolizerSystem.cs b/Content.Shared/Metabolism/MetabolizerSystem.cs index 3e31fae1ac..af9ee038e2 100644 --- a/Content.Shared/Metabolism/MetabolizerSystem.cs +++ b/Content.Shared/Metabolism/MetabolizerSystem.cs @@ -31,16 +31,13 @@ public sealed class MetabolizerSystem : EntitySystem [Dependency] private readonly SharedEntityEffectsSystem _entityEffects = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; - private EntityQuery _organQuery; - private EntityQuery _solutionQuery; + [Dependency] private readonly EntityQuery _organQuery = default!; + [Dependency] private readonly EntityQuery _solutionQuery = default!; public override void Initialize() { base.Initialize(); - _organQuery = GetEntityQuery(); - _solutionQuery = GetEntityQuery(); - SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent>(OnApplyMetabolicMultiplier); } diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.cs b/Content.Shared/Mobs/Systems/MobStateSystem.cs index 0497f3cce0..c55c4d1bd6 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.cs @@ -19,12 +19,11 @@ public partial class MobStateSystem : EntitySystem [Dependency] private readonly DamageableSystem _damageable = default!; private ISawmill _sawmill = default!; - private EntityQuery _mobStateQuery; + [Dependency] private readonly EntityQuery _mobStateQuery = default!; public override void Initialize() { _sawmill = _logManager.GetSawmill("MobState"); - _mobStateQuery = GetEntityQuery(); base.Initialize(); SubscribeEvents(); } diff --git a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs index 0d6a9858ff..8485dc4386 100644 --- a/Content.Shared/Movement/Systems/SharedJetpackSystem.cs +++ b/Content.Shared/Movement/Systems/SharedJetpackSystem.cs @@ -20,6 +20,8 @@ public abstract class SharedJetpackSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly EntityQuery _jetpackQuery = default!; + public override void Initialize() { base.Initialize(); @@ -54,13 +56,11 @@ public abstract class SharedJetpackSystem : EntitySystem private void OnJetpackUserGravityChanged(ref GravityChangedEvent ev) { var gridUid = ev.ChangedGridIndex; - var jetpackQuery = GetEntityQuery(); - var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var user, out var transform)) { if (transform.GridUid == gridUid && ev.HasGravity && - jetpackQuery.TryGetComponent(user.Jetpack, out var jetpack)) + _jetpackQuery.TryGetComponent(user.Jetpack, out var jetpack)) { _popup.PopupClient(Loc.GetString("jetpack-to-grid"), uid, uid); diff --git a/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs b/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs index fab9552271..63a92fc4ab 100644 --- a/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs +++ b/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs @@ -20,8 +20,8 @@ public abstract class SharedMobCollisionSystem : EntitySystem [Dependency] protected readonly SharedPhysicsSystem Physics = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; - protected EntityQuery MobQuery; - protected EntityQuery PhysicsQuery; + [Dependency] protected readonly EntityQuery MobQuery = default!; + [Dependency] protected readonly EntityQuery PhysicsQuery = default!; /// /// @@ -64,8 +64,6 @@ public abstract class SharedMobCollisionSystem : EntitySystem }, true); Subs.CVar(CfgManager, CCVars.MovementPushMassCap, val => _massDiffCap = val, true); - MobQuery = GetEntityQuery(); - PhysicsQuery = GetEntityQuery(); SubscribeAllEvent(OnCollision); SubscribeLocalEvent(OnMoveModifier); diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index bda05da019..12f23ddf09 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -48,22 +48,22 @@ public abstract partial class SharedMoverController : VirtualController [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly TagSystem _tags = default!; - protected EntityQuery CanMoveInAirQuery; - protected EntityQuery FootstepModifierQuery; - protected EntityQuery FTLQuery; - protected EntityQuery MoverQuery; - protected EntityQuery MapQuery; - protected EntityQuery MapGridQuery; - protected EntityQuery MobMoverQuery; - protected EntityQuery RelayTargetQuery; - protected EntityQuery ModifierQuery; - protected EntityQuery NoRotateQuery; - protected EntityQuery PhysicsQuery; - protected EntityQuery PilotQuery; - protected EntityQuery PreventPilotQuery; - protected EntityQuery RelayQuery; - protected EntityQuery PullableQuery; - protected EntityQuery XformQuery; + [Dependency] protected readonly EntityQuery CanMoveInAirQuery = default!; + [Dependency] protected readonly EntityQuery FootstepModifierQuery = default!; + [Dependency] protected readonly EntityQuery FTLQuery = default!; + [Dependency] protected readonly EntityQuery MoverQuery = default!; + [Dependency] protected readonly EntityQuery MapQuery = default!; + [Dependency] protected readonly EntityQuery MapGridQuery = default!; + [Dependency] protected readonly EntityQuery MobMoverQuery = default!; + [Dependency] protected readonly EntityQuery RelayTargetQuery = default!; + [Dependency] protected readonly EntityQuery ModifierQuery = default!; + [Dependency] protected readonly EntityQuery NoRotateQuery = default!; + [Dependency] protected readonly EntityQuery PhysicsQuery = default!; + [Dependency] protected readonly EntityQuery PilotQuery = default!; + [Dependency] protected readonly EntityQuery PreventPilotQuery = default!; + [Dependency] protected readonly EntityQuery RelayQuery = default!; + [Dependency] protected readonly EntityQuery PullableQuery = default!; + [Dependency] protected readonly EntityQuery XformQuery = default!; private static readonly ProtoId FootstepSoundTag = "FootstepSound"; @@ -84,23 +84,6 @@ public abstract partial class SharedMoverController : VirtualController UpdatesBefore.Add(typeof(TileFrictionController)); base.Initialize(); - MoverQuery = GetEntityQuery(); - MobMoverQuery = GetEntityQuery(); - ModifierQuery = GetEntityQuery(); - RelayTargetQuery = GetEntityQuery(); - PhysicsQuery = GetEntityQuery(); - RelayQuery = GetEntityQuery(); - PullableQuery = GetEntityQuery(); - XformQuery = GetEntityQuery(); - NoRotateQuery = GetEntityQuery(); - CanMoveInAirQuery = GetEntityQuery(); - FootstepModifierQuery = GetEntityQuery(); - MapGridQuery = GetEntityQuery(); - MapQuery = GetEntityQuery(); - FTLQuery = GetEntityQuery(); - PilotQuery = GetEntityQuery(); - PreventPilotQuery = GetEntityQuery(); - SubscribeLocalEvent(OnTileFriction); SubscribeLocalEvent(OnMoverStartup); SubscribeLocalEvent(OnPhysicsBodyChanged); diff --git a/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs b/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs index e69f0c2f7a..e52573ab03 100644 --- a/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs +++ b/Content.Shared/NPC/Systems/NpcFactionSystem.Exception.cs @@ -9,14 +9,11 @@ namespace Content.Shared.NPC.Systems; /// public sealed partial class NpcFactionSystem { - private EntityQuery _exceptionQuery; - private EntityQuery _trackerQuery; + [Dependency] private readonly EntityQuery _exceptionQuery = default!; + [Dependency] private readonly EntityQuery _trackerQuery = default!; public void InitializeException() { - _exceptionQuery = GetEntityQuery(); - _trackerQuery = GetEntityQuery(); - SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnTrackerShutdown); } @@ -25,13 +22,13 @@ public sealed partial class NpcFactionSystem { foreach (var uid in ent.Comp.Hostiles) { - if (_trackerQuery.TryGetComponent(uid, out var tracker)) + if (_trackerQuery.TryComp(uid, out var tracker)) tracker.Entities.Remove(ent); } foreach (var uid in ent.Comp.Ignored) { - if (_trackerQuery.TryGetComponent(uid, out var tracker)) + if (_trackerQuery.TryComp(uid, out var tracker)) tracker.Entities.Remove(ent); } } @@ -40,7 +37,7 @@ public sealed partial class NpcFactionSystem { foreach (var uid in ent.Comp.Entities) { - if (!_exceptionQuery.TryGetComponent(uid, out var exception)) + if (!_exceptionQuery.TryComp(uid, out var exception)) continue; exception.Ignored.Remove(ent); @@ -114,7 +111,7 @@ public sealed partial class NpcFactionSystem if (!Resolve(ent, ref ent.Comp, false)) return; - if (!ent.Comp.Hostiles.Remove(target) || !_trackerQuery.TryGetComponent(target, out var tracker)) + if (!ent.Comp.Hostiles.Remove(target) || !_trackerQuery.TryComp(target, out var tracker)) return; tracker.Entities.Remove(ent); diff --git a/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs b/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs index 62ddcecb81..b25c1d79ac 100644 --- a/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs @@ -14,14 +14,12 @@ public abstract class SharedSpaceNinjaSystem : EntitySystem [Dependency] protected readonly SharedNinjaSuitSystem Suit = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; - public EntityQuery NinjaQuery; + [Dependency] public readonly EntityQuery NinjaQuery = default!; public override void Initialize() { base.Initialize(); - NinjaQuery = GetEntityQuery(); - SubscribeLocalEvent(OnNinjaAttacked); SubscribeLocalEvent(OnNinjaAttack); SubscribeLocalEvent(OnShotAttempted); diff --git a/Content.Shared/Nutrition/EntitySystems/ExaminableHungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/ExaminableHungerSystem.cs index 1a1418b644..9b30e5d438 100644 --- a/Content.Shared/Nutrition/EntitySystems/ExaminableHungerSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/ExaminableHungerSystem.cs @@ -8,14 +8,13 @@ namespace Content.Shared.Nutrition.EntitySystems; public sealed class ExaminableHungerSystem : EntitySystem { [Dependency] private readonly HungerSystem _hunger = default!; - private EntityQuery _hungerQuery; + + [Dependency] private readonly EntityQuery _hungerQuery = default!; public override void Initialize() { base.Initialize(); - _hungerQuery = GetEntityQuery(); - SubscribeLocalEvent(OnExamine); } diff --git a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs index 8446cf5d0f..3c70060b9e 100644 --- a/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs +++ b/Content.Shared/Nutrition/EntitySystems/IngestionSystem.Utensils.cs @@ -15,15 +15,13 @@ public sealed partial class IngestionSystem [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly IGameTiming _timing = default!; - private EntityQuery _utensilsQuery; + [Dependency] private readonly EntityQuery _utensilsQuery = default!; public void InitializeUtensils() { SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(ToolOpenableSystem) }); SubscribeLocalEvent(OnGetEdibleUtensils); - - _utensilsQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs index 315f8d8115..bbefe4f421 100644 --- a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs +++ b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs @@ -14,15 +14,6 @@ public abstract class SharedObjectivesSystem : EntitySystem [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!; - private EntityQuery _metaQuery; - - public override void Initialize() - { - base.Initialize(); - - _metaQuery = GetEntityQuery(); - } - /// /// Checks requirements and duplicate objectives to see if an objective can be assigned. /// @@ -39,10 +30,10 @@ public abstract class SharedObjectivesSystem : EntitySystem // only check for duplicate prototypes if it's unique if (comp.Unique) { - var proto = _metaQuery.GetComponent(uid).EntityPrototype?.ID; + var proto = MetaData(uid).EntityPrototype?.ID; foreach (var objective in mind.Objectives) { - if (_metaQuery.GetComponent(objective).EntityPrototype?.ID == proto) + if (MetaData(objective).EntityPrototype?.ID == proto) return false; } } diff --git a/Content.Shared/Paper/PaperSystem.cs b/Content.Shared/Paper/PaperSystem.cs index 75496d93b4..d99fffda74 100644 --- a/Content.Shared/Paper/PaperSystem.cs +++ b/Content.Shared/Paper/PaperSystem.cs @@ -28,10 +28,11 @@ public sealed class PaperSystem : EntitySystem [Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly EntityQuery _paperQuery = default!; + private static readonly ProtoId WriteIgnoreStampsTag = "WriteIgnoreStamps"; private static readonly ProtoId WriteTag = "Write"; - private EntityQuery _paperQuery; public override void Initialize() { @@ -47,8 +48,6 @@ public sealed class PaperSystem : EntitySystem SubscribeLocalEvent(OnRandomPaperContentMapInit); SubscribeLocalEvent(OnPaperWrite); - - _paperQuery = GetEntityQuery(); } private void OnMapInit(Entity entity, ref MapInitEvent args) diff --git a/Content.Shared/Physics/Controllers/SharedConveyorController.cs b/Content.Shared/Physics/Controllers/SharedConveyorController.cs index 74b1a5026d..26f9df9424 100644 --- a/Content.Shared/Physics/Controllers/SharedConveyorController.cs +++ b/Content.Shared/Physics/Controllers/SharedConveyorController.cs @@ -31,20 +31,16 @@ public abstract class SharedConveyorController : VirtualController private ConveyorJob _job; - private EntityQuery _conveyorQuery; - private EntityQuery _conveyedQuery; - protected EntityQuery PhysicsQuery; - protected EntityQuery XformQuery; + [Dependency] private readonly EntityQuery _conveyorQuery = default!; + [Dependency] private readonly EntityQuery _conveyedQuery = default!; + [Dependency] protected readonly EntityQuery PhysicsQuery = default!; + [Dependency] protected readonly EntityQuery XformQuery = default!; protected HashSet Intersecting = new(); public override void Initialize() { _job = new ConveyorJob(this); - _conveyorQuery = GetEntityQuery(); - _conveyedQuery = GetEntityQuery(); - PhysicsQuery = GetEntityQuery(); - XformQuery = GetEntityQuery(); UpdatesAfter.Add(typeof(SharedMoverController)); diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 5d64803273..2f7e1dfdfd 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -26,8 +26,9 @@ public abstract class SharedNavMapSystem : EntitySystem [Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!; [Robust.Shared.IoC.Dependency] private readonly INetManager _net = default!; + [Robust.Shared.IoC.Dependency] private readonly EntityQuery _doorQuery = default!; + private static readonly ProtoId[] WallTags = {"Wall", "Window"}; - private EntityQuery _doorQuery; public override void Initialize() { @@ -36,8 +37,6 @@ public abstract class SharedNavMapSystem : EntitySystem // Data handling events SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnConfigurableExamined); - - _doorQuery = GetEntityQuery(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Content.Shared/Power/EntitySystems/PowerStateSystem.cs b/Content.Shared/Power/EntitySystems/PowerStateSystem.cs index aba41e2432..c669557115 100644 --- a/Content.Shared/Power/EntitySystems/PowerStateSystem.cs +++ b/Content.Shared/Power/EntitySystems/PowerStateSystem.cs @@ -11,14 +11,7 @@ public abstract class SharedPowerStateSystem : EntitySystem { [Dependency] private readonly SharedPowerReceiverSystem _powerReceiverSystem = default!; - private EntityQuery _powerStateQuery; - - public override void Initialize() - { - base.Initialize(); - - _powerStateQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _powerStateQuery = default!; /// /// Sets the working state of the entity, adjusting its power draw accordingly. diff --git a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs index a00ec1d0c6..56c3c2a9a6 100644 --- a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs +++ b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs @@ -13,16 +13,12 @@ public sealed class ProximityDetectionSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ItemToggleSystem _toggle = default!; - private EntityQuery _xformQuery; - public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnToggled); - - _xformQuery = GetEntityQuery(); } private void OnMapInit(Entity ent, ref MapInitEvent args) @@ -85,7 +81,7 @@ public sealed class ProximityDetectionSystem : EntitySystem { var component = detector.Comp; - if (!_xformQuery.TryGetComponent(detector, out var transform)) + if (!TryComp(detector, out TransformComponent? transform)) return; if (Deleted(component.Target)) @@ -98,7 +94,7 @@ public sealed class ProximityDetectionSystem : EntitySystem while (query.MoveNext(out var uid)) { - if (!_xformQuery.TryGetComponent(uid, out var xForm)) + if (!TryComp(uid, out TransformComponent? xForm)) continue; if (!transform.Coordinates.TryDistance(EntityManager, xForm.Coordinates, out var distance) || diff --git a/Content.Shared/Random/Rules/NearbyAccess.cs b/Content.Shared/Random/Rules/NearbyAccess.cs index 2a8ad077c6..3f91d0fbed 100644 --- a/Content.Shared/Random/Rules/NearbyAccess.cs +++ b/Content.Shared/Random/Rules/NearbyAccess.cs @@ -31,9 +31,7 @@ public sealed partial class NearbyAccessRule : RulesRule public override bool Check(EntityManager entManager, EntityUid uid) { - var xformQuery = entManager.GetEntityQuery(); - - if (!xformQuery.TryGetComponent(uid, out var xform) || + if (!entManager.TryGetComponent(uid, out TransformComponent? xform) || xform.MapUid == null) { return false; @@ -44,7 +42,7 @@ public sealed partial class NearbyAccessRule : RulesRule var reader = entManager.System(); var found = false; - var worldPos = transform.GetWorldPosition(xform, xformQuery); + var worldPos = transform.GetWorldPosition(xform); var count = 0; // TODO: Update this when we get the callback version @@ -54,7 +52,7 @@ public sealed partial class NearbyAccessRule : RulesRule { if (!reader.AreAccessTagsAllowed(Access, comp) || Anchored && - (!xformQuery.TryGetComponent(comp, out var compXform) || + (!entManager.TryGetComponent(comp, out TransformComponent? compXform) || !compXform.Anchored)) { continue; diff --git a/Content.Shared/Random/Rules/NearbyComponents.cs b/Content.Shared/Random/Rules/NearbyComponents.cs index 13108e88d6..eb5b7073eb 100644 --- a/Content.Shared/Random/Rules/NearbyComponents.cs +++ b/Content.Shared/Random/Rules/NearbyComponents.cs @@ -22,9 +22,8 @@ public sealed partial class NearbyComponentsRule : RulesRule public override bool Check(EntityManager entManager, EntityUid uid) { var inRange = new HashSet>(); - var xformQuery = entManager.GetEntityQuery(); - if (!xformQuery.TryGetComponent(uid, out var xform) || + if (!entManager.TryGetComponent(uid, out TransformComponent? xform) || xform.MapUid == null) { return false; @@ -44,7 +43,7 @@ public sealed partial class NearbyComponentsRule : RulesRule foreach (var comp in inRange) { if (Anchored && - (!xformQuery.TryGetComponent(comp, out var compXform) || + (!entManager.TryGetComponent(comp, out TransformComponent? compXform) || !compXform.Anchored)) { continue; diff --git a/Content.Shared/RepulseAttract/RepulseAttractSystem.cs b/Content.Shared/RepulseAttract/RepulseAttractSystem.cs index f95a38d2eb..e526ab00dc 100644 --- a/Content.Shared/RepulseAttract/RepulseAttractSystem.cs +++ b/Content.Shared/RepulseAttract/RepulseAttractSystem.cs @@ -20,14 +20,14 @@ public sealed class RepulseAttractSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _xForm = default!; [Dependency] private readonly UseDelaySystem _delay = default!; - private EntityQuery _physicsQuery; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + private HashSet _entSet = new(); + public override void Initialize() { base.Initialize(); - _physicsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnMeleeAttempt, before: [typeof(UseDelayOnMeleeHitSystem)], after: [typeof(SharedWieldableSystem)]); SubscribeLocalEvent(OnRepulseAttractAction); } @@ -44,7 +44,7 @@ public sealed class RepulseAttractSystem : EntitySystem { if (args.Handled) return; - + var position = _xForm.GetMapCoordinates(args.Performer); args.Handled = TryRepulseAttract(position, args.Performer, ent.Comp.Speed, ent.Comp.Range, ent.Comp.Whitelist, ent.Comp.CollisionMask); } diff --git a/Content.Shared/Rootable/RootableSystem.cs b/Content.Shared/Rootable/RootableSystem.cs index 1b9be3537b..451b994aff 100644 --- a/Content.Shared/Rootable/RootableSystem.cs +++ b/Content.Shared/Rootable/RootableSystem.cs @@ -44,16 +44,13 @@ public sealed class RootableSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; - private EntityQuery _puddleQuery; - private EntityQuery _physicsQuery; + [Dependency] private readonly EntityQuery _puddleQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; public override void Initialize() { base.Initialize(); - _puddleQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnRootableMapInit); SubscribeLocalEvent(OnRootableShutdown); SubscribeLocalEvent(OnStartCollide); diff --git a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs index 891a5c9e12..c551cbc112 100644 --- a/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs +++ b/Content.Shared/Shuttles/Systems/SharedShuttleSystem.cs @@ -26,9 +26,8 @@ public abstract partial class SharedShuttleSystem : EntitySystem public const float FTLBufferRange = 8f; public const float TileDensityMultiplier = 0.5f; - private EntityQuery _gridQuery; - private EntityQuery _physicsQuery; - private EntityQuery _xformQuery; + [Dependency] private readonly EntityQuery _gridQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; private List> _grids = new(); @@ -37,10 +36,6 @@ public abstract partial class SharedShuttleSystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnGridFixtureChange); - - _gridQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); } private void OnGridFixtureChange(EntityUid uid, FixturesComponent manager, GridFixtureChangeEvent args) @@ -58,7 +53,7 @@ public abstract partial class SharedShuttleSystem : EntitySystem public bool CanFTLTo(EntityUid shuttleUid, MapId targetMap, EntityUid consoleUid) { var mapUid = Maps.GetMapOrInvalid(targetMap); - var shuttleMap = _xformQuery.GetComponent(shuttleUid).MapID; + var shuttleMap = Transform(shuttleUid).MapID; if (shuttleMap == targetMap) return true; @@ -190,7 +185,7 @@ public abstract partial class SharedShuttleSystem : EntitySystem public bool FTLFree(EntityUid shuttleUid, EntityCoordinates coordinates, Angle angle, List? exclusionZones) { if (!_physicsQuery.TryGetComponent(shuttleUid, out var shuttlePhysics) || - !_xformQuery.TryGetComponent(shuttleUid, out var shuttleXform)) + !TryComp(shuttleUid, out TransformComponent? shuttleXform)) { return false; } diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.Module.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.Module.cs index 3095190710..5d6f162414 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.Module.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.Module.cs @@ -9,7 +9,7 @@ namespace Content.Shared.Silicons.Borgs; public abstract partial class SharedBorgSystem { - private EntityQuery _moduleQuery; + [Dependency] private readonly EntityQuery _moduleQuery = default!; public void InitializeModule() { @@ -30,8 +30,6 @@ public abstract partial class SharedBorgSystem SubscribeLocalEvent>( OnComponentModuleInstalledRelay); - - _moduleQuery = GetEntityQuery(); } #region BorgModule diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs index cd801511ca..2eec51c410 100644 --- a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs @@ -72,8 +72,8 @@ public abstract partial class SharedStationAiSystem : EntitySystem // StationAiOverlay handles the static overlay. It also handles interaction blocking on client and server // for anything under it. - private EntityQuery _broadphaseQuery; - private EntityQuery _gridQuery; + [Dependency] private readonly EntityQuery _broadphaseQuery = default!; + [Dependency] private readonly EntityQuery _gridQuery = default!; private static readonly EntProtoId DefaultAi = "StationAiBrain"; private readonly ProtoId _downloadChatNotificationPrototype = "IntellicardDownload"; @@ -82,9 +82,6 @@ public abstract partial class SharedStationAiSystem : EntitySystem { base.Initialize(); - _broadphaseQuery = GetEntityQuery(); - _gridQuery = GetEntityQuery(); - InitializeAirlock(); InitializeHeld(); InitializeLight(); diff --git a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs index 7ae27da497..253c9df60c 100644 --- a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs +++ b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs @@ -20,6 +20,8 @@ public sealed class StationAiVisionSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _xforms = default!; [Dependency] private readonly SharedPowerReceiverSystem _power = default!; + [Dependency] private readonly EntityQuery _occluderQuery = default!; + private SeedJob _seedJob; private ViewJob _job; @@ -27,8 +29,6 @@ public sealed class StationAiVisionSystem : EntitySystem private readonly HashSet> _seeds = new(); private readonly HashSet _viewportTiles = new(); - private EntityQuery _occluderQuery; - // Dummy set private readonly HashSet _singleTiles = new(); @@ -45,8 +45,6 @@ public sealed class StationAiVisionSystem : EntitySystem { base.Initialize(); - _occluderQuery = GetEntityQuery(); - _seedJob = new() { System = this, diff --git a/Content.Shared/Slippery/SlidingSystem.cs b/Content.Shared/Slippery/SlidingSystem.cs index e0084c11ab..2bd771da5d 100644 --- a/Content.Shared/Slippery/SlidingSystem.cs +++ b/Content.Shared/Slippery/SlidingSystem.cs @@ -13,14 +13,12 @@ public sealed class SlidingSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _speedModifierSystem = default!; - private EntityQuery _slipperyQuery; + [Dependency] private readonly EntityQuery _slipperyQuery = default!; public override void Initialize() { base.Initialize(); - _slipperyQuery = GetEntityQuery(); - SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnStand); diff --git a/Content.Shared/Slippery/SlipperySystem.cs b/Content.Shared/Slippery/SlipperySystem.cs index 355d898dbf..6ab466f2e0 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/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index 4ed0da5b9e..7cc93d25f6 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -24,19 +24,10 @@ public abstract class SharedStationSpawningSystem : EntitySystem [Dependency] private readonly SharedStorageSystem _storage = default!; [Dependency] private readonly SharedTransformSystem _xformSystem = default!; - private EntityQuery _handsQuery; - private EntityQuery _inventoryQuery; - private EntityQuery _storageQuery; - private EntityQuery _xformQuery; - - public override void Initialize() - { - base.Initialize(); - _handsQuery = GetEntityQuery(); - _inventoryQuery = GetEntityQuery(); - _storageQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _handsQuery = default!; + [Dependency] private readonly EntityQuery _inventoryQuery = default!; + [Dependency] private readonly EntityQuery _storageQuery = default!; + [Dependency] private readonly EntityQuery _xformQuery = default!; /// /// Equips the data from a `RoleLoadout` onto an entity. diff --git a/Content.Shared/Station/SharedStationSystem.Tracker.cs b/Content.Shared/Station/SharedStationSystem.Tracker.cs index 770420d47e..41129da13f 100644 --- a/Content.Shared/Station/SharedStationSystem.Tracker.cs +++ b/Content.Shared/Station/SharedStationSystem.Tracker.cs @@ -50,7 +50,7 @@ public abstract partial class SharedStationSystem var xform = ent.Comp2; - if (!_xformQuery.Resolve(ent, ref xform)) + if (!Resolve(ent, ref xform)) return; // Entity is in nullspace or not on a grid diff --git a/Content.Shared/Station/SharedStationSystem.cs b/Content.Shared/Station/SharedStationSystem.cs index afd2e77258..13726ac2e1 100644 --- a/Content.Shared/Station/SharedStationSystem.cs +++ b/Content.Shared/Station/SharedStationSystem.cs @@ -11,8 +11,7 @@ public abstract partial class SharedStationSystem : EntitySystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly MetaDataSystem _meta = default!; - private EntityQuery _xformQuery; - private EntityQuery _stationMemberQuery; + [Dependency] private readonly EntityQuery _stationMemberQuery = default!; /// public override void Initialize() @@ -20,9 +19,6 @@ public abstract partial class SharedStationSystem : EntitySystem base.Initialize(); InitializeTracker(); - - _xformQuery = GetEntityQuery(); - _stationMemberQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs b/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs index 1405a5fd62..e5614cd971 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectAlertSystem.cs @@ -10,7 +10,7 @@ public sealed class StatusEffectAlertSystem : EntitySystem { [Dependency] private readonly AlertsSystem _alerts = default!; - private EntityQuery _effectQuery; + [Dependency] private readonly EntityQuery _effectQuery = default!; public override void Initialize() { @@ -19,8 +19,6 @@ public sealed class StatusEffectAlertSystem : EntitySystem SubscribeLocalEvent(OnStatusEffectApplied); SubscribeLocalEvent(OnStatusEffectRemoved); SubscribeLocalEvent(OnEndTimeUpdated); - - _effectQuery = GetEntityQuery(); } private void OnStatusEffectApplied(Entity ent, ref StatusEffectAppliedEvent args) diff --git a/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs b/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs index 512285eaf3..18dcf20c98 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectsSystem.cs @@ -20,8 +20,8 @@ public sealed partial class StatusEffectsSystem : EntitySystem [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly IPrototypeManager _proto = default!; - private EntityQuery _containerQuery; - private EntityQuery _effectQuery; + [Dependency] private readonly EntityQuery _containerQuery = default!; + [Dependency] private readonly EntityQuery _effectQuery = default!; public readonly HashSet StatusEffectPrototypes = []; @@ -40,9 +40,6 @@ public sealed partial class StatusEffectsSystem : EntitySystem SubscribeLocalEvent(OnPrototypesReloaded); - _containerQuery = GetEntityQuery(); - _effectQuery = GetEntityQuery(); - ReloadStatusEffectsCache(); } diff --git a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs index f1d6a9c7dd..c25b37865c 100644 --- a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs +++ b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs @@ -15,6 +15,8 @@ public sealed class StepTriggerSystem : EntitySystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly EntityQuery _physicsquery = default!; + public override void Initialize() { UpdatesOutsidePrediction = true; @@ -37,12 +39,11 @@ public sealed class StepTriggerSystem : EntitySystem public override void Update(float frameTime) { - var query = GetEntityQuery(); var enumerator = EntityQueryEnumerator(); while (enumerator.MoveNext(out var uid, out var active, out var trigger, out var transform)) { - if (!Update(uid, trigger, transform, query)) + if (!Update(uid, trigger, transform)) { continue; } @@ -51,7 +52,7 @@ public sealed class StepTriggerSystem : EntitySystem } } - private bool Update(EntityUid uid, StepTriggerComponent component, TransformComponent transform, EntityQuery query) + private bool Update(EntityUid uid, StepTriggerComponent component, TransformComponent transform) { if (!component.Active || component.Colliding.Count == 0) @@ -78,15 +79,15 @@ public sealed class StepTriggerSystem : EntitySystem foreach (var otherUid in component.Colliding) { - UpdateColliding(uid, component, transform, otherUid, query); + UpdateColliding(uid, component, transform, otherUid); } return false; } - private void UpdateColliding(EntityUid uid, StepTriggerComponent component, TransformComponent ownerXform, EntityUid otherUid, EntityQuery query) + private void UpdateColliding(EntityUid uid, StepTriggerComponent component, TransformComponent ownerXform, EntityUid otherUid) { - if (!query.TryGetComponent(otherUid, out var otherPhysics)) + if (!_physicsquery.TryComp(otherUid, out var otherPhysics)) return; var otherXform = Transform(otherUid); diff --git a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs index 0d744a4fe9..dea4f79656 100644 --- a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs @@ -20,12 +20,12 @@ public sealed class DumpableSystem : EntitySystem [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - private EntityQuery _itemQuery; + [Dependency] private readonly EntityQuery _itemQuery = default!; public override void Initialize() { base.Initialize(); - _itemQuery = GetEntityQuery(); + SubscribeLocalEvent(OnAfterInteract, after: new[]{ typeof(SharedEntityStorageSystem) }); SubscribeLocalEvent>(AddDumpVerb); SubscribeLocalEvent>(AddUtilityVerbs); diff --git a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs index 27a15c87a6..6d33fac51f 100644 --- a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs +++ b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs @@ -18,15 +18,14 @@ public sealed class MagnetPickupSystem : EntitySystem [Dependency] private readonly SharedStorageSystem _storage = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; private static readonly TimeSpan ScanDelay = TimeSpan.FromSeconds(1); - private EntityQuery _physicsQuery; public override void Initialize() { base.Initialize(); - _physicsQuery = GetEntityQuery(); SubscribeLocalEvent(OnMagnetMapInit); } diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 6ede24e8b7..5f5eaf0d1c 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -73,10 +73,10 @@ public abstract class SharedStorageSystem : EntitySystem [Dependency] private readonly TagSystem _tag = default!; [Dependency] protected readonly UseDelaySystem UseDelay = default!; - private EntityQuery _itemQuery; - private EntityQuery _stackQuery; - private EntityQuery _xformQuery; - private EntityQuery _userQuery; + [Dependency] private readonly EntityQuery _itemQuery = default!; + [Dependency] private readonly EntityQuery _stackQuery = default!; + [Dependency] private readonly EntityQuery _xformQuery = default!; + [Dependency] private readonly EntityQuery _userQuery = default!; /// /// Whether we're allowed to go up-down storage via UI. @@ -124,10 +124,6 @@ public abstract class SharedStorageSystem : EntitySystem { base.Initialize(); - _itemQuery = GetEntityQuery(); - _stackQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - _userQuery = GetEntityQuery(); _prototype.PrototypesReloaded += OnPrototypesReloaded; Subs.CVar(_cfg, CCVars.StorageLimit, OnStorageLimitChanged, true); diff --git a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs index 58f38c9db8..4fb2022deb 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 @@ public abstract partial class SharedStunSystem if (intersecting.Count == 0) return false; - var fixtureQuery = GetEntityQuery(); - var xformQuery = GetEntityQuery(); - var ourAABB = _entityLookup.GetAABBNoContainer(entity, entity.Comp.LocalPosition, entity.Comp.LocalRotation); foreach (var ent in intersecting) { - if (!fixtureQuery.TryGetComponent(ent, out var fixtures)) + if (!_fixtureQuery.TryGetComponent(ent, out var fixtures)) continue; - if (!xformQuery.TryComp(ent, out var xformComp)) + if (!TryComp(ent, out TransformComponent? xformComp)) continue; var xform = new Transform(xformComp.LocalPosition, xformComp.LocalRotation); diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs index a4fa463ee0..a66060bf4c 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -26,14 +26,12 @@ namespace Content.Shared.SubFloor [Dependency] private readonly SharedVisibilitySystem _visibility = default!; [Dependency] protected readonly SharedPopupSystem _popup = default!; - private EntityQuery _hideQuery; + [Dependency] private readonly EntityQuery _hideQuery = default!; public override void Initialize() { base.Initialize(); - _hideQuery = GetEntityQuery(); - SubscribeLocalEvent(OnTileChanged); SubscribeLocalEvent(OnSubFloorStarted); SubscribeLocalEvent(OnSubFloorTerminating); diff --git a/Content.Shared/Tag/TagSystem.cs b/Content.Shared/Tag/TagSystem.cs index b75e2a4af1..fd74c10a59 100644 --- a/Content.Shared/Tag/TagSystem.cs +++ b/Content.Shared/Tag/TagSystem.cs @@ -18,14 +18,12 @@ public sealed class TagSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _proto = default!; - private EntityQuery _tagQuery; + [Dependency] private readonly EntityQuery _tagQuery = default!; public override void Initialize() { base.Initialize(); - _tagQuery = GetEntityQuery(); - #if DEBUG SubscribeLocalEvent(OnTagInit); #endif diff --git a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs index 69805fd585..8620cca292 100644 --- a/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs +++ b/Content.Shared/Teleportation/Systems/SwapTeleporterSystem.cs @@ -29,8 +29,6 @@ public sealed class SwapTeleporterSystem : EntitySystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - private EntityQuery _xformQuery; - /// public override void Initialize() { @@ -40,8 +38,6 @@ public sealed class SwapTeleporterSystem : EntitySystem SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnShutdown); - - _xformQuery = GetEntityQuery(); } private void OnInteract(Entity ent, ref AfterInteractEvent args) @@ -224,7 +220,7 @@ public sealed class SwapTeleporterSystem : EntitySystem if (HasComp(parent) || HasComp(parent)) return ent; - if (!_xformQuery.TryGetComponent(parent, out var parentXform) || parentXform.Anchored) + if (!TryComp(parent, out TransformComponent? parentXform) || parentXform.Anchored) return ent; if (!TryComp(parent, out var body) || body.BodyType == BodyType.Static) diff --git a/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs b/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs index 216349d6f5..a5e2dbab38 100644 --- a/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs +++ b/Content.Shared/Temperature/Systems/SharedTemperatureSystem.cs @@ -16,7 +16,7 @@ public abstract class SharedTemperatureSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; - protected EntityQuery TemperatureQuery; + [Dependency] protected readonly EntityQuery TemperatureQuery = default!; /// /// Band-aid for unpredicted atmos. Delays the application for a short period so that laggy clients can get the replicated temperature. @@ -29,8 +29,6 @@ public abstract class SharedTemperatureSystem : EntitySystem SubscribeLocalEvent(OnTemperatureChanged); SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); - - TemperatureQuery = GetEntityQuery(); } private void OnTemperatureChanged(Entity ent, ref OnTemperatureChangeEvent args) diff --git a/Content.Shared/Throwing/CatchableSystem.cs b/Content.Shared/Throwing/CatchableSystem.cs index 244522f192..93c07d1e7e 100644 --- a/Content.Shared/Throwing/CatchableSystem.cs +++ b/Content.Shared/Throwing/CatchableSystem.cs @@ -23,17 +23,14 @@ public sealed partial class CatchableSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; - private EntityQuery _handsQuery; - private EntityQuery _combatModeQuery; + [Dependency] private readonly EntityQuery _handsQuery = default!; + [Dependency] private readonly EntityQuery _combatModeQuery = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnDoHit); - - _handsQuery = GetEntityQuery(); - _combatModeQuery = GetEntityQuery(); } private void OnDoHit(Entity ent, ref ThrowDoHitEvent args) diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index c3ea10822e..530c591f60 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -38,14 +38,14 @@ public sealed class ThrowingSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly IConfigurationManager _configManager = default!; - private EntityQuery _anchorableQuery; + [Dependency] private readonly EntityQuery _anchorableQuery = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + [Dependency] private readonly EntityQuery _projectileQuery = default!; public override void Initialize() { base.Initialize(); - _anchorableQuery = GetEntityQuery(); - Subs.CVar(_configManager, CCVars.TileFrictionModifier, value => _frictionModifier = value, true); Subs.CVar(_configManager, CCVars.AirFriction, value => _airDamping = value, true); } @@ -97,18 +97,14 @@ public sealed class ThrowingSystem : EntitySystem bool doSpin = true, ThrowingUnanchorStrength unanchor = ThrowingUnanchorStrength.None) { - var physicsQuery = GetEntityQuery(); - if (!physicsQuery.TryGetComponent(uid, out var physics)) + if (!_physicsQuery.TryComp(uid, out var physics)) return; - var projectileQuery = GetEntityQuery(); - TryThrow( uid, direction, physics, Transform(uid), - projectileQuery, baseThrowSpeed, user, pushbackRatio, @@ -130,7 +126,6 @@ public sealed class ThrowingSystem : EntitySystem Vector2 direction, PhysicsComponent physics, TransformComponent transform, - EntityQuery projectileQuery, float baseThrowSpeed = 10.0f, EntityUid? user = null, float pushbackRatio = PushbackDefault, @@ -156,7 +151,7 @@ public sealed class ThrowingSystem : EntitySystem return; // Allow throwing if this projectile only acts as a projectile when shot, otherwise disallow - if (projectileQuery.TryGetComponent(uid, out var proj) && !proj.OnlyCollideWhenShot) + if (_projectileQuery.TryComp(uid, out var proj) && !proj.OnlyCollideWhenShot) return; var comp = new ThrownItemComponent diff --git a/Content.Shared/Tiles/FloorTileSystem.cs b/Content.Shared/Tiles/FloorTileSystem.cs index a2743ca6ca..15ca721dbd 100644 --- a/Content.Shared/Tiles/FloorTileSystem.cs +++ b/Content.Shared/Tiles/FloorTileSystem.cs @@ -38,8 +38,11 @@ public sealed class FloorTileSystem : EntitySystem [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly TurfSystem _turf = default!; + [Dependency] private readonly EntityQuery _physicsQuery = default!; + private static readonly Vector2 CheckRange = new(1f, 1f); + /// /// A recycled hashset used to check for walls when trying to place tiles on turfs. /// @@ -68,9 +71,6 @@ public sealed class FloorTileSystem : EntitySystem if (locationMap.MapId == MapId.Nullspace) return; - var physicQuery = GetEntityQuery(); - var transformQuery = GetEntityQuery(); - var map = _transform.ToMapCoordinates(location); // Disallow placement close to grids. @@ -97,7 +97,7 @@ public sealed class FloorTileSystem : EntitySystem return; } - var userPos = _transform.ToMapCoordinates(transformQuery.GetComponent(args.User).Coordinates).Position; + var userPos = _transform.ToMapCoordinates(Transform(args.User).Coordinates).Position; var dir = userPos - map.Position; var canAccessCenter = false; if (dir.LengthSquared() > 0.01) @@ -115,7 +115,7 @@ public sealed class FloorTileSystem : EntitySystem _lookup.GetEntitiesInTile(tileRef.Value, _turfCheck); foreach (var ent in _turfCheck) { - if (physicQuery.TryGetComponent(ent, out var phys) && + if (_physicsQuery.TryGetComponent(ent, out var phys) && phys.BodyType == BodyType.Static && phys.Hard && (phys.CollisionLayer & (int)CollisionGroup.Impassable) != 0) diff --git a/Content.Shared/Tools/Systems/WeldableSystem.cs b/Content.Shared/Tools/Systems/WeldableSystem.cs index c6c47d539e..2651fa7449 100644 --- a/Content.Shared/Tools/Systems/WeldableSystem.cs +++ b/Content.Shared/Tools/Systems/WeldableSystem.cs @@ -15,7 +15,8 @@ public sealed class WeldableSystem : EntitySystem [Dependency] private readonly SharedToolSystem _toolSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - private EntityQuery _query; + + [Dependency] private readonly EntityQuery _query = default!; public override void Initialize() { @@ -24,8 +25,6 @@ public sealed class WeldableSystem : EntitySystem SubscribeLocalEvent(OnWeldFinished); SubscribeLocalEvent(OnWeldChanged); SubscribeLocalEvent(OnExamine); - - _query = GetEntityQuery(); } public bool IsWelded(EntityUid uid, WeldableComponent? component = null) diff --git a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs index 4bcfe8a69f..7c4b97abc4 100644 --- a/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs +++ b/Content.Shared/Weapons/Hitscan/Systems/HitscanBasicRaycastSystem.cs @@ -21,14 +21,12 @@ public sealed class HitscanBasicRaycastSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _log = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - private EntityQuery _visualsQuery; + [Dependency] private readonly EntityQuery _visualsQuery = default!; public override void Initialize() { base.Initialize(); - _visualsQuery = GetEntityQuery(); - SubscribeLocalEvent(OnHitscanFired); } diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 02d561d942..d50f73d286 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); /// @@ -656,12 +658,10 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem } 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); diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs index e684d9aad1..6830c2504b 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs @@ -118,8 +118,7 @@ public partial class SharedGunSystem return false; } - var xformQuery = GetEntityQuery(); - var xform = xformQuery.GetComponent(insertEnt); + var xform = Transform(insertEnt); var ammo = new List<(EntityUid? Entity, IShootable Shootable)>(freeSlots); var ev = new TakeAmmoEvent(freeSlots, ammo, xform.Coordinates, user); RaiseLocalEvent(insertEnt, ev); diff --git a/Content.Shared/Weather/SharedWeatherSystem.cs b/Content.Shared/Weather/SharedWeatherSystem.cs index d2270cf5c0..08429fc958 100644 --- a/Content.Shared/Weather/SharedWeatherSystem.cs +++ b/Content.Shared/Weather/SharedWeatherSystem.cs @@ -22,20 +22,12 @@ public abstract class SharedWeatherSystem : EntitySystem [Dependency] private readonly SharedRoofSystem _roof = default!; [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - private EntityQuery _blockQuery; - private EntityQuery _weatherQuery; + [Dependency] private readonly EntityQuery _blockQuery = default!; + [Dependency] private readonly EntityQuery _weatherQuery = default!; public static readonly TimeSpan StartupTime = TimeSpan.FromSeconds(15); public static readonly TimeSpan ShutdownTime = TimeSpan.FromSeconds(15); - public override void Initialize() - { - base.Initialize(); - - _blockQuery = GetEntityQuery(); - _weatherQuery = GetEntityQuery(); - } - public bool CanWeatherAffect(Entity ent, TileRef tileRef) { if (tileRef.Tile.IsEmpty) diff --git a/Content.Shared/Whitelist/EntityWhitelistSystem.cs b/Content.Shared/Whitelist/EntityWhitelistSystem.cs index bb4abe3197..d94b48d9cb 100644 --- a/Content.Shared/Whitelist/EntityWhitelistSystem.cs +++ b/Content.Shared/Whitelist/EntityWhitelistSystem.cs @@ -9,13 +9,7 @@ public sealed class EntityWhitelistSystem : EntitySystem { [Dependency] private readonly TagSystem _tag = default!; - private EntityQuery _itemQuery; - - public override void Initialize() - { - base.Initialize(); - _itemQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _itemQuery = default!; /// public bool IsValid(EntityWhitelist list, [NotNullWhen(true)] EntityUid? uid) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 09a1e0bfea..9a7b0d8ee3 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -12,15 +12,12 @@ public abstract partial class SharedXenoArtifactSystem { [Dependency] private readonly EntityTableSystem _entityTable = default!; - private EntityQuery _xenoArtifactQuery; - private EntityQuery _nodeQuery; + [Dependency] private readonly EntityQuery _xenoArtifactQuery = default!; + [Dependency] private readonly EntityQuery _nodeQuery = default!; private void InitializeNode() { SubscribeLocalEvent(OnNodeMapInit); - - _xenoArtifactQuery = GetEntityQuery(); - _nodeQuery = GetEntityQuery(); } /// diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index fedb7f1f60..f6d9331a12 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -12,12 +12,10 @@ public abstract partial class SharedXenoArtifactSystem { [Dependency] private readonly SharedAudioSystem _audio = default!; - private EntityQuery _unlockingQuery; + [Dependency] private readonly EntityQuery _unlockingQuery = default!; private void InitializeUnlock() { - _unlockingQuery = GetEntityQuery(); - SubscribeLocalEvent(OnUnlockingStarted); } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEShuffleSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEShuffleSystem.cs index 9eb627b4f6..4feb2b1649 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEShuffleSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEShuffleSystem.cs @@ -15,19 +15,11 @@ public sealed class XAEShuffleSystem : BaseXAESystem [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly IGameTiming _timing = default!; - private EntityQuery _mobState; + [Dependency] private readonly EntityQuery _mobState = default!; /// Pre-allocated and re-used collection. private readonly HashSet _entities= new(); - /// - public override void Initialize() - { - base.Initialize(); - - _mobState = GetEntityQuery(); - } - /// protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) { diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseQueryUpdateXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseQueryUpdateXATSystem.cs index 0abad7bdd5..ca4c645ba1 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseQueryUpdateXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseQueryUpdateXATSystem.cs @@ -8,15 +8,7 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAT; /// Type of XAT component that system will work with. public abstract class BaseQueryUpdateXATSystem : BaseXATSystem where T : Component { - protected EntityQuery _xenoArtifactQuery; - - /// - public override void Initialize() - { - base.Initialize(); - - _xenoArtifactQuery = GetEntityQuery(); - } + [Dependency] protected readonly EntityQuery _xenoArtifactQuery = default!; /// public override void Update(float frameTime) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs index d995a12d6a..48d0e01636 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -12,15 +12,7 @@ public abstract class BaseXATSystem : EntitySystem where T : Component [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] protected readonly SharedXenoArtifactSystem XenoArtifact = default!; - private EntityQuery _unlockingQuery; - - /// - public override void Initialize() - { - base.Initialize(); - - _unlockingQuery = GetEntityQuery(); - } + [Dependency] private readonly EntityQuery _unlockingQuery = default!; /// /// Subscribes to event occurring on artifact (and by relaying - on node). diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs index 50a7b0200e..e37f44adf8 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs @@ -11,15 +11,13 @@ public sealed class XATDeathSystem : BaseXATSystem { [Dependency] private readonly SharedTransformSystem _transform = default!; - private EntityQuery _xenoArtifactQuery; + [Dependency] private readonly EntityQuery _xenoArtifactQuery = default!; /// public override void Initialize() { base.Initialize(); - _xenoArtifactQuery = GetEntityQuery(); - SubscribeLocalEvent(OnMobStateChanged); } From ee07317bc3425058bf25122e55fed0f887ae4c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=81da?= Date: Sat, 11 Apr 2026 19:37:42 -0500 Subject: [PATCH 167/247] Move potassium iodide to new status effects (#43548) * added * remove * last changes * remove more old stuff * rename * rename --------- Co-authored-by: iaada --- .../RadiationProtectionComponent.cs | 18 --------- .../Systems/RadiationProtectionSystem.cs | 38 ------------------- .../DamageModifierStatusEffectComponent.cs | 16 ++++++++ .../DamageProtectionBuffComponent.cs | 17 --------- .../Systems/DamageModifierStatusEffect.cs | 19 ++++++++++ .../Systems/DamageProtectionBuffSystem.cs | 19 ---------- .../StatusEffectSystem.Relay.cs | 2 + Resources/Prototypes/Body/species_base.yml | 1 - Resources/Prototypes/Damage/modifier_sets.yml | 6 --- .../Entities/Mobs/NPCs/asteroid.yml | 1 - .../Entities/Mobs/NPCs/simplemob.yml | 2 - .../Entities/Mobs/Player/dragon.yml | 1 - .../Entities/StatusEffects/damage.yml | 27 +++++++++++++ Resources/Prototypes/Reagents/medicine.yml | 5 +-- Resources/Prototypes/status_effects.yml | 3 -- 15 files changed, 66 insertions(+), 109 deletions(-) delete mode 100644 Content.Server/Radiation/Components/RadiationProtectionComponent.cs delete mode 100644 Content.Server/Radiation/Systems/RadiationProtectionSystem.cs create mode 100644 Content.Shared/Damage/Components/DamageModifierStatusEffectComponent.cs delete mode 100644 Content.Shared/Damage/Components/DamageProtectionBuffComponent.cs create mode 100644 Content.Shared/Damage/Systems/DamageModifierStatusEffect.cs delete mode 100644 Content.Shared/Damage/Systems/DamageProtectionBuffSystem.cs create mode 100644 Resources/Prototypes/Entities/StatusEffects/damage.yml diff --git a/Content.Server/Radiation/Components/RadiationProtectionComponent.cs b/Content.Server/Radiation/Components/RadiationProtectionComponent.cs deleted file mode 100644 index 44b11af834..0000000000 --- a/Content.Server/Radiation/Components/RadiationProtectionComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Robust.Shared.Prototypes; -using Content.Shared.Damage.Prototypes; - -namespace Content.Server.Radiation.Components; - -/// -/// Exists for use as a status effect. -/// Adds the DamageProtectionBuffComponent to the entity and adds the specified DamageModifierSet to its list of modifiers. -/// -[RegisterComponent] -public sealed partial class RadiationProtectionComponent : Component -{ - /// - /// The radiation damage modifier for entities with this component. - /// - [DataField("modifier")] - public ProtoId RadiationProtectionModifierSetId = "PotassiumIodide"; -} diff --git a/Content.Server/Radiation/Systems/RadiationProtectionSystem.cs b/Content.Server/Radiation/Systems/RadiationProtectionSystem.cs deleted file mode 100644 index a32fa810c9..0000000000 --- a/Content.Server/Radiation/Systems/RadiationProtectionSystem.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Content.Server.Radiation.Components; -using Content.Shared.Damage.Components; -using Robust.Shared.Prototypes; - -namespace Content.Server.Radiation.EntitySystems; - -public sealed class RadiationProtectionSystem : EntitySystem -{ - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - } - - private void OnInit(EntityUid uid, RadiationProtectionComponent component, ComponentInit args) - { - if (!_prototypeManager.Resolve(component.RadiationProtectionModifierSetId, out var modifier)) - return; - var buffComp = EnsureComp(uid); - // add the damage modifier if it isn't in the dict yet - if (!buffComp.Modifiers.ContainsKey(component.RadiationProtectionModifierSetId)) - buffComp.Modifiers.Add(component.RadiationProtectionModifierSetId, modifier); - } - - private void OnShutdown(EntityUid uid, RadiationProtectionComponent component, ComponentShutdown args) - { - if (!TryComp(uid, out var buffComp)) - return; - // remove the damage modifier from the dict - buffComp.Modifiers.Remove(component.RadiationProtectionModifierSetId); - // if the dict is empty now, remove the buff component - if (buffComp.Modifiers.Count == 0) - RemComp(uid); - } -} diff --git a/Content.Shared/Damage/Components/DamageModifierStatusEffectComponent.cs b/Content.Shared/Damage/Components/DamageModifierStatusEffectComponent.cs new file mode 100644 index 0000000000..51a80a12fe --- /dev/null +++ b/Content.Shared/Damage/Components/DamageModifierStatusEffectComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Damage.Components; + +/// +/// Component used on a status effect entity to modify damage taken. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class DamageModifierStatusEffectComponent : Component +{ + /// + /// The modifier prototype to apply. + /// + [DataField(required: true)] + public DamageModifierSet Modifiers; +} diff --git a/Content.Shared/Damage/Components/DamageProtectionBuffComponent.cs b/Content.Shared/Damage/Components/DamageProtectionBuffComponent.cs deleted file mode 100644 index 99b055a645..0000000000 --- a/Content.Shared/Damage/Components/DamageProtectionBuffComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Shared.Damage.Prototypes; -using Robust.Shared.GameStates; - -namespace Content.Shared.Damage.Components; - -/// -/// Applies the specified DamageModifierSets when the entity takes damage. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class DamageProtectionBuffComponent : Component -{ - /// - /// The damage modifiers for entities with this component. - /// - [DataField] - public Dictionary Modifiers = new(); -} diff --git a/Content.Shared/Damage/Systems/DamageModifierStatusEffect.cs b/Content.Shared/Damage/Systems/DamageModifierStatusEffect.cs new file mode 100644 index 0000000000..a47b239330 --- /dev/null +++ b/Content.Shared/Damage/Systems/DamageModifierStatusEffect.cs @@ -0,0 +1,19 @@ +using Content.Shared.Damage.Components; +using Content.Shared.StatusEffectNew; + +namespace Content.Shared.Damage.Systems; + +public sealed class DamageModifierStatusEffectSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnDamageModifyStatus); + } + + private void OnDamageModifyStatus(Entity status, ref StatusEffectRelayedEvent args) + { + args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, status.Comp.Modifiers); + } +} diff --git a/Content.Shared/Damage/Systems/DamageProtectionBuffSystem.cs b/Content.Shared/Damage/Systems/DamageProtectionBuffSystem.cs deleted file mode 100644 index bbb7bfda32..0000000000 --- a/Content.Shared/Damage/Systems/DamageProtectionBuffSystem.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Shared.Damage.Components; - -namespace Content.Shared.Damage.Systems; - -public sealed class DamageProtectionBuffSystem : EntitySystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnDamageModify); - } - - private void OnDamageModify(EntityUid uid, DamageProtectionBuffComponent component, DamageModifyEvent args) - { - foreach (var modifier in component.Modifiers.Values) - args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modifier); - } -} diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs index cafe5075c9..cf81f66d9f 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs @@ -1,5 +1,6 @@ using Content.Shared.Body.Events; using Content.Shared.Damage.Events; +using Content.Shared.Damage.Systems; using Content.Shared.Mobs.Events; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; @@ -35,6 +36,7 @@ public sealed partial class StatusEffectsSystem SubscribeLocalEvent(RelayStatusEffectEvent); SubscribeLocalEvent(RefRelayStatusEffectEvent); + SubscribeLocalEvent(RelayStatusEffectEvent); } private void RefRelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, ref T args) where T : struct diff --git a/Resources/Prototypes/Body/species_base.yml b/Resources/Prototypes/Body/species_base.yml index f5c428fe1e..4716e3ca7f 100644 --- a/Resources/Prototypes/Body/species_base.yml +++ b/Resources/Prototypes/Body/species_base.yml @@ -78,7 +78,6 @@ - TemporaryBlindness - Pacified - Flashed - - RadiationProtection - Adrenaline - type: Identity - type: IdExaminable diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml index 1a19bfdcc2..86201ef759 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/Mobs/NPCs/asteroid.yml b/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml index 894c317d7f..f790b4abf9 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/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index c443f53794..0bdfcdfa27 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/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index a1e2e962df..7a1dbd7144 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 diff --git a/Resources/Prototypes/Entities/StatusEffects/damage.yml b/Resources/Prototypes/Entities/StatusEffects/damage.yml new file mode 100644 index 0000000000..2853eb19e2 --- /dev/null +++ b/Resources/Prototypes/Entities/StatusEffects/damage.yml @@ -0,0 +1,27 @@ +## Abstract + +- type: entity + abstract: true + parent: StatusEffectBase + id: StatusEffectDamageModifierBase + description: Status effect for modifying incoming sources of damage. You shouldn't be seeing this. + components: + - type: DamageModifierStatusEffect + - type: StatusEffect + whitelist: + components: + - Damageable + +## Buff + +- type: entity + parent: StatusEffectDamageModifierBase + id: StatusEffectRadiationProtection + name: radiation protection + components: + - type: DamageModifierStatusEffect + modifiers: + coefficients: + Radiation: 0.1 + +## Debuff diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index b8f0216e6c..a38be9e4cb 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/status_effects.yml b/Resources/Prototypes/status_effects.yml index a48758716c..c40b26fe85 100644 --- a/Resources/Prototypes/status_effects.yml +++ b/Resources/Prototypes/status_effects.yml @@ -53,9 +53,6 @@ - type: statusEffect id: Flashed -- type: statusEffect - id: RadiationProtection - - type: statusEffect id: Adrenaline alert: Adrenaline From 0f1109cc33bd081abd57e5faf9135fef4dcecfbe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 15:07:55 +0200 Subject: [PATCH 168/247] Update Credits (#43565) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 64ab3e20ae..554fb27446 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, 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, 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, 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, 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-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, 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, 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, zekins3366, 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, 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, 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, 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, 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, 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, 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, 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, zekins3366, ZelteHonor, zero, ZeroDiamond, ZeWaka, zHonys, zionnBE, ZNixian, Zokkie, ZoldorfTheWizard, zonespace27, Zylofan, Zymem, zzylex From e7807e1126755dda314054a672568257fa23763e Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sun, 12 Apr 2026 15:15:38 +0200 Subject: [PATCH 169/247] Add FleetingClothingComponent (#43444) * fleeting clothing * empty containers --- .../Clothing/ClientClothingSystem.cs | 2 +- .../Inventory/ClientInventorySystem.cs | 8 +- .../Atmos/EntitySystems/BarotraumaSystem.cs | 8 +- .../Nutrition/EntitySystems/SmokingSystem.cs | 2 +- .../Radiation/Systems/GeigerSystem.cs | 2 +- .../Radio/EntitySystems/HeadsetSystem.cs | 4 +- Content.Shared/Actions/SharedActionsSystem.cs | 2 +- Content.Shared/Body/Systems/LungSystem.cs | 6 +- .../Components/ChameleonClothingComponent.cs | 2 +- .../Components/FleetingClothingComponent.cs | 64 ++++++++++ .../Clothing/EntitySystems/ClothingSystem.cs | 23 +++- .../EntitySystems/FactionClothingSystem.cs | 6 +- .../EntitySystems/FleetingClothingSystem.cs | 117 ++++++++++++++++++ .../EntitySystems/PilotedClothingSystem.cs | 2 +- .../SelfUnremovableClothingSystem.cs | 2 +- .../SharedChameleonClothingSystem.cs | 10 +- .../EntitySystems/ToggleableClothingSystem.cs | 2 +- Content.Shared/Cuffs/SharedCuffableSystem.cs | 4 +- .../Eye/Blinding/Systems/BlindfoldSystem.cs | 4 +- .../Blinding/Systems/BlurryVisionSystem.cs | 4 +- .../ExtraHandsEquipmentSystem.cs | 8 +- .../EntitySystems/SharedHandsSystem.Pickup.cs | 4 + .../Inventory/Events/BeforeEquipEvents.cs | 50 ++++++++ .../Inventory/Events/BeforeUnequipEvents.cs | 50 ++++++++ .../Inventory/Events/EquipAttemptEvents.cs | 19 +-- .../Inventory/Events/EquippedEvents.cs | 44 +++---- .../Inventory/Events/UnequipAttemptEvent.cs | 19 +-- .../Inventory/Events/UnequippedEvents.cs | 46 +++---- .../Inventory/InventorySystem.Equip.cs | 32 +++-- .../Inventory/SelfEquipOnlySystem.cs | 4 +- .../Systems/MobStateSystem.Subscribers.cs | 4 +- .../Ninja/Systems/EnergyKatanaSystem.cs | 2 +- .../Ninja/Systems/SharedNinjaSuitSystem.cs | 2 +- Content.Shared/Stunnable/SharedStunSystem.cs | 4 +- .../SubFloor/SharedTrayScannerSystem.cs | 4 +- .../Systems/TriggerOnEquipmentSystem.cs | 4 +- .../Wieldable/SharedWieldableSystem.cs | 2 +- .../components/fleeting-clothing-coponent.ftl | 2 + 38 files changed, 439 insertions(+), 135 deletions(-) create mode 100644 Content.Shared/Clothing/Components/FleetingClothingComponent.cs create mode 100644 Content.Shared/Clothing/EntitySystems/FleetingClothingSystem.cs create mode 100644 Content.Shared/Inventory/Events/BeforeEquipEvents.cs create mode 100644 Content.Shared/Inventory/Events/BeforeUnequipEvents.cs create mode 100644 Resources/Locale/en-US/clothing/components/fleeting-clothing-coponent.ftl diff --git a/Content.Client/Clothing/ClientClothingSystem.cs b/Content.Client/Clothing/ClientClothingSystem.cs index 1a6db7a1b6..9c37476676 100644 --- a/Content.Client/Clothing/ClientClothingSystem.cs +++ b/Content.Client/Clothing/ClientClothingSystem.cs @@ -223,7 +223,7 @@ public sealed class ClientClothingSystem : ClothingSystem { base.OnGotEquipped(uid, component, args); - RenderEquipment(args.Equipee, uid, args.Slot, clothingComponent: component); + RenderEquipment(args.EquipTarget, uid, args.Slot, clothingComponent: component); } private void RenderEquipment(EntityUid equipee, EntityUid equipment, string slot, diff --git a/Content.Client/Inventory/ClientInventorySystem.cs b/Content.Client/Inventory/ClientInventorySystem.cs index 73f837d0d8..8cbb33d165 100644 --- a/Content.Client/Inventory/ClientInventorySystem.cs +++ b/Content.Client/Inventory/ClientInventorySystem.cs @@ -73,8 +73,8 @@ namespace Content.Client.Inventory private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent args) { - UpdateSlot(args.Equipee, component, args.Slot); - if (args.Equipee != _playerManager.LocalEntity) + UpdateSlot(args.EquipTarget, component, args.Slot); + if (args.EquipTarget != _playerManager.LocalEntity) return; var update = new SlotSpriteUpdate(null, args.SlotGroup, args.Slot, false); OnSpriteUpdate?.Invoke(update); @@ -82,8 +82,8 @@ namespace Content.Client.Inventory private void OnDidEquip(InventorySlotsComponent component, DidEquipEvent args) { - UpdateSlot(args.Equipee, component, args.Slot); - if (args.Equipee != _playerManager.LocalEntity) + UpdateSlot(args.EquipTarget, component, args.Slot); + if (args.EquipTarget != _playerManager.LocalEntity) return; var update = new SlotSpriteUpdate(args.Equipment, args.SlotGroup, args.Slot, HasComp(args.Equipment)); diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 6eb46b1946..6f881a32e7 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -64,17 +64,17 @@ namespace Content.Server.Atmos.EntitySystems private void OnPressureProtectionEquipped(EntityUid uid, PressureProtectionComponent pressureProtection, GotEquippedEvent args) { - if (TryComp(args.Equipee, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot)) + if (TryComp(args.EquipTarget, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot)) { - UpdateCachedResistances(args.Equipee, barotrauma); + UpdateCachedResistances(args.EquipTarget, barotrauma); } } private void OnPressureProtectionUnequipped(EntityUid uid, PressureProtectionComponent pressureProtection, GotUnequippedEvent args) { - if (TryComp(args.Equipee, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot)) + if (TryComp(args.EquipTarget, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot)) { - UpdateCachedResistances(args.Equipee, barotrauma); + UpdateCachedResistances(args.EquipTarget, barotrauma); } } diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs index ae35fa4825..e9ecba3454 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs @@ -106,7 +106,7 @@ namespace Content.Server.Nutrition.EntitySystems { if (args.Slot == "mask") { - _forensics.TransferDna(entity.Owner, args.Equipee, false); + _forensics.TransferDna(entity.Owner, args.EquipTarget, false); } } diff --git a/Content.Server/Radiation/Systems/GeigerSystem.cs b/Content.Server/Radiation/Systems/GeigerSystem.cs index 77df6c09e2..eb69e2ccd8 100644 --- a/Content.Server/Radiation/Systems/GeigerSystem.cs +++ b/Content.Server/Radiation/Systems/GeigerSystem.cs @@ -47,7 +47,7 @@ public sealed class GeigerSystem : SharedGeigerSystem { if (geiger.Comp.AttachedToSuit) SetEnabled(geiger, true); - SetUser(geiger, args.Equipee); + SetUser(geiger, args.EquipTarget); } private void OnEquippedHand(Entity geiger, ref GotEquippedHandEvent args) diff --git a/Content.Server/Radio/EntitySystems/HeadsetSystem.cs b/Content.Server/Radio/EntitySystems/HeadsetSystem.cs index 7d16687d5f..13b331c778 100644 --- a/Content.Server/Radio/EntitySystems/HeadsetSystem.cs +++ b/Content.Server/Radio/EntitySystems/HeadsetSystem.cs @@ -58,7 +58,7 @@ public sealed class HeadsetSystem : SharedHeadsetSystem base.OnGotEquipped(uid, component, args); if (component.IsEquipped && component.Enabled) { - EnsureComp(args.Equipee).Headset = uid; + EnsureComp(args.EquipTarget).Headset = uid; UpdateRadioChannels(uid, component); } } @@ -67,7 +67,7 @@ public sealed class HeadsetSystem : SharedHeadsetSystem { base.OnGotUnequipped(uid, component, args); RemComp(uid); - RemComp(args.Equipee); + RemComp(args.EquipTarget); } public void SetEnabled(EntityUid uid, bool value, HeadsetComponent? component = null) diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index db7b019e55..b8bd20034e 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -923,7 +923,7 @@ public abstract partial class SharedActionsSystem : EntitySystem if (GameTiming.ApplyingState) return; - var ev = new GetItemActionsEvent(_actionContainer, args.Equipee, args.Equipment, args.SlotFlags); + var ev = new GetItemActionsEvent(_actionContainer, args.EquipTarget, args.Equipment, args.SlotFlags); RaiseLocalEvent(args.Equipment, ev); if (ev.Actions.Count == 0) diff --git a/Content.Shared/Body/Systems/LungSystem.cs b/Content.Shared/Body/Systems/LungSystem.cs index 705a931102..8a7a00001a 100644 --- a/Content.Shared/Body/Systems/LungSystem.cs +++ b/Content.Shared/Body/Systems/LungSystem.cs @@ -37,10 +37,10 @@ public sealed class LungSystem : EntitySystem return; } - if (TryComp(args.Equipee, out InternalsComponent? internals)) + if (TryComp(args.EquipTarget, out InternalsComponent? internals)) { - ent.Comp.ConnectedInternalsEntity = args.Equipee; - _internals.ConnectBreathTool((args.Equipee, internals), ent); + ent.Comp.ConnectedInternalsEntity = args.EquipTarget; + _internals.ConnectBreathTool((args.EquipTarget, internals), ent); } } diff --git a/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs b/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs index 5caddda18b..56991fb1a3 100644 --- a/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs +++ b/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs @@ -29,7 +29,7 @@ public sealed partial class ChameleonClothingComponent : Component /// /// Current user that wears chameleon clothing. /// - [ViewVariables] + [DataField, AutoNetworkedField] public EntityUid? User; /// diff --git a/Content.Shared/Clothing/Components/FleetingClothingComponent.cs b/Content.Shared/Clothing/Components/FleetingClothingComponent.cs new file mode 100644 index 0000000000..0790d47672 --- /dev/null +++ b/Content.Shared/Clothing/Components/FleetingClothingComponent.cs @@ -0,0 +1,64 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Clothing.Components; + +/// +/// Makes a clothing item despawn when unequipped, stripped or removed in any other way. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class FleetingClothingComponent : Component +{ + /// + /// The sound to play when unequipping. + /// + [DataField] + public SoundSpecifier? RemovedSound; + + /// + /// Should the sound also be played when the player wearing the item unequips it themselves? + /// + [DataField] + public bool PlaySoundOnSelfUnequip = true; + + /// + /// The popup to show to the wearer if they unequip this item themselves. + /// + [DataField] + public LocId? SelfUnquipPopupWearer = "fleeting-clothing-component-default-popup"; + + /// + /// The popup to show to others if the wearer unequips this item themselves. + /// + [DataField] + public LocId? SelfUnquipPopupOthers = "fleeting-clothing-component-default-popup"; + + /// + /// The popup to show to everone if this item was removed by any other means. + /// + /// + /// We can't split this one up into wearer/others popups because EntGotRemovedFromContainerEvent does not have the user passed in. + /// + [DataField] + public LocId? RemovedPopup = "fleeting-clothing-component-default-popup"; + + /// + /// Examine text shown to the wearer. + /// + [DataField] + public LocId? ExamineWearer = "fleeting-clothing-component-default-examine"; + + /// + /// Examine text shown to others. + /// + [DataField] + public LocId? ExamineOthers = "fleeting-clothing-component-default-examine"; + + /// + /// If true this entity will use rather than simply be deleted. + /// Use this if you need to do stuff before deleting it, for example emptying storage containers so that the contents don't get deleted with with. + /// If false the clothing item will just be deleted instead. + /// + [DataField] + public bool DestroyOnUnequip = true; +} diff --git a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs index 6ebaa94e2e..6c7b024b29 100644 --- a/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ClothingSystem.cs @@ -88,22 +88,22 @@ public abstract class ClothingSystem : EntitySystem if ((component.Slots & args.SlotFlags) == SlotFlags.NONE) return; - var gotEquippedEvent = new ClothingGotEquippedEvent(args.Equipee, component); + var gotEquippedEvent = new ClothingGotEquippedEvent(args.EquipTarget, component); RaiseLocalEvent(uid, ref gotEquippedEvent); var didEquippedEvent = new ClothingDidEquippedEvent((uid, component)); - RaiseLocalEvent(args.Equipee, ref didEquippedEvent); + RaiseLocalEvent(args.EquipTarget, ref didEquippedEvent); } protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args) { if ((component.Slots & args.SlotFlags) != SlotFlags.NONE) { - var gotUnequippedEvent = new ClothingGotUnequippedEvent(args.Equipee, component); + var gotUnequippedEvent = new ClothingGotUnequippedEvent(args.EquipTarget, component); RaiseLocalEvent(uid, ref gotUnequippedEvent); var didUnequippedEvent = new ClothingDidUnequippedEvent((uid, component)); - RaiseLocalEvent(args.Equipee, ref didUnequippedEvent); + RaiseLocalEvent(args.EquipTarget, ref didUnequippedEvent); } component.InSlot = null; @@ -139,6 +139,21 @@ public abstract class ClothingSystem : EntitySystem #region Public API + /// + /// Returns true if this clothing item is currently inside an inventory slot AND that slot is considered valid for equipping. + /// For example putting shoes into your pockets does not count as being equipped. + /// + public bool IsEquipped(Entity item) + { + if (!Resolve(item, ref item.Comp, false)) + return false; + + if ((item.Comp.Slots & item.Comp.InSlotFlag) != SlotFlags.NONE) + return true; + + return false; + } + public void SetEquippedPrefix(EntityUid uid, string? prefix, ClothingComponent? clothing = null) { if (!Resolve(uid, ref clothing, false)) diff --git a/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs index 76b7b9aa66..5c0e936bdf 100644 --- a/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/FactionClothingSystem.cs @@ -22,8 +22,8 @@ public sealed class FactionClothingSystem : EntitySystem private void OnEquipped(Entity ent, ref GotEquippedEvent args) { - TryComp(args.Equipee, out var factionComp); - var faction = (args.Equipee, factionComp); + TryComp(args.EquipTarget, out var factionComp); + var faction = (args.EquipTarget, factionComp); ent.Comp.AlreadyMember = _faction.IsMember(faction, ent.Comp.Faction); _faction.AddFaction(faction, ent.Comp.Faction); @@ -37,6 +37,6 @@ public sealed class FactionClothingSystem : EntitySystem return; } - _faction.RemoveFaction(args.Equipee, ent.Comp.Faction); + _faction.RemoveFaction(args.EquipTarget, ent.Comp.Faction); } } diff --git a/Content.Shared/Clothing/EntitySystems/FleetingClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/FleetingClothingSystem.cs new file mode 100644 index 0000000000..e9a61dd8b3 --- /dev/null +++ b/Content.Shared/Clothing/EntitySystems/FleetingClothingSystem.cs @@ -0,0 +1,117 @@ +using Content.Shared.Clothing.Components; +using Content.Shared.Destructible; +using Content.Shared.Examine; +using Content.Shared.Inventory.Events; +using Content.Shared.Popups; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; +using Robust.Shared.Timing; + +namespace Content.Shared.Clothing.EntitySystems; + +public sealed class FleetingClothingSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly ClothingSystem _clothing = default!; + [Dependency] private readonly SharedDestructibleSystem _destructibleSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnBeforeGettingUnequipped); + SubscribeLocalEvent(OnGotUnequipped); + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + string? examineText = null; + // check if the item is clothing, is currently equipped and if the wearer is the examiner + if (_clothing.IsEquipped(ent.Owner) && Transform(ent.Owner).ParentUid == args.Examiner) + { + // text to show to the wearer only + if (ent.Comp.ExamineWearer != null) + examineText = Loc.GetString(ent.Comp.ExamineWearer); + } + else + { + // text to show to everyone else + if (ent.Comp.ExamineOthers != null) + examineText = Loc.GetString(ent.Comp.ExamineOthers); + } + + if (!string.IsNullOrEmpty(examineText)) + args.PushMarkup(examineText); + } + + // Raised before the item is being unequipped. + // We have to use QueueDel instead of Del because directly deleting the entity while an event is being raised on it will cause errors. + // We can't do this in.GotUnequippedEvent because container events don't include the user. + private void OnBeforeGettingUnequipped(Entity ent, ref BeforeGettingUnequippedEvent args) + { + if (ent.Comp.DestroyOnUnequip) + _destructibleSystem.DestroyEntity(ent.Owner); // Empty containers first. + else + PredictedQueueDel(ent.Owner); // Poof! + + // Use coords because the entity will be deleted. + var coords = Transform(ent).Coordinates; + if (ent.Comp.PlaySoundOnSelfUnequip || args.User != args.EquipTarget) + _audio.PlayPredicted(ent.Comp.RemovedSound, coords, args.User); + + if (args.User == args.EquipTarget) + { + // If the wearer removes the item themselves show specific messages to them and others. + string? selfMessage = null; + string? othersMessage = null; + if (ent.Comp.SelfUnquipPopupWearer != null) + selfMessage = Loc.GetString(ent.Comp.SelfUnquipPopupWearer, ("item", ent.Owner)); + if (ent.Comp.SelfUnquipPopupOthers != null) + othersMessage = Loc.GetString(ent.Comp.SelfUnquipPopupOthers, ("item", ent.Owner)); + + // Use the wearer for the popup location because the item item itself will get deleted. + _popup.PopupPredicted(selfMessage, othersMessage, args.EquipTarget, args.User, PopupType.LargeCaution); + } + else + { + // Show the same popup message for everyone. + if (ent.Comp.RemovedPopup != null) + _popup.PopupPredicted(Loc.GetString(ent.Comp.RemovedPopup, ("item", ent.Owner)), args.EquipTarget, args.User, PopupType.LargeCaution); + } + } + + // In case the item was somehow removed by any other means not using TryUnequip. + private void OnGotUnequipped(Entity ent, ref GotUnequippedEvent args) + { + if (_timing.ApplyingState) + return; // The results of the container change were already networked as part of the same game state. + + if (Terminating(ent.Owner) || EntityManager.IsQueuedForDeletion(ent.Owner)) + return; // Don't do the popups or sound twice. + + if (Terminating(args.EquipTarget)) + return; // Don't do anything if the item is removed due to the wearer getting deleted. + + if (ent.Comp.DestroyOnUnequip) + _destructibleSystem.DestroyEntity(ent.Owner); // Empty containers first. + else + PredictedQueueDel(ent.Owner); // Poof! + + // Can't predict the popup or sound without a user. + // TODO: Make the popup and sound API sane and remove this guard. + if (_net.IsClient) + return; + + // Use the wearer for the popup location because the item item itself will get deleted. + _audio.PlayPvs(ent.Comp.RemovedSound, args.EquipTarget); + if (ent.Comp.RemovedPopup != null) + _popup.PopupEntity(Loc.GetString(ent.Comp.RemovedPopup, ("item", ent.Owner)), args.EquipTarget, PopupType.LargeCaution); + } +} diff --git a/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs index 49df7aee94..9890832ee7 100644 --- a/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/PilotedClothingSystem.cs @@ -63,7 +63,7 @@ public sealed partial class PilotedClothingSystem : EntitySystem if (!isCorrectSlot) return; - entity.Comp.Wearer = args.Equipee; + entity.Comp.Wearer = args.EquipTarget; Dirty(entity); // Attempt to setup control link, if Pilot and Wearer are both present. diff --git a/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs index ab0c41c5c7..e722735267 100644 --- a/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SelfUnremovableClothingSystem.cs @@ -23,7 +23,7 @@ public sealed class SelfUnremovableClothingSystem : EntitySystem if (TryComp(selfUnremovableClothing, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) return; - if (args.UnEquipTarget == args.Unequipee) + if (args.UnEquipTarget == args.User) { args.Cancel(); } diff --git a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs index 07aae61f83..0aa0670624 100644 --- a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs @@ -66,12 +66,20 @@ public abstract class SharedChameleonClothingSystem : EntitySystem private void OnGotEquipped(EntityUid uid, ChameleonClothingComponent component, GotEquippedEvent args) { - component.User = args.Equipee; + if (Timing.ApplyingState) + return; // Already networked as part of the same gamestate + + component.User = args.EquipTarget; + Dirty(uid, component); } private void OnGotUnequipped(EntityUid uid, ChameleonClothingComponent component, GotUnequippedEvent args) { + if (Timing.ApplyingState) + return; // Already networked as part of the same gamestate + component.User = null; + Dirty(uid, component); } // Updates chameleon visuals and meta information. diff --git a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs index a2b7d01641..ce167e06f4 100644 --- a/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/ToggleableClothingSystem.cs @@ -160,7 +160,7 @@ public sealed class ToggleableClothingSystem : EntitySystem // This should maybe double check that the entity currently in the slot is actually the attached clothing, but // if its not, then something else has gone wrong already... if (component.Container != null && component.Container.ContainedEntity == null && component.ClothingUid != null) - _inventorySystem.TryUnequip(args.Equipee, component.Slot, force: true, triggerHandContact: true); + _inventorySystem.TryUnequip(args.EquipTarget, component.Slot, force: true, triggerHandContact: true); } private void OnRemoveToggleable(EntityUid uid, ToggleableClothingComponent component, ComponentRemove args) diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index f8efa20afa..577b7dfe2c 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -795,14 +795,14 @@ namespace Content.Shared.Cuffs private void OnEquipAttempt(EntityUid uid, CuffableComponent component, IsEquippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Equipee == uid) + if (args.User == uid) CheckAct(uid, component, args); } private void OnUnequipAttempt(EntityUid uid, CuffableComponent component, IsUnequippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Unequipee == uid) + if (args.User == uid) CheckAct(uid, component, args); } diff --git a/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs index a2ecb4d034..ca0b8694a7 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlindfoldSystem.cs @@ -24,11 +24,11 @@ public sealed class BlindfoldSystem : EntitySystem private void OnEquipped(Entity blindfold, ref GotEquippedEvent args) { - _blindableSystem.UpdateIsBlind(args.Equipee); + _blindableSystem.UpdateIsBlind(args.EquipTarget); } private void OnUnequipped(Entity blindfold, ref GotUnequippedEvent args) { - _blindableSystem.UpdateIsBlind(args.Equipee); + _blindableSystem.UpdateIsBlind(args.EquipTarget); } } diff --git a/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs b/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs index 099753d51e..aa0a5a1574 100644 --- a/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs +++ b/Content.Shared/Eye/Blinding/Systems/BlurryVisionSystem.cs @@ -44,12 +44,12 @@ public sealed class BlurryVisionSystem : EntitySystem private void OnGlassesEquipped(Entity glasses, ref GotEquippedEvent args) { - UpdateBlurMagnitude(args.Equipee); + UpdateBlurMagnitude(args.EquipTarget); } private void OnGlassesUnequipped(Entity glasses, ref GotUnequippedEvent args) { - UpdateBlurMagnitude(args.Equipee); + UpdateBlurMagnitude(args.EquipTarget); } } diff --git a/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs b/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs index 4b96f89d81..4146d4ec64 100644 --- a/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs +++ b/Content.Shared/Hands/EntitySystems/ExtraHandsEquipmentSystem.cs @@ -17,27 +17,27 @@ public sealed class ExtraHandsEquipmentSystem : EntitySystem private void OnEquipped(Entity ent, ref GotEquippedEvent args) { - if (!TryComp(args.Equipee, out var handsComp)) + if (!TryComp(args.EquipTarget, out var handsComp)) return; foreach (var (handName, hand) in ent.Comp.Hands) { // add the NetEntity id to the container name to prevent multiple items with this component from conflicting var handId = $"{GetNetEntity(ent.Owner).Id}-{handName}"; - _hands.AddHand((args.Equipee, handsComp), handId, hand.Location); + _hands.AddHand((args.EquipTarget, handsComp), handId, hand.Location); } } private void OnUnequipped(Entity ent, ref GotUnequippedEvent args) { - if (!TryComp(args.Equipee, out var handsComp)) + if (!TryComp(args.EquipTarget, out var handsComp)) return; foreach (var handName in ent.Comp.Hands.Keys) { // add the NetEntity id to the container name to prevent multiple items with this component from conflicting var handId = $"{GetNetEntity(ent.Owner).Id}-{handName}"; - _hands.RemoveHand((args.Equipee, handsComp), handId); + _hands.RemoveHand((args.EquipTarget, handsComp), handId); } } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs index c19de9629a..17794807ed 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs @@ -80,6 +80,10 @@ public abstract partial class SharedHandsSystem if (handId == null) return false; + // don't try to pick up the item if it's being deleted anyways + if (TerminatingOrDeleted(entity) || EntityManager.IsQueuedForDeletion(entity)) + return false; + if (!Resolve(entity, ref item, false)) return false; diff --git a/Content.Shared/Inventory/Events/BeforeEquipEvents.cs b/Content.Shared/Inventory/Events/BeforeEquipEvents.cs new file mode 100644 index 0000000000..6ea408107b --- /dev/null +++ b/Content.Shared/Inventory/Events/BeforeEquipEvents.cs @@ -0,0 +1,50 @@ +namespace Content.Shared.Inventory.Events; + +public abstract class BeforeEquipEventBase(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : EntityEventArgs +{ + /// + /// The entity performing the action. + /// NOT necessarily the one actually "receiving" the equipment. + /// + public readonly EntityUid User = user; + + /// + /// The entity which got equipped to. + /// NOT necessarily the one who performed the interaction. + /// + public readonly EntityUid EquipTarget = equipTarget; + + /// + /// The entity which got equipped. + /// + public readonly EntityUid Equipment = equipment; + + /// + /// The slot the entity got equipped to. + /// + public readonly string Slot = slotDefinition.Name; + + /// + /// The slot group the entity got equipped in. + /// + public readonly string SlotGroup = slotDefinition.SlotGroup; + + /// + /// Slotflags of the slot the entity just got equipped to. + /// + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; +} + +/// +/// Raised directed on an equipee before something is equipped to them. +/// Note: This is only raised when equipped using TryEquip, not if the container is directly modified. +/// +public sealed class BeforeEquipEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : BeforeEquipEventBase(user, equipTarget, equipment, slotDefinition); + +/// +/// Raised directed on equipment before it is equipped to an equipee. +/// Note: This is only raised when equipped using TryEquip, not if the container is directly modified. +/// +public sealed class BeforeGettingEquippedEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : BeforeEquipEventBase(user, equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/BeforeUnequipEvents.cs b/Content.Shared/Inventory/Events/BeforeUnequipEvents.cs new file mode 100644 index 0000000000..a084e1536a --- /dev/null +++ b/Content.Shared/Inventory/Events/BeforeUnequipEvents.cs @@ -0,0 +1,50 @@ +namespace Content.Shared.Inventory.Events; + +public abstract class BeforeUnequipEventBase(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : EntityEventArgs +{ + /// + /// The entity performing the action. + /// NOT necessarily the same as the entity whose equipment is being removed. + /// + public readonly EntityUid User = user; + + /// + /// The entity which was unequipped from. + /// NOT necessarily the one who performed the interaction. + /// + public readonly EntityUid EquipTarget = equipTarget; + + /// + /// The entity which got unequipped. + /// + public readonly EntityUid Equipment = equipment; + + /// + /// The slot the entity got unequipped from. + /// + public readonly string Slot = slotDefinition.Name; + + /// + /// The slot group the entity got unequipped from. + /// + public readonly string SlotGroup = slotDefinition.SlotGroup; + + /// + /// Slotflags of the slot the entity just got unequipped from. + /// + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; +} + +/// +/// Raised directed on an equipee before something is unequipped from them. +/// Note: This is only raised when enequipped using TryUnequip, not if the container is directly modified or the item is deleted. +/// +public sealed class BeforeUnequipEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : BeforeUnequipEventBase(user, equipTarget, equipment, slotDefinition); + +/// +/// Raised directed on equipment before it is unequipped from an equipee. +/// Note: This is only raised when enequipped using TryUnequip, not if the container is directly modified or the item is deleted. +/// +public sealed class BeforeGettingUnequippedEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : BeforeUnequipEventBase(user, equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/EquipAttemptEvents.cs b/Content.Shared/Inventory/Events/EquipAttemptEvents.cs index 2914dd469e..97babafe26 100644 --- a/Content.Shared/Inventory/Events/EquipAttemptEvents.cs +++ b/Content.Shared/Inventory/Events/EquipAttemptEvents.cs @@ -1,14 +1,15 @@ namespace Content.Shared.Inventory.Events; -public abstract class EquipAttemptBase(EntityUid equipee, EntityUid equipTarget, EntityUid equipment, +public abstract class EquipAttemptBase(EntityUid user, EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : CancellableEntityEventArgs, IInventoryRelayEvent { public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; /// - /// The entity performing the action. NOT necessarily the one actually "receiving" the equipment. + /// The entity performing the action. + /// NOT necessarily the one actually "receiving" the equipment. /// - public readonly EntityUid Equipee = equipee; + public readonly EntityUid User = user; /// /// The entity being equipped to. @@ -39,17 +40,17 @@ public abstract class EquipAttemptBase(EntityUid equipee, EntityUid equipTarget, /// /// Raised on the item that is being equipped. /// -public sealed class BeingEquippedAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition); +public sealed class BeingEquippedAttemptEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : EquipAttemptBase(user, equipTarget, equipment, slotDefinition); /// /// Raised on the entity that is equipping an item. /// -public sealed class IsEquippingAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition); +public sealed class IsEquippingAttemptEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : EquipAttemptBase(user, equipTarget, equipment, slotDefinition); /// /// Raised on the entity on who item is being equipped. /// -public sealed class IsEquippingTargetAttemptEvent(EntityUid equipee, EntityUid equipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : EquipAttemptBase(equipee, equipTarget, equipment, slotDefinition); +public sealed class IsEquippingTargetAttemptEvent(EntityUid user, EntityUid equipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : EquipAttemptBase(user, equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/EquippedEvents.cs b/Content.Shared/Inventory/Events/EquippedEvents.cs index 77fbe83e8a..a152a54b44 100644 --- a/Content.Shared/Inventory/Events/EquippedEvents.cs +++ b/Content.Shared/Inventory/Events/EquippedEvents.cs @@ -1,58 +1,42 @@ namespace Content.Shared.Inventory.Events; -public abstract class EquippedEventBase : EntityEventArgs +public abstract class EquippedEventBase(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : EntityEventArgs { /// - /// The entity equipping. + /// The entity which got equipped to. + /// NOT necessarily the one who performed the interaction. /// - public readonly EntityUid Equipee; + public readonly EntityUid EquipTarget = equipTarget; /// /// The entity which got equipped. /// - public readonly EntityUid Equipment; + public readonly EntityUid Equipment = equipment; /// /// The slot the entity got equipped to. /// - public readonly string Slot; + public readonly string Slot = slotDefinition.Name; /// /// The slot group the entity got equipped in. /// - public readonly string SlotGroup; + public readonly string SlotGroup = slotDefinition.SlotGroup; /// /// Slotflags of the slot the entity just got equipped to. /// - public readonly SlotFlags SlotFlags; - - public EquippedEventBase(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) - { - Equipee = equipee; - Equipment = equipment; - Slot = slotDefinition.Name; - SlotGroup = slotDefinition.SlotGroup; - SlotFlags = slotDefinition.SlotFlags; - } + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; } /// -/// Raised directed on an equipee when something is equipped. +/// Raised directed on an equipee when something was equipped. /// -public sealed class DidEquipEvent : EquippedEventBase -{ - public DidEquipEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } -} +public sealed class DidEquipEvent(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : EquippedEventBase(equipTarget, equipment, slotDefinition); /// -/// Raised directed on equipment when it's equipped to an equipee +/// Raised directed on equipment when it was equipped to an equipee. /// -public sealed class GotEquippedEvent : EquippedEventBase -{ - public GotEquippedEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } -} +public sealed class GotEquippedEvent(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : EquippedEventBase(equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs b/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs index a74b3e2d7c..4e557cc157 100644 --- a/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs +++ b/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs @@ -1,14 +1,15 @@ namespace Content.Shared.Inventory.Events; -public abstract class UnequipAttemptEventBase(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment, +public abstract class UnequipAttemptEventBase(EntityUid user, EntityUid unEquipTarget, EntityUid equipment, SlotDefinition slotDefinition) : CancellableEntityEventArgs, IInventoryRelayEvent { public SlotFlags TargetSlots { get; } = SlotFlags.WITHOUT_POCKET; /// - /// The entity performing the action. NOT necessarily the same as the entity whose equipment is being removed.. + /// The entity performing the action. + /// NOT necessarily the same as the entity whose equipment is being removed. /// - public readonly EntityUid Unequipee = unequipee; + public readonly EntityUid User = user; /// /// The entity being unequipped from. @@ -39,17 +40,17 @@ public abstract class UnequipAttemptEventBase(EntityUid unequipee, EntityUid unE /// /// Raised on the item that is being unequipped. /// -public sealed class BeingUnequippedAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition); +public sealed class BeingUnequippedAttemptEvent(EntityUid user, EntityUid unEquipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : UnequipAttemptEventBase(user, unEquipTarget, equipment, slotDefinition); /// /// Raised on the entity that is unequipping an item. /// -public sealed class IsUnequippingAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition); +public sealed class IsUnequippingAttemptEvent(EntityUid user, EntityUid unEquipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : UnequipAttemptEventBase(user, unEquipTarget, equipment, slotDefinition); /// /// Raised on the entity from who item is being unequipped. /// -public sealed class IsUnequippingTargetAttemptEvent(EntityUid unequipee, EntityUid unEquipTarget, EntityUid equipment, - SlotDefinition slotDefinition) : UnequipAttemptEventBase(unequipee, unEquipTarget, equipment, slotDefinition); +public sealed class IsUnequippingTargetAttemptEvent(EntityUid user, EntityUid unEquipTarget, EntityUid equipment, + SlotDefinition slotDefinition) : UnequipAttemptEventBase(user, unEquipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/Events/UnequippedEvents.cs b/Content.Shared/Inventory/Events/UnequippedEvents.cs index 4e1764a7d2..3f553c9591 100644 --- a/Content.Shared/Inventory/Events/UnequippedEvents.cs +++ b/Content.Shared/Inventory/Events/UnequippedEvents.cs @@ -1,52 +1,42 @@ namespace Content.Shared.Inventory.Events; -public abstract class UnequippedEventBase : EntityEventArgs +public abstract class UnequippedEventBase(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) : EntityEventArgs { /// - /// The entity unequipping. + /// The entity which was unequipped from. + /// NOT necessarily the one who performed the interaction. /// - public readonly EntityUid Equipee; + public readonly EntityUid EquipTarget = equipTarget; /// /// The entity which got unequipped. /// - public readonly EntityUid Equipment; + public readonly EntityUid Equipment = equipment; /// /// The slot the entity got unequipped from. /// - public readonly string Slot; + public readonly string Slot = slotDefinition.Name; /// /// The slot group the entity got unequipped from. /// - public readonly string SlotGroup; + public readonly string SlotGroup = slotDefinition.SlotGroup; /// /// Slotflags of the slot the entity just got unequipped from. /// - public readonly SlotFlags SlotFlags; - - public UnequippedEventBase(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) - { - Equipee = equipee; - Equipment = equipment; - Slot = slotDefinition.Name; - SlotGroup = slotDefinition.SlotGroup; - SlotFlags = slotDefinition.SlotFlags; - } + public readonly SlotFlags SlotFlags = slotDefinition.SlotFlags; } -public sealed class DidUnequipEvent : UnequippedEventBase -{ - public DidUnequipEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } -} +/// +/// Raised directed on an equipee when something was unequipped. +/// +public sealed class DidUnequipEvent(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : UnequippedEventBase(equipTarget, equipment, slotDefinition); -public sealed class GotUnequippedEvent : UnequippedEventBase -{ - public GotUnequippedEvent(EntityUid equipee, EntityUid equipment, SlotDefinition slotDefinition) : base(equipee, equipment, slotDefinition) - { - } -} +/// +/// Raised directed on equipment when it was unequipped from an equipee. +/// +public sealed class GotUnequippedEvent(EntityUid equipTarget, EntityUid equipment, SlotDefinition slotDefinition) + : UnequippedEventBase(equipTarget, equipment, slotDefinition); diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index ca069b71ca..311761e1e2 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -133,7 +133,7 @@ public abstract partial class InventorySystem { if (!Resolve(target, ref inventory, false)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-equip-cannot")); return false; } @@ -144,14 +144,14 @@ public abstract partial class InventorySystem if (!TryGetSlotContainer(target, slot, out var slotContainer, out var slotDefinition, inventory)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-equip-cannot")); return false; } if (!force && !CanEquip(actor, target, itemUid, slot, out var reason, slotDefinition, inventory, clothing)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString(reason)); return false; } @@ -179,9 +179,17 @@ public abstract partial class InventorySystem return false; } + // give other systems a chance to do stuff before equipping + var beforeGettingEquippedEvent = new BeforeGettingEquippedEvent(actor, target, itemUid, slotDefinition); + var beforeEquipEvent = new BeforeEquipEvent(actor, target, itemUid, slotDefinition); + + RaiseLocalEvent(itemUid, beforeGettingEquippedEvent); + RaiseLocalEvent(target, beforeEquipEvent); + + // actually equip the item if (!_containerSystem.Insert(itemUid, slotContainer)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot")); return false; } @@ -400,14 +408,14 @@ public abstract partial class InventorySystem if (!Resolve(target, ref inventory, false)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot")); return false; } if (!TryGetSlotContainer(target, slot, out var slotContainer, out var slotDefinition, inventory)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot")); return false; } @@ -419,7 +427,7 @@ public abstract partial class InventorySystem if (!force && !CanUnequip(actor, target, slot, out var reason, slotContainer, slotDefinition, inventory)) { - if(!silent) + if (!silent) _popup.PopupCursor(Loc.GetString(reason)); return false; } @@ -450,6 +458,14 @@ public abstract partial class InventorySystem return false; } + // give other systems a chance do stuff before unequipping + var beforeGettingUnequippedEvent = new BeforeGettingUnequippedEvent(actor, target, removedItem.Value, slotDefinition); + var beforeUnequipEvent = new BeforeUnequipEvent(actor, target, removedItem.Value, slotDefinition); + + RaiseLocalEvent(removedItem.Value, beforeGettingUnequippedEvent); + RaiseLocalEvent(target, beforeUnequipEvent); + + // actually unequip the item if (!_containerSystem.Remove(removedItem.Value, slotContainer, force: force, reparent: reparent)) return false; @@ -457,6 +473,8 @@ public abstract partial class InventorySystem var firstRun = itemsDropped == 0; ++itemsDropped; + // TODO: This is not being checked at the moment if we remove clothing by any other means than TryUnequip, for example when deleting the item or teleporting it away. + // But checking this in a EntGotRemovedFromContainerMessage subscription is fundamentally incompatible with the current prediction API for popups and audio since we don't have a user. foreach (var slotDef in inventory.Slots) { if (slotDef != slotDefinition && slotDef.DependsOn == slotDefinition.Name) diff --git a/Content.Shared/Inventory/SelfEquipOnlySystem.cs b/Content.Shared/Inventory/SelfEquipOnlySystem.cs index 2bd113e22b..113457e564 100644 --- a/Content.Shared/Inventory/SelfEquipOnlySystem.cs +++ b/Content.Shared/Inventory/SelfEquipOnlySystem.cs @@ -23,7 +23,7 @@ public sealed class SelfEquipOnlySystem : EntitySystem if (TryComp(ent, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) return; - if (args.Equipee != args.EquipTarget) + if (args.User != args.EquipTarget) args.Cancel(); } @@ -32,7 +32,7 @@ public sealed class SelfEquipOnlySystem : EntitySystem if (args.Cancelled) return; - if (args.Unequipee == args.UnEquipTarget) + if (args.User == args.UnEquipTarget) return; if (TryComp(ent, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index b0a6eb8c80..06f081f525 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -182,14 +182,14 @@ public partial class MobStateSystem private void OnEquipAttempt(EntityUid target, MobStateComponent component, IsEquippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Equipee == target) + if (args.User == target) CheckAct(target, component, args); } private void OnUnequipAttempt(EntityUid target, MobStateComponent component, IsUnequippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Unequipee == target) + if (args.User == target) CheckAct(target, component, args); } diff --git a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs index c2425b9264..053e7aa8b4 100644 --- a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs +++ b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs @@ -23,7 +23,7 @@ public sealed class EnergyKatanaSystem : EntitySystem /// private void OnEquipped(Entity ent, ref GotEquippedEvent args) { - _ninja.BindKatana(args.Equipee, ent); + _ninja.BindKatana(args.EquipTarget, ent); } private void OnCheckDash(Entity ent, ref CheckDashEvent args) diff --git a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs index 01f860da12..e5325c55ba 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs @@ -98,7 +98,7 @@ public abstract class SharedNinjaSuitSystem : EntitySystem /// private void OnUnequipped(Entity ent, ref GotUnequippedEvent args) { - var user = args.Equipee; + var user = args.EquipTarget; if (_ninja.NinjaQuery.TryComp(user, out var ninja)) UserUnequippedSuit(ent, (user, ninja)); } diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index d064434eac..2a3d96b9c7 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -392,14 +392,14 @@ public abstract partial class SharedStunSystem : EntitySystem private void OnEquipAttempt(EntityUid uid, StunnedComponent stunned, IsEquippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Equipee == uid) + if (args.User == uid) args.Cancel(); } private void OnUnequipAttempt(EntityUid uid, StunnedComponent stunned, IsUnequippingAttemptEvent args) { // is this a self-equip, or are they being stripped? - if (args.Unequipee == uid) + if (args.User == uid) args.Cancel(); } diff --git a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs index 9a7c829f14..03e5e517d6 100644 --- a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs +++ b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs @@ -80,12 +80,12 @@ public abstract class SharedTrayScannerSystem : EntitySystem private void OnTrayUnequipped(Entity ent, ref GotUnequippedEvent args) { - OnUnequip(args.Equipee); + OnUnequip(args.EquipTarget); } private void OnTrayEquipped(Entity ent, ref GotEquippedEvent args) { - OnEquip(args.Equipee); + OnEquip(args.EquipTarget); } private void OnTrayScannerActivate(EntityUid uid, TrayScannerComponent scanner, ActivateInWorldEvent args) diff --git a/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs b/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs index 8a122a1f65..8fad7ea231 100644 --- a/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs +++ b/Content.Shared/Trigger/Systems/TriggerOnEquipmentSystem.cs @@ -53,7 +53,7 @@ public sealed class TriggerOnEquipmentSystem : TriggerOnXSystem if ((ent.Comp.SlotFlags & args.SlotFlags) == 0) return; - Trigger.Trigger(ent.Owner, args.Equipee, ent.Comp.KeyOut); + Trigger.Trigger(ent.Owner, args.EquipTarget, ent.Comp.KeyOut); } private void OnGotUnequipped(Entity ent, ref GotUnequippedEvent args) @@ -64,6 +64,6 @@ public sealed class TriggerOnEquipmentSystem : TriggerOnXSystem if ((ent.Comp.SlotFlags & args.SlotFlags) == 0) return; - Trigger.Trigger(ent.Owner, args.Equipee, ent.Comp.KeyOut); + Trigger.Trigger(ent.Owner, args.EquipTarget, ent.Comp.KeyOut); } } diff --git a/Content.Shared/Wieldable/SharedWieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs index 49db4ff86c..bb8abffe7e 100644 --- a/Content.Shared/Wieldable/SharedWieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -209,7 +209,7 @@ public abstract class SharedWieldableSystem : EntitySystem private void OnBlockerEquipped(Entity ent, ref GotEquippedEvent args) { if (ent.Comp.BlockEquipped) - UnwieldAll(args.Equipee, force: true); + UnwieldAll(args.EquipTarget, force: true); } private void OnBlockerEquippedHand(Entity ent, ref GotEquippedHandEvent args) diff --git a/Resources/Locale/en-US/clothing/components/fleeting-clothing-coponent.ftl b/Resources/Locale/en-US/clothing/components/fleeting-clothing-coponent.ftl new file mode 100644 index 0000000000..f66a8a012a --- /dev/null +++ b/Resources/Locale/en-US/clothing/components/fleeting-clothing-coponent.ftl @@ -0,0 +1,2 @@ +fleeting-clothing-component-default-popup = {CAPITALIZE(THE($item))} crumbles into dust. +fleeting-clothing-component-default-examine = This is a fleeting item. It will diseappear when unequipped. From 2c8e76c4e0fbfe8067e121f29af2b7a3d1a45135 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:16:20 -0700 Subject: [PATCH 170/247] [STAGING] Fix blood regeneration (#43576) fsafsaasffas Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Shared/Body/Systems/SharedBloodstreamSystem.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs index a3caad69be..bde65b99fe 100644 --- a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs +++ b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs @@ -404,8 +404,10 @@ public abstract class SharedBloodstreamSystem : EntitySystem || amount == 0) return false; + // TODO: Either make this percentage based regeneration and pre-pass the percentage. + // TODO: Solution regulation API that doesn't result in very minor FixedPoint2 errors (Currently gingerbreadman only regenerates 0.99u instead of 1.00u) referenceFactor = Math.Clamp(referenceFactor, 0f, ent.Comp.MaxVolumeModifier); - var ratio = amount / ent.Comp.BloodReferenceSolution.Volume; + var ratio = (float)amount / (float)ent.Comp.BloodReferenceSolution.Volume; foreach (var (referenceReagent, referenceQuantity) in ent.Comp.BloodReferenceSolution) { From 17d0f27b128901549e886cd1592f864c3dbe945b Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:16:38 -0700 Subject: [PATCH 171/247] [STAGING] Revert "Force Vent Critters to Attack (#42399)" (#43577) Revert "Force Vent Critters to Attack (#42399)" This reverts commit 81be6f25713f5770636368e0d1c6782922d5e7dd. Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../ForceAttack/ForceAttackComponent.cs | 35 -------- .../ForceAttack/ForceAttackSystem.cs | 82 ------------------- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 12 +-- .../components/force-attack-component.ftl | 1 - .../Prototypes/Entities/Mobs/NPCs/animals.yml | 1 - .../Entities/Mobs/NPCs/elemental.yml | 1 - .../Prototypes/Entities/Mobs/NPCs/slimes.yml | 3 - .../FeedbackPopup/feedbackpopups.yml | 13 --- 8 files changed, 6 insertions(+), 142 deletions(-) delete mode 100644 Content.Server/ForceAttack/ForceAttackComponent.cs delete mode 100644 Content.Server/ForceAttack/ForceAttackSystem.cs delete mode 100644 Resources/Locale/en-US/components/force-attack-component.ftl diff --git a/Content.Server/ForceAttack/ForceAttackComponent.cs b/Content.Server/ForceAttack/ForceAttackComponent.cs deleted file mode 100644 index ce24554714..0000000000 --- a/Content.Server/ForceAttack/ForceAttackComponent.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; - -namespace Content.Server.ForceAttack; - -/// -/// This is used to force a player-controlled mob to attack nearby enemies, preventing "friendly antag"ing. -/// -[RegisterComponent] -[AutoGenerateComponentPause] -public sealed partial class ForceAttackComponent : Component -{ - /// - /// The next time this component will attempt to force an attack. - /// - [AutoPausedField] - public TimeSpan NextAttack = TimeSpan.MaxValue; - - /// - /// Whether an enemy is in range. - /// - public bool InRange = false; - - /// - /// The time this component will wait before forcing an attack when an enemy is in range. - /// - [DataField] - public TimeSpan PassiveTime = TimeSpan.FromSeconds(5); - - /// - /// The message displayed on forced attack - /// - [DataField] - public LocId Message = "force-attack-component-message"; -} diff --git a/Content.Server/ForceAttack/ForceAttackSystem.cs b/Content.Server/ForceAttack/ForceAttackSystem.cs deleted file mode 100644 index 33512b0e68..0000000000 --- a/Content.Server/ForceAttack/ForceAttackSystem.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Linq; -using Content.Shared.CombatMode; -using Content.Shared.Mobs.Systems; -using Content.Shared.NPC.Components; -using Content.Shared.NPC.Systems; -using Content.Shared.Popups; -using Content.Shared.Weapons.Melee; -using Content.Shared.Weapons.Melee.Events; -using Robust.Shared.Player; -using Robust.Shared.Timing; -using Robust.Shared.Utility; - -namespace Content.Server.ForceAttack; - -/// -/// This handles forcing a player-controlled mob to attack nearby enemies. -/// -public sealed class ForceAttackSystem : EntitySystem -{ - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedMeleeWeaponSystem _melee = default!; - [Dependency] private readonly NpcFactionSystem _faction = default!; - [Dependency] private readonly SharedCombatModeSystem _mode = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly MobStateSystem _mob = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnMeleeAttack); - } - - private void OnMeleeAttack(Entity ent, ref MeleeAttackEvent args) - { - ent.Comp.NextAttack = _timing.CurTime + ent.Comp.PassiveTime; - } - - /// - public override void Update(float frameTime) - { - base.Update(frameTime); - - var curTime = _timing.CurTime; - - // Query includes ActorComponent to only get mobs currently controlled by players - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var forceComp, out var factionComp, out var modeComp, out _)) - { - // Check if we have a weapon - if (!_melee.TryGetWeapon(uid, out var weaponUid, out var weapon) || weapon.NextAttack > curTime) - continue; - - // Find a target in range that isn't critical or dead - if (!_faction.GetNearbyHostiles((uid, factionComp), weapon.Range) - .Where((potTarget) => !_mob.IsIncapacitated(potTarget)) - .TryFirstOrNull(out var target)) - { - forceComp.InRange = false; - continue; - } - - if (!forceComp.InRange) // Just entered range - { - forceComp.InRange = true; - forceComp.NextAttack = curTime + forceComp.PassiveTime; - continue; - } - - if (forceComp.NextAttack > curTime) - continue; - - // Force mob to enter combat mode (necessary for AttemptAttack to succeed). - _mode.SetInCombatMode(uid, true, modeComp); - - var popupMessage = Loc.GetString(forceComp.Message); - if (popupMessage.Length != 0) - _popup.PopupEntity(popupMessage, uid, uid); - - _melee.AttemptLightAttack(uid, weaponUid, weapon, target.Value, false); - } - } -} diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 02d561d942..4937952dc4 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -344,12 +344,12 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(null, GetNetEntity(weaponUid), GetNetCoordinates(coordinates)), null); } - public bool AttemptLightAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target, bool predicted = true) + public bool AttemptLightAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target) { if (!TryComp(target, out TransformComponent? targetXform)) return false; - return AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(targetXform.Coordinates)), null, predicted); + return AttemptAttack(user, weaponUid, weapon, new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(targetXform.Coordinates)), null); } public bool AttemptDisarmAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, EntityUid target) @@ -364,7 +364,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem /// Called when a windup is finished and an attack is tried. /// /// True if attack successful - private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, AttackEvent attack, ICommonSession? session, bool predicted = true) + private bool AttemptAttack(EntityUid user, EntityUid weaponUid, MeleeWeaponComponent weapon, AttackEvent attack, ICommonSession? session) { var curTime = Timing.CurTime; @@ -471,7 +471,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem throw new NotImplementedException(); } - DoLungeAnimation(user, weaponUid, weapon.Angle, TransformSystem.ToMapCoordinates(GetCoordinates(attack.Coordinates)), weapon.Range, animation, predicted); + DoLungeAnimation(user, weaponUid, weapon.Angle, TransformSystem.ToMapCoordinates(GetCoordinates(attack.Coordinates)), weapon.Range, animation); } var attackEv = new MeleeAttackEvent(weaponUid); @@ -963,7 +963,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem return true; } - private void DoLungeAnimation(EntityUid user, EntityUid weapon, Angle angle, MapCoordinates coordinates, float length, string? animation, bool predicted = true) + private void DoLungeAnimation(EntityUid user, EntityUid weapon, Angle angle, MapCoordinates coordinates, float length, string? animation) { // TODO: Assert that offset eyes are still okay. if (!TryComp(user, out TransformComponent? userXform)) @@ -984,7 +984,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem if (localPos.Length() > visualLength) localPos = localPos.Normalized() * visualLength; - DoLunge(user, weapon, angle, localPos, animation, predicted); + DoLunge(user, weapon, angle, localPos, animation); } public abstract void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true); diff --git a/Resources/Locale/en-US/components/force-attack-component.ftl b/Resources/Locale/en-US/components/force-attack-component.ftl deleted file mode 100644 index fc033e1edc..0000000000 --- a/Resources/Locale/en-US/components/force-attack-component.ftl +++ /dev/null @@ -1 +0,0 @@ -force-attack-component-message = Your anger overwhelms you! diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 2954199554..aaf94e0d2d 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -2608,7 +2608,6 @@ raffle: settings: short - type: GhostTakeoverAvailable - - type: ForceAttack - type: entity name: tarantula diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml index 49f53654f2..b3f7749246 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml @@ -386,7 +386,6 @@ solution: bloodstream - type: DrainableSolution solution: bloodstream - - type: ForceAttack - type: entity parent: MarkerBase diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml index ef1203e458..8d8b4f763e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml @@ -168,7 +168,6 @@ - MindRoleGhostRoleTeamAntagonist raffle: settings: short - - type: ForceAttack - type: entity name: green slime @@ -209,7 +208,6 @@ - MindRoleGhostRoleTeamAntagonist raffle: settings: short - - type: ForceAttack - type: entity name: yellow slime @@ -249,4 +247,3 @@ - MindRoleGhostRoleTeamAntagonist raffle: settings: short - - type: ForceAttack diff --git a/Resources/Prototypes/FeedbackPopup/feedbackpopups.yml b/Resources/Prototypes/FeedbackPopup/feedbackpopups.yml index a71276bffc..d424a24244 100644 --- a/Resources/Prototypes/FeedbackPopup/feedbackpopups.yml +++ b/Resources/Prototypes/FeedbackPopup/feedbackpopups.yml @@ -17,16 +17,3 @@ responseType: "General Feedback" responseLink: "https://forum.spacestation14.com/c/development/feedback/51" showRoundEnd: false - -- type: feedbackPopup - id: ForceVentCrittersToAttackFeedback - popupOrigin: wizden_master - title: "[bold]Played an antagonist vent critter role?[/bold]" - description: >- - If you've played or interacted with a player-controlled antagonist vent critter (spiders, slimes, clown spiders), please leave your feedback on how the new auto-attack feature went. - responseType: "Feedback Thread" - responseLink: "https://forum.spacestation14.com/t/force-vent-critters-to-attack-feedback/26861" - showRoundEnd: true - ruleWhitelist: - components: - - VentHordeRule From c0c909f4564061ac517e9f8a25cafec84a0793ec Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 12 Apr 2026 12:00:19 -0700 Subject: [PATCH 172/247] Fix the Paradox Clone slowness bug. (#43528) * fix slow clones * actually I like this better. * sasadaasdsa --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Server/Cloning/CloningSystem.cs | 15 ++---- .../StatusEffectCompletionParser.cs | 2 +- .../CloneableStatusEffectComponent.cs | 9 ++++ .../StatusEffectNew/StatusEffectSystem.API.cs | 52 +++++++++++++++++++ .../Entities/StatusEffects/body.yml | 11 ++-- .../Entities/StatusEffects/traits.yml | 18 +++++++ Resources/Prototypes/Traits/disabilities.yml | 4 +- 7 files changed, 89 insertions(+), 22 deletions(-) create mode 100644 Content.Shared/StatusEffectNew/Components/CloneableStatusEffectComponent.cs create mode 100644 Resources/Prototypes/Entities/StatusEffects/traits.yml diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index ed0e3c85d1..ed72004551 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -40,6 +40,8 @@ public sealed partial class CloningSystem : SharedCloningSystem [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 EntityQuery _cloneableEffectQuery = default!; + /// /// Spawns a clone of the given humanoid mob at the specified location or in nullspace. /// @@ -280,19 +282,10 @@ public sealed partial class CloningSystem : SharedCloningSystem /// 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) + foreach (var effect in _statusEffects.EnumerateStatusEffects(original, _cloneableEffectQuery)) { - if (!TryComp(effect, out var effectComp)) - continue; - //We are not interested in temporary effects, only permanent ones. - if (effectComp.EndEffectTime is not null) + if (effect.Comp1.EndEffectTime is not null) continue; var effectProto = Prototype(effect); diff --git a/Content.Server/Toolshed/TypeParsers/StatusEffects/StatusEffectCompletionParser.cs b/Content.Server/Toolshed/TypeParsers/StatusEffects/StatusEffectCompletionParser.cs index 6b605e98d3..839f7fa9ca 100644 --- a/Content.Server/Toolshed/TypeParsers/StatusEffects/StatusEffectCompletionParser.cs +++ b/Content.Server/Toolshed/TypeParsers/StatusEffects/StatusEffectCompletionParser.cs @@ -11,6 +11,6 @@ public sealed class StatusEffectCompletionParser : CustomCompletionParser().StatusEffectPrototypes, GetArgHint(arg)); + return CompletionResult.FromHintOptions(IoCManager.Resolve().System().StatusEffectPrototypes, GetArgHint(arg)); } } diff --git a/Content.Shared/StatusEffectNew/Components/CloneableStatusEffectComponent.cs b/Content.Shared/StatusEffectNew/Components/CloneableStatusEffectComponent.cs new file mode 100644 index 0000000000..e40a62af25 --- /dev/null +++ b/Content.Shared/StatusEffectNew/Components/CloneableStatusEffectComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.StatusEffectNew.Components; + +/// +/// A simple marker component for a which allows this status effect to be cloned +/// by the CloningSystem (for example for paradox clones, cloning pods or changeling transformations). +/// This is used for traits that use permanent status effects. +/// +[RegisterComponent] +public sealed partial class CloneableStatusEffectComponent : Component; diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs index a65d4fe063..ffd7f11c0e 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs @@ -407,4 +407,56 @@ public sealed partial class StatusEffectsSystem return endTime is not null; } + + /// + /// Enumerates through and returns all status effects on an entity + /// + /// Status effect container we're enumerating through + /// All status effects in this container + public IEnumerable> EnumerateStatusEffects( + Entity container) + { + if (!_containerQuery.Resolve(container, ref container.Comp) || 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) || 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) || 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/Resources/Prototypes/Entities/StatusEffects/body.yml b/Resources/Prototypes/Entities/StatusEffects/body.yml index 7c7d5fc68c..c85f975b93 100644 --- a/Resources/Prototypes/Entities/StatusEffects/body.yml +++ b/Resources/Prototypes/Entities/StatusEffects/body.yml @@ -24,8 +24,9 @@ - type: DrunkStatusEffect - type: entity + abstract: true parent: MobStatusEffectBase - id: PainNumbnessTraitStatusEffect + id: PainNumbnessStatusEffectBase components: - type: StatusEffect whitelist: @@ -34,12 +35,6 @@ - MobThresholds - type: PainNumbnessStatusEffect -- type: entity - parent: BloodstreamStatusEffectBase - id: StatusEffectHemophiliaTrait - components: - - type: HemophiliaStatusEffect - - type: entity parent: BloodstreamStatusEffectDebuff id: StatusEffectAnticoagulant # Decreases the amount of blood coagulation when bleeding. @@ -59,7 +54,7 @@ bleedAmountMultiplier: 1.33 - type: entity - parent: [ PainNumbnessTraitStatusEffect, MobStatusEffectDebuff ] + parent: [ PainNumbnessStatusEffectBase, MobStatusEffectDebuff ] id: StatusEffectPainNumbness name: pain numbness diff --git a/Resources/Prototypes/Entities/StatusEffects/traits.yml b/Resources/Prototypes/Entities/StatusEffects/traits.yml new file mode 100644 index 0000000000..82200e80e3 --- /dev/null +++ b/Resources/Prototypes/Entities/StatusEffects/traits.yml @@ -0,0 +1,18 @@ +- type: entity + abstract: true + parent: StatusEffectBase + id: TraitStatusEffectBase + components: + - type: CloneableStatusEffect + +- type: entity + parent: [BloodstreamStatusEffectBase, TraitStatusEffectBase] + id: TraitStatusEffectHemophilia + components: + - type: HemophiliaStatusEffect + +- type: entity + parent: [ PainNumbnessStatusEffectBase, TraitStatusEffectBase ] + id: TraitStatusEffectPainNumbness + name: pain numbness + diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index 299032bf77..de3e93223b 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -86,7 +86,7 @@ specials: - !type:ApplyStatusEffectSpecial statusEffects: - - PainNumbnessTraitStatusEffect + - TraitStatusEffectPainNumbness - type: trait id: Hemophilia @@ -96,7 +96,7 @@ specials: - !type:ApplyStatusEffectSpecial statusEffects: - - StatusEffectHemophiliaTrait + - TraitStatusEffectHemophilia - type: trait id: ImpairedMobility From 825671c3b092283f689bddd457bec0b31f76350e Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 12 Apr 2026 19:16:17 +0000 Subject: [PATCH 173/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 530e2be409..bc33f50f78 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4031,3 +4024,10 @@ 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 From ee0f4050efd7f0f88872d134795f2da69758d0c6 Mon Sep 17 00:00:00 2001 From: ThatGuyUSA Date: Sun, 12 Apr 2026 20:10:12 -0700 Subject: [PATCH 174/247] Movement mod status clean up (#43582) * save station * Revert "Merge branch 'wiz-guardian-deck'" This reverts commit 78fa318583b6c93110c47e3b9e23f7222747f89a, reversing changes made to 4d5dab1098bcfdbce14906d9c77dbc669e295760. * yea basically this is good * newline * ok newline REAL on the RIGHT part of the code * i hate formatting --- .../Movement/Systems/MovementModStatusSystem.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Movement/Systems/MovementModStatusSystem.cs b/Content.Shared/Movement/Systems/MovementModStatusSystem.cs index a56ff9e683..91b8f304a3 100644 --- a/Content.Shared/Movement/Systems/MovementModStatusSystem.cs +++ b/Content.Shared/Movement/Systems/MovementModStatusSystem.cs @@ -52,7 +52,7 @@ public sealed class MovementModStatusSystem : EntitySystem ref StatusEffectRelayedEvent args ) { - args.Args.ModifySpeed(entity.Comp.WalkSpeedModifier, entity.Comp.WalkSpeedModifier); + args.Args.ModifySpeed(entity.Comp.WalkSpeedModifier, entity.Comp.SprintSpeedModifier); } private void OnRefreshFrictionStatus(Entity ent, ref StatusEffectRelayedEvent args) @@ -158,8 +158,7 @@ public sealed class MovementModStatusSystem : EntitySystem /// Status effect entity whose modifiers we are updating /// New walkSpeedModifer we're applying /// New sprintSpeedModifier we're applying - public bool TryUpdateMovementStatus( - EntityUid uid, + public bool TryUpdateMovementStatus(EntityUid uid, Entity status, float walkSpeedModifier, float sprintSpeedModifier @@ -170,7 +169,8 @@ public sealed class MovementModStatusSystem : EntitySystem status.Comp.SprintSpeedModifier = sprintSpeedModifier; status.Comp.WalkSpeedModifier = walkSpeedModifier; - + Dirty(status); + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); return true; From a363a61007fa633b022e522d4e77a80b231e82d7 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Mon, 13 Apr 2026 06:25:50 -0700 Subject: [PATCH 175/247] Fix Nuke Target Station Map (#43583) flukies begone Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../GameTicking/GameTicker.GameRule.cs | 28 ++++++- Content.Server/Pinpointer/StationMapSystem.cs | 73 +++++++++++++------ 2 files changed, 78 insertions(+), 23 deletions(-) diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index c1d8e3b31e..cf1d6eb1a8 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -309,7 +309,7 @@ public sealed partial class GameTicker } /// - /// Gets all the gamerule entities which are currently active. + /// Gets all the gamerule entities that have been added. /// public IEnumerable GetAddedGameRules() { @@ -321,6 +321,20 @@ public sealed partial class GameTicker } } + /// + /// Gets all the gamerule entities with {T} component that have been added. + /// + public IEnumerable> GetAddedGameRules() where T : Component + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var ruleData)) + { + if (IsGameRuleAdded(uid, ruleData)) + yield return (uid, comp); + } + } + + /// /// Gets all the gamerule entities which are currently active. /// @@ -333,6 +347,18 @@ public sealed partial class GameTicker } } + /// + /// Gets all the gamerule entities with {T} component that are currently active. + /// + public IEnumerable> GetActiveGameRules() where T : Component + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out _, out _)) + { + yield return (uid, comp); + } + } + /// /// Gets all gamerule prototypes /// diff --git a/Content.Server/Pinpointer/StationMapSystem.cs b/Content.Server/Pinpointer/StationMapSystem.cs index bf2d2e2817..1e5fa74b7c 100644 --- a/Content.Server/Pinpointer/StationMapSystem.cs +++ b/Content.Server/Pinpointer/StationMapSystem.cs @@ -22,9 +22,11 @@ public sealed class StationMapSystem : EntitySystem SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnUserParentChanged); - SubscribeLocalEvent(OnNukeopsStationSelected); + SubscribeLocalEvent(OnNukeOpsStationMap); + SubscribeLocalEvent(OnNukeopsStationSelected); - Subs.BuiEvents(StationMapUiKey.Key, subs => + Subs.BuiEvents(StationMapUiKey.Key, + subs => { subs.Event(OnStationMapOpened); subs.Event(OnStationMapClosed); @@ -36,18 +38,13 @@ public sealed class StationMapSystem : EntitySystem if (!ent.Comp.InitializeWithStation) return; - // If we ever find a need to make more exceptions like this, just turn this into an event. - if (HasComp(ent)) + var ev = new ChooseStationMapEvent(); + RaiseLocalEvent(ent, ref ev); + if (ev.Handled) { - foreach (var rule in _gameTicker.GetActiveGameRules()) - { - if (TryComp(rule, out var nukeopsRule) && nukeopsRule.TargetStation != null) - { - ent.Comp.TargetGrid = _station.GetLargestGrid((nukeopsRule.TargetStation.Value, null)); - Dirty(ent); - return; - } - } + ent.Comp.TargetGrid = ev.TargetGrid; + Dirty(ent); + return; } var station = _station.GetStationInMap(_xform.GetMapId(ent.Owner)); @@ -80,18 +77,50 @@ public sealed class StationMapSystem : EntitySystem comp.Map = uid; } - private void OnNukeopsStationSelected(Entity ent, ref NukeopsTargetStationSelectedEvent args) + private void OnNukeOpsStationMap(Entity entity, ref ChooseStationMapEvent args) { - if (args.TargetStation == null) + // If we have this component, we don't want a fallback map! + args.Handle(); + + foreach (var rule in _gameTicker.GetActiveGameRules()) + { + if (rule.Comp.TargetStation == null) + continue; + + args.TargetGrid = _station.GetLargestGrid((rule.Comp.TargetStation.Value, null)); + return; + } + } + + private void OnNukeopsStationSelected(ref NukeopsTargetStationSelectedEvent args) + { + if (args.TargetStation == null || !TryComp(args.RuleEntity, out var ruleGrids)) return; - if (!TryComp(ent, out var stationMap) || !TryComp(args.RuleEntity, out var ruleGrids)) - return; + var mapquery = EntityQueryEnumerator(); + while (mapquery.MoveNext(out var uid, out _, out var map)) + { + if (Transform(uid).MapID != ruleGrids.Map) + continue; - if (Transform(ent).MapID != ruleGrids.Map) - return; - - stationMap.TargetGrid = _station.GetLargestGrid((args.TargetStation.Value, null)); - Dirty(ent); + map.TargetGrid = _station.GetLargestGrid((args.TargetStation.Value, null)); + Dirty(uid, map); + } + } +} + +/// +/// Selects an alternative target for our station map! +/// If handled, this will not get the map of the current station. +/// +[ByRefEvent] +public record struct ChooseStationMapEvent +{ + public EntityUid? TargetGrid; + public bool Handled { get; private set; } + + public void Handle() + { + Handled = true; } } From 1b7bd4b895ecda049bcfcbf388e46599de821e24 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 13 Apr 2026 13:41:56 +0000 Subject: [PATCH 176/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index bc33f50f78..5829f05ff3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,13 +1,4 @@ Entries: -- 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 @@ -4031,3 +4022,11 @@ 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 From fc7cd007ab32563738051615900c7fd2c4f58236 Mon Sep 17 00:00:00 2001 From: Marchy <89603088+M4rchy-S@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:17:37 +0200 Subject: [PATCH 177/247] Dehardcode strings in StationEmergencyShuttleComponent (#43579) * Dehardcode strings in StationEmergencyShuttleComponent.cs * Add more fields * Rem names --- .../Components/StationEmergencyShuttleComponent.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Content.Server/Shuttles/Components/StationEmergencyShuttleComponent.cs b/Content.Server/Shuttles/Components/StationEmergencyShuttleComponent.cs index d4fcfb81c6..cfb667e1f0 100644 --- a/Content.Server/Shuttles/Components/StationEmergencyShuttleComponent.cs +++ b/Content.Server/Shuttles/Components/StationEmergencyShuttleComponent.cs @@ -26,35 +26,42 @@ public sealed partial class StationEmergencyShuttleComponent : Component /// /// The announcement made when the shuttle has successfully docked with the station. /// + [DataField] public LocId DockedAnnouncement = "emergency-shuttle-docked"; /// /// Sound played when the shuttle has successfully docked with the station. /// + [DataField] public SoundSpecifier DockedAudio = new SoundPathSpecifier("/Audio/Announcements/shuttle_dock.ogg"); /// /// The announcement made when the shuttle is unable to dock and instead parks in nearby space. /// + [DataField] public LocId NearbyAnnouncement = "emergency-shuttle-nearby"; /// /// Sound played when the shuttle is unable to dock and instead parks in nearby space. /// + [DataField] public SoundSpecifier NearbyAudio = new SoundPathSpecifier("/Audio/Misc/notice1.ogg"); /// /// The announcement made when the shuttle is unable to find a station. /// + [DataField] public LocId FailureAnnouncement = "emergency-shuttle-good-luck"; /// /// Sound played when the shuttle is unable to find a station. /// + [DataField] public SoundSpecifier FailureAudio = new SoundPathSpecifier("/Audio/Misc/notice1.ogg"); /// /// Text appended to the docking announcement if the launch time has been extended. /// + [DataField] public LocId LaunchExtendedMessage = "emergency-shuttle-extended"; } From 4052d3d6cfdd889ff38c6eb1307224ca363fc83e Mon Sep 17 00:00:00 2001 From: Glissadia Date: Mon, 13 Apr 2026 13:53:33 -0600 Subject: [PATCH 178/247] Fix ChemMaster UI not updating when solutions dragged into buffer (#43584) * Added new ChemMaster UI update event subscription * Added comment and TODO for when this fix can be removed. --- Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs index 7252a59f60..e4dc4fb8f9 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemMasterSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; +using Content.Shared.DragDrop; using Content.Shared.FixedPoint; using Content.Shared.Labels.EntitySystems; using Content.Shared.Storage; @@ -49,6 +50,9 @@ namespace Content.Server.Chemistry.EntitySystems SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(SubscribeUpdateUiState); + // Subscribing to DragDropTargetEvent is a quick fix to ensure the UI updates when fluids are dragged and dropped into the ChemMaster, since Shared.Fluids.EntitySystems.SolutionDumpingSystem.cs bypasses UpdateChemicals(). + // TODO: Remove when proper support for infinite volume solutions is added. + SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(SubscribeUpdateUiState); SubscribeLocalEvent(OnSetModeMessage); From 568c729ea0dfd8132be67d819bcc5d754319feaf Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 13 Apr 2026 20:09:34 +0000 Subject: [PATCH 179/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 5829f05ff3..2665da0550 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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 @@ -4030,3 +4022,11 @@ 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 From aa8c12e6eb92d725e1997e777b822fab2b4ccf2a Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:36:41 -0700 Subject: [PATCH 180/247] asfafasasffas --- .../Radiation/Systems/SharedRadiationSystem.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs index ea298d313d..78e061b9c3 100644 --- a/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs +++ b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs @@ -6,6 +6,11 @@ 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)) @@ -15,8 +20,8 @@ public abstract partial class SharedRadiationSystem : EntitySystem UpdateSource((entity, entity.Comp)); } - protected virtual void UpdateSource(Entity entity) - { - - } + /// + /// Updates the radiation source cache. Does nothing on client, see server! + /// + protected virtual void UpdateSource(Entity entity) { } } From 6c111c6d9218ebd0c191d4597008a736795b7395 Mon Sep 17 00:00:00 2001 From: _Svist_ <148935188+Svist666s@users.noreply.github.com> Date: Wed, 15 Apr 2026 21:12:09 +0400 Subject: [PATCH 181/247] CODEOWNERS (#3569) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 094a5b1557..2c74388c0c 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 From 1b992ceefa28e0b62b0063f116de5c0e25f107d6 Mon Sep 17 00:00:00 2001 From: CaasGit <87243814+CaasGit@users.noreply.github.com> Date: Wed, 15 Apr 2026 18:01:55 -0700 Subject: [PATCH 182/247] fix(Content.Server): Bump database packages for sec update. (#43601) --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3604412933..9e7322a529 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -8,11 +8,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 63783dd83f188e680f68ea766b2ea25d6c5d7c13 Mon Sep 17 00:00:00 2001 From: Moony Date: Thu, 16 Apr 2026 09:39:53 -0700 Subject: [PATCH 183/247] Correctly warning-only the remaining SVE warnings. (#43602) --- MSBuild/Content.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MSBuild/Content.props b/MSBuild/Content.props index de44d3ccc6..1ec3fe5cba 100644 --- a/MSBuild/Content.props +++ b/MSBuild/Content.props @@ -12,6 +12,6 @@ true - CS0618,CS0672,CS0612,CS1062,CS1064,NU1903 + CS0618,CS0672,CS0612,CS1062,CS1064,NU1901,NU1902,NU1903,NU1904 From 00808c9e0172b00a2b3dc1888d8aac39999b4d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ph=C3=B6nix?= Date: Thu, 16 Apr 2026 20:10:49 +0300 Subject: [PATCH 184/247] Fix access removal causing misspredicts (#43564) * lets see * final --- .../Access/Components/AccessReaderComponent.cs | 2 +- Content.Shared/Access/Systems/AccessReaderSystem.cs | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Access/Components/AccessReaderComponent.cs b/Content.Shared/Access/Components/AccessReaderComponent.cs index c261c7deca..32662d7838 100644 --- a/Content.Shared/Access/Components/AccessReaderComponent.cs +++ b/Content.Shared/Access/Components/AccessReaderComponent.cs @@ -38,7 +38,7 @@ public sealed partial class AccessReaderComponent : Component /// An unmodified copy of the original list of the access groups that grant access to this reader. /// /// - /// If null, the access lists of this entity have not been modified yet. + /// If null, entity isn't intialized yet. /// [DataField] public List>>? AccessListsOriginal = null; diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index f8c6d49244..19c1cb880b 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -41,6 +41,7 @@ public sealed class AccessReaderSystem : EntitySystem { base.Initialize(); + SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnLinkAttempt); @@ -52,13 +53,18 @@ public sealed class AccessReaderSystem : EntitySystem SubscribeLocalEvent(OnHandleState); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.AccessListsOriginal ??= [.. ent.Comp.AccessLists]; + Dirty(ent); + } + private void OnExamined(Entity ent, ref ExaminedEvent args) { - if (!GetMainAccessReader(ent, out var mainAccessReader)) + if (!GetMainAccessReader(ent, out var mainAccessReader) || + mainAccessReader.Value.Comp.AccessListsOriginal == null) return; - mainAccessReader.Value.Comp.AccessListsOriginal ??= new(mainAccessReader.Value.Comp.AccessLists); - var accessHasBeenModified = mainAccessReader.Value.Comp.AccessLists.Count != mainAccessReader.Value.Comp.AccessListsOriginal.Count; if (!accessHasBeenModified) From a1d85443229f5a14a0d96da9ec639b834b7967d9 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 16 Apr 2026 17:26:35 +0000 Subject: [PATCH 185/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 2665da0550..71aaa1b19d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- 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. @@ -4030,3 +4022,11 @@ 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 From 4e91c6652275a3961f4a5c729f98f96ee1b23dd3 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:49:44 -0700 Subject: [PATCH 186/247] Allow Organs to have Markings Displacements again. (#43604) * first shtep * readd markings support --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Client/Body/VisualBodySystem.cs | 28 ++++++++++++++++--- .../Body/VisualOrganMarkingsComponent.cs | 8 ++++++ Resources/Prototypes/Body/Species/vox.yml | 6 ++++ .../Prototypes/Body/Species/vulpkanin.yml | 6 ++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Content.Client/Body/VisualBodySystem.cs b/Content.Client/Body/VisualBodySystem.cs index fba936ee58..cf1e09bf30 100644 --- a/Content.Client/Body/VisualBodySystem.cs +++ b/Content.Client/Body/VisualBodySystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Client.DisplacementMap; using Content.Shared.Body; using Content.Shared.CCVar; using Content.Shared.Humanoid.Markings; @@ -15,6 +16,7 @@ public sealed class VisualBodySystem : SharedVisualBodySystem { [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly DisplacementMapSystem _displacement = default!; [Dependency] private readonly MarkingManager _marking = default!; [Dependency] private readonly SpriteSystem _sprite = default!; @@ -167,8 +169,11 @@ public sealed class VisualBodySystem : SharedVisualBodySystem } } - private void ApplyMarkings(Entity ent, EntityUid target) + private void ApplyMarkings(Entity ent, Entity target) { + if (!Resolve(target, ref target.Comp)) + return; + var applied = new List(); foreach (var marking in AllMarkings(ent)) { @@ -178,6 +183,8 @@ public sealed class VisualBodySystem : SharedVisualBodySystem if (!_sprite.LayerMapTryGet(target, proto.BodyPart, out var index, true)) continue; + ent.Comp.MarkingsDisplacement.TryGetValue(proto.BodyPart, out var displacement); + for (var i = 0; i < proto.Sprites.Count; i++) { var sprite = proto.Sprites[i]; @@ -190,8 +197,8 @@ public sealed class VisualBodySystem : SharedVisualBodySystem if (!_sprite.LayerMapTryGet(target, layerId, out _, false)) { - var layer = _sprite.AddLayer(target, sprite, index + i + 1); - _sprite.LayerMapSet(target, layerId, layer); + var spriteLayer = _sprite.AddLayer(target, sprite, index + i + 1); + _sprite.LayerMapSet(target, layerId, spriteLayer); _sprite.LayerSetSprite(target, layerId, rsi); } @@ -199,6 +206,9 @@ public sealed class VisualBodySystem : SharedVisualBodySystem _sprite.LayerSetColor(target, layerId, marking.MarkingColors[i]); else _sprite.LayerSetColor(target, layerId, Color.White); + + if (displacement != null && proto.CanBeDisplaced) + _displacement.TryAddDisplacement(displacement, (target, target.Comp), index + i + 1, layerId, out _); } applied.Add(marking); @@ -206,8 +216,11 @@ public sealed class VisualBodySystem : SharedVisualBodySystem ent.Comp.AppliedMarkings = applied; } - private void RemoveMarkings(Entity ent, EntityUid target) + private void RemoveMarkings(Entity ent, Entity target) { + if (!Resolve(target, ref target.Comp)) + return; + foreach (var marking in ent.Comp.AppliedMarkings) { if (!_marking.TryGetMarking(marking, out var proto)) @@ -221,6 +234,13 @@ public sealed class VisualBodySystem : SharedVisualBodySystem var layerId = $"{proto.ID}-{rsi.RsiState}"; + // If this marking is one that can be displaced, we need to remove the displacement as well; otherwise + // altering a marking at runtime can lead to the renderer falling over. + // The Vulps must be shaved. + // (https://github.com/space-wizards/space-station-14/issues/40135). + if (proto.CanBeDisplaced) + _displacement.EnsureDisplacementIsNotOnSprite((target, target.Comp), layerId); + if (!_sprite.LayerMapTryGet(target, layerId, out var index, false)) continue; diff --git a/Content.Shared/Body/VisualOrganMarkingsComponent.cs b/Content.Shared/Body/VisualOrganMarkingsComponent.cs index af79fd1830..31c34a42f9 100644 --- a/Content.Shared/Body/VisualOrganMarkingsComponent.cs +++ b/Content.Shared/Body/VisualOrganMarkingsComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.DisplacementMap; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Markings; using Robust.Shared.GameStates; @@ -37,6 +38,13 @@ public sealed partial class VisualOrganMarkingsComponent : Component [DataField, AutoNetworkedField] public Dictionary> DependentHidingLayers = new(); + /// + /// Optional displacement data for this organ to apply to markings. + /// Only applies to markings which support displacement data. + /// + [DataField, AutoNetworkedField] + public Dictionary MarkingsDisplacement = new(); + /// /// Client only - the last markings applied by this component /// diff --git a/Resources/Prototypes/Body/Species/vox.yml b/Resources/Prototypes/Body/Species/vox.yml index b947b3e15f..dabfebcb55 100644 --- a/Resources/Prototypes/Body/Species/vox.yml +++ b/Resources/Prototypes/Body/Species/vox.yml @@ -314,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 c4e1577149..616b371361 100644 --- a/Resources/Prototypes/Body/Species/vulpkanin.yml +++ b/Resources/Prototypes/Body/Species/vulpkanin.yml @@ -268,6 +268,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 ] From aee7808e96dc6c73a48404369596dab27ba9d9be Mon Sep 17 00:00:00 2001 From: Perry Fraser Date: Thu, 16 Apr 2026 18:24:20 -0400 Subject: [PATCH 187/247] fix: allow opening doors with open wire panels (#38733) * fix: allow opening doors with open wire panels * fix: set wire panel sprite state on airlock close * fix: make airlock wire panels animate better * removed unused dependency --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Client/Doors/AirlockSystem.cs | 5 +++ Content.Client/Doors/DoorSystem.cs | 8 +++-- Content.Server/Doors/Systems/AirlockSystem.cs | 15 --------- Content.Server/Wires/WiresSystem.cs | 14 +++----- .../Doors/Components/AirlockComponent.cs | 9 ++++- .../Doors/Components/DoorComponent.cs | 4 +-- Content.Shared/Wires/SharedWiresSystem.cs | 31 ++++++++++++++++++ .../components/wires-panel-component.ftl | 2 ++ .../Doors/Airlocks/Glass/glass.rsi/meta.json | 7 ++-- .../Airlocks/Glass/glass.rsi/panel_closed.png | Bin 269 -> 0 bytes .../{closing-panel.png => panel_closing.png} | Bin .../Airlocks/Glass/glass.rsi/panel_open.png | Bin 150 -> 269 bytes .../{opening-panel.png => panel_opening.png} | Bin 13 files changed, 60 insertions(+), 35 deletions(-) delete mode 100644 Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/panel_closed.png rename Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/{closing-panel.png => panel_closing.png} (100%) rename Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/{opening-panel.png => panel_opening.png} (100%) diff --git a/Content.Client/Doors/AirlockSystem.cs b/Content.Client/Doors/AirlockSystem.cs index 7be15bd88f..cc1f918f01 100644 --- a/Content.Client/Doors/AirlockSystem.cs +++ b/Content.Client/Doors/AirlockSystem.cs @@ -61,6 +61,11 @@ public sealed class AirlockSystem : SharedAirlockSystem if (!comp.AnimatePanel) return; + // For some reason the open panel sprite is used for both open and + // closed sprites. I really don't get it. + door.OpenSpriteStates.Add((WiresVisualLayers.MaintenancePanel, comp.OpenPanelSpriteState)); + door.ClosedSpriteStates.Add((WiresVisualLayers.MaintenancePanel, comp.OpenPanelSpriteState)); + ((Animation)door.OpeningAnimation).AnimationTracks.Add(new AnimationTrackSpriteFlick() { LayerKey = WiresVisualLayers.MaintenancePanel, diff --git a/Content.Client/Doors/DoorSystem.cs b/Content.Client/Doors/DoorSystem.cs index 0f6537eead..e6e71fd148 100644 --- a/Content.Client/Doors/DoorSystem.cs +++ b/Content.Client/Doors/DoorSystem.cs @@ -23,8 +23,8 @@ public sealed class DoorSystem : SharedDoorSystem 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)); @@ -108,6 +108,9 @@ public sealed class DoorSystem : SharedDoorSystem case DoorState.Open: 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); } @@ -115,6 +118,7 @@ public sealed class DoorSystem : SharedDoorSystem case DoorState.Closed: foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates) { + _sprite.LayerSetAutoAnimated((entity.Owner, sprite), layer, true); _sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState); } diff --git a/Content.Server/Doors/Systems/AirlockSystem.cs b/Content.Server/Doors/Systems/AirlockSystem.cs index 7dfdebf367..1bf19a4ece 100644 --- a/Content.Server/Doors/Systems/AirlockSystem.cs +++ b/Content.Server/Doors/Systems/AirlockSystem.cs @@ -12,8 +12,6 @@ namespace Content.Server.Doors.Systems; public sealed class AirlockSystem : SharedAirlockSystem { - [Dependency] private readonly WiresSystem _wiresSystem = default!; - public override void Initialize() { base.Initialize(); @@ -58,19 +56,6 @@ public sealed class AirlockSystem : SharedAirlockSystem 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 diff --git a/Content.Server/Wires/WiresSystem.cs b/Content.Server/Wires/WiresSystem.cs index c9fd429c47..e4d7861029 100644 --- a/Content.Server/Wires/WiresSystem.cs +++ b/Content.Server/Wires/WiresSystem.cs @@ -28,7 +28,6 @@ public sealed class WiresSystem : SharedWiresSystem [Dependency] private readonly HandsSystem _hands = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ConstructionSystem _construction = default!; @@ -448,7 +447,7 @@ public sealed class WiresSystem : SharedWiresSystem { if (TryComp(args.User, out ActorComponent? actor)) { - _uiSystem.OpenUi(uid, WiresUiKey.Key, actor.PlayerSession); + UI.OpenUi(uid, WiresUiKey.Key, actor.PlayerSession); args.Handled = true; } } @@ -459,7 +458,7 @@ public sealed class WiresSystem : SharedWiresSystem if (args.Open) return; - _uiSystem.CloseUi(ent.Owner, WiresUiKey.Key); + UI.CloseUi(ent.Owner, WiresUiKey.Key); } private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args) @@ -548,7 +547,7 @@ public sealed class WiresSystem : SharedWiresSystem statuses.Sort((a, b) => a.position.CompareTo(b.position)); - _uiSystem.SetUiState((uid, ui), WiresUiKey.Key, new WiresBoundUserInterfaceState( + UI.SetUiState((uid, ui), WiresUiKey.Key, new WiresBoundUserInterfaceState( clientList.ToArray(), statuses.Select(p => new StatusEntry(p.key, p.value)).ToArray(), Loc.GetString(wires.BoardName), @@ -556,11 +555,6 @@ public sealed class WiresSystem : SharedWiresSystem wires.WireSeed)); } - public void OpenUserInterface(EntityUid uid, ICommonSession player) - { - _uiSystem.OpenUi(uid, WiresUiKey.Key, player); - } - /// /// Tries to get a wire on this entity by its integer id. /// @@ -602,7 +596,7 @@ public sealed class WiresSystem : SharedWiresSystem if (!args.WiresAccessible) { - _uiSystem.CloseUi(uid, WiresUiKey.Key); + UI.CloseUi(uid, WiresUiKey.Key); } } diff --git a/Content.Shared/Doors/Components/AirlockComponent.cs b/Content.Shared/Doors/Components/AirlockComponent.cs index 6b3fcfad7e..f8357dd954 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. /// @@ -115,6 +115,13 @@ public sealed partial class AirlockComponent : Component [DataField] public string OpeningPanelSpriteState = "panel_opening"; + /// + /// The sprite state to use for the wire panel when the airlock is open. The + /// first frame will be used for when the airlock is closed. + /// + [DataField] + public string OpenPanelSpriteState = "panel_open"; + /// /// The sprite state used to animate the airlock frame when the airlock closes. /// diff --git a/Content.Shared/Doors/Components/DoorComponent.cs b/Content.Shared/Doors/Components/DoorComponent.cs index e66dff2611..c071b76eaa 100644 --- a/Content.Shared/Doors/Components/DoorComponent.cs +++ b/Content.Shared/Doors/Components/DoorComponent.cs @@ -153,7 +153,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 +166,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/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index c4f860e165..b812115211 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -4,7 +4,10 @@ using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Tools.Systems; using Content.Shared.UserInterface; +using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; +using Robust.Shared.Player; +using Robust.Shared.Utility; namespace Content.Shared.Wires; @@ -15,6 +18,7 @@ public abstract class SharedWiresSystem : EntitySystem [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedToolSystem Tool = default!; + [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; public override void Initialize() { @@ -24,6 +28,7 @@ public abstract class SharedWiresSystem : EntitySystem SubscribeLocalEvent(OnPanelDoAfter); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent>(OnGetVerbs); SubscribeLocalEvent(OnAttemptOpenActivatableUI); SubscribeLocalEvent(OnActivatableUIPanelChanged); @@ -96,6 +101,32 @@ public abstract class SharedWiresSystem : EntitySystem } } + private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!IsPanelOpen(ent.Owner)) + return; + + var actor = args.User; + var verb = new AlternativeVerb + { + Text = Loc.GetString("wires-panel-verb-view-panel"), + Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/screwdriver.png")), + Act = () => OpenUserInterface(ent, actor), + }; + + args.Verbs.Add(verb); + } + + public void OpenUserInterface(EntityUid uid, EntityUid actor) + { + UI.OpenUi(uid, WiresUiKey.Key, actor); + } + + public void OpenUserInterface(EntityUid uid, ICommonSession player) + { + UI.OpenUi(uid, WiresUiKey.Key, player); + } + public void ChangePanelVisibility(EntityUid uid, WiresPanelComponent component, bool visible) { component.Visible = visible; diff --git a/Resources/Locale/en-US/wires/components/wires-panel-component.ftl b/Resources/Locale/en-US/wires/components/wires-panel-component.ftl index ffe9741c28..a982eeaf6f 100644 --- a/Resources/Locale/en-US/wires/components/wires-panel-component.ftl +++ b/Resources/Locale/en-US/wires/components/wires-panel-component.ftl @@ -1,6 +1,8 @@ wires-panel-component-on-examine-open = The [color=lightgray]maintenance panel[/color] is [color=red]open[/color]. wires-panel-component-on-examine-closed = The [color=lightgray]maintenance panel[/color] is [color=darkgreen]closed[/color]. +wires-panel-verb-view-panel = View maintenance panel + # wire colors wire-name-color-red = Red diff --git a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/meta.json b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/meta.json index 9bb7e9bb72..a89eb6f559 100644 --- a/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/meta.json +++ b/Resources/Textures/Structures/Doors/Airlocks/Glass/glass.rsi/meta.json @@ -31,9 +31,6 @@ { "name": "closed-glass" }, - { - "name": "panel_closed" - }, { "name": "closing", "delays": [ @@ -87,7 +84,7 @@ ] }, { - "name": "closing-panel", + "name": "panel_closing", "delays": [ [ 0.1, @@ -168,7 +165,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 7fe48cc120a834ec52191ebc3afe7870f9c21905..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D7fF# z#WAE}&f6=7T!#!q*dEwR%z85^c?zGb%pNy>1-S!9FFG!;Im?aJ{u(2?UW!|!%#BwZZOC?|S+77N8)5>M| z{nxDLdhYvrEcOpyIxhfB&nd0PPd7($8u&nY$uqzxk#`rxOwM;0aY=)Jj-w@0gVX&bFERoKi*QxK02UwPOOyb zFI^`H1LxubXsCQd3>e?+&khkGzRCUnC1d~t7!W!yYK1hNsBEdb00000NkvXXu0mjf D@#kYM delta 133 zcmeBWn#MRmqK<``fq|hhB>gy$k`C|*aXoP0fVr-&v9YlS1H-Bf?D~c*)+{W3JUj{P z?8Yeq>_BCVB|(0{3=Yq3qyagao-U3d8WWQfBv?5PtmsiYAk?IGK+4O5b+*tf2J1Fc i-WN Date: Thu, 16 Apr 2026 22:38:09 +0000 Subject: [PATCH 188/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 71aaa1b19d..0453b1f591 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4030,3 +4023,12 @@ 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 From bcdfaa587fa6f9ef2ff7de1c0ab4c900507a1d8b Mon Sep 17 00:00:00 2001 From: Rachel Lowe <60523642+11BelowStudio@users.noreply.github.com> Date: Fri, 17 Apr 2026 03:37:44 +0100 Subject: [PATCH 189/247] Solving #42844 by adjusting base armour inheritances (#42862) * Solving #42844 by adjusting base armour inheritances * cardboard armour is now minor contraband * ERT chest rig is now CentComm contraband This originally was syndie contraband, but I figured I may as well fix this oversight whilst I was here (same sort of bugfix, y'know?) * remove minor contra --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Prototypes/Entities/Clothing/Belt/belts.yml | 2 +- .../Entities/Clothing/OuterClothing/armor.yml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml index 9c62774185..2fa68934fa 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/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index e1cdba065e..88a6a51cfd 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. From 313ca83bae486a517c1a2a755efbb71465a6433f Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 17 Apr 2026 02:54:14 +0000 Subject: [PATCH 190/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0453b1f591..db027bf3f9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,14 +1,4 @@ Entries: -- 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! @@ -4032,3 +4022,13 @@ 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 From 86f4f937af30aecd24fc82c0ac60474cd739e866 Mon Sep 17 00:00:00 2001 From: ThatGuyUSA Date: Thu, 16 Apr 2026 20:28:46 -0700 Subject: [PATCH 191/247] Elite webvest has pockets again (#43615) pockets --- Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index 88a6a51cfd..b7a1e3cd30 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -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. From 2dd891c41ae38a355d201a8f7bb66893df70d3c5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 17 Apr 2026 03:42:56 +0000 Subject: [PATCH 192/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index db027bf3f9..69bfe26358 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4032,3 +4025,10 @@ 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 From c9375e1e95868b1badbcb8e0b65573663b669bb6 Mon Sep 17 00:00:00 2001 From: Jessica M Date: Fri, 17 Apr 2026 03:16:05 -0700 Subject: [PATCH 193/247] Port door animation fix from ephemeral space (#43613) * port ES animation fix * close sound too :) * pump up the jam * worlds most minor sloptimization * silly --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Client/Doors/DoorSystem.cs | 58 +++++++++++++++--- .../Doors/Components/DoorComponent.cs | 12 +++- Resources/Audio/Machines/airlock_close.ogg | Bin 22530 -> 14723 bytes Resources/Audio/Machines/attributions.yml | 7 ++- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/Content.Client/Doors/DoorSystem.cs b/Content.Client/Doors/DoorSystem.cs index e6e71fd148..cba9ba1d49 100644 --- a/Content.Client/Doors/DoorSystem.cs +++ b/Content.Client/Doors/DoorSystem.cs @@ -18,6 +18,7 @@ public sealed class DoorSystem : SharedDoorSystem { base.Initialize(); SubscribeLocalEvent(OnAppearanceChange); + SubscribeLocalEvent(OnAnimationCompleted); } protected override void OnComponentInit(Entity ent, ref ComponentInit args) @@ -78,6 +79,32 @@ public sealed class DoorSystem : SharedDoorSystem }; } + private void OnAnimationCompleted(Entity ent, ref AnimationCompletedEvent args) + { + if (args.Key != DoorComponent.OpenCloseKey || !TryComp(ent, out var sprite)) + return; + + switch (ent.Comp.State) + { + case DoorState.Open: + + foreach (var (layer, layerState) in ent.Comp.OpenSpriteStates) + { + _sprite.LayerSetRsiState((ent.Owner, sprite), layer, layerState); + } + + break; + case DoorState.Closed: + + foreach (var (layer, layerState) in ent.Comp.ClosedSpriteStates) + { + _sprite.LayerSetRsiState((ent.Owner, sprite), layer, layerState); + } + + break; + } + } + private void OnAppearanceChange(Entity entity, ref AppearanceChangeEvent args) { if (args.Sprite == null) @@ -89,9 +116,6 @@ public sealed class DoorSystem : SharedDoorSystem if (AppearanceSystem.TryGetData(entity, PaintableVisuals.Prototype, out var prototype, args.Component)) UpdateSpriteLayers((entity.Owner, args.Sprite), prototype); - if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey)) - _animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey); - // We are checking beforehand since some doors may not have an emagging visual layer, and we don't want LayerSetVisible to throw an error. if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false)) _sprite.LayerSetVisible(entity.Owner, DoorVisualLayers.BaseEmagging, state == DoorState.Emagging); @@ -106,6 +130,9 @@ public sealed class DoorSystem : SharedDoorSystem switch (state) { case DoorState.Open: + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey)) + return; + foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates) { // Allow animations to play while it's open (e.g., pinion); @@ -116,6 +143,9 @@ public sealed class DoorSystem : SharedDoorSystem 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); @@ -127,24 +157,36 @@ public sealed class DoorSystem : SharedDoorSystem if (entity.Comp.OpeningAnimationTime == TimeSpan.Zero) return; - _animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.AnimationKey); + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey)) + return; + + _animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.OpenCloseKey); return; case DoorState.Closing: - if (entity.Comp.ClosingAnimationTime == TimeSpan.Zero || entity.Comp.CurrentlyCrushing.Count != 0) + if (entity.Comp.ClosingAnimationTime == TimeSpan.Zero) return; - _animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.AnimationKey); + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenCloseKey)) + return; + + _animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.OpenCloseKey); return; case DoorState.Denying: - _animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.AnimationKey); + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.DenyKey)) + return; + + _animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.DenyKey); return; case DoorState.Emagging: + if (_animationSystem.HasRunningAnimation(entity, DoorComponent.EmagKey)) + return; + // We are checking beforehand since some doors may not have an emagging visual layer. if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false)) - _animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.AnimationKey); + _animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.EmagKey); return; } diff --git a/Content.Shared/Doors/Components/DoorComponent.cs b/Content.Shared/Doors/Components/DoorComponent.cs index c071b76eaa..057fd5982d 100644 --- a/Content.Shared/Doors/Components/DoorComponent.cs +++ b/Content.Shared/Doors/Components/DoorComponent.cs @@ -140,7 +140,17 @@ public sealed partial class DoorComponent : Component /// /// The key used when playing door opening/closing/emagging/deny animations. /// - public const string AnimationKey = "door_animation"; + public const string OpenCloseKey = "door_animation_openclose"; + + /// + /// The key used when playing door deny animations. + /// + public const string DenyKey = "door_animation_deny"; + + /// + /// The key used when playing door emag animations. + /// + public const string EmagKey = "door_animation_emag"; /// /// The sprite state used for the door when it's open. diff --git a/Resources/Audio/Machines/airlock_close.ogg b/Resources/Audio/Machines/airlock_close.ogg index 5da73f770e82a25d7c6d5b4b051bef7c4dc305f5..5b818eeefae6fe028a6422f83cd3684807e7490c 100644 GIT binary patch delta 11043 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 AP5=M^ delta 18912 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 Date: Fri, 17 Apr 2026 10:32:24 +0000 Subject: [PATCH 194/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 69bfe26358..fa4653a690 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4032,3 +4025,12 @@ 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 From fa1e588ca9ef5853387503da675426bb63217668 Mon Sep 17 00:00:00 2001 From: Pok <113675512+Pok27@users.noreply.github.com> Date: Fri, 17 Apr 2026 15:38:07 +0300 Subject: [PATCH 195/247] =?UTF-8?q?=D0=A1=D0=A0=D0=9F=20=D0=94=D0=A1=D0=9E?= =?UTF-8?q?=20(#3566)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🚨не слефмерж (почти)🚨 --- .../Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl | 1 + .../ru-RU/corvax/guidebook/SOP/codes.ftl | 24 +- .../ru-RU/corvax/guidebook/SOP/command.ftl | 4 +- .../Locale/ru-RU/corvax/guidebook/SOP/dso.ftl | 248 ++++++++++++++++++ .../ru-RU/corvax/guidebook/SOP/service.ftl | 17 +- Resources/Prototypes/Corvax/Guidebook/SOP.yml | 6 + .../ServerInfo/Corvax/Guidebook/SOP/DSO.xml | 138 ++++++++++ .../Corvax/Guidebook/SOP/Engineering.xml | 2 - .../Corvax/Guidebook/SOP/Service.xml | 3 - 9 files changed, 424 insertions(+), 19 deletions(-) create mode 100644 Resources/Locale/ru-RU/corvax/guidebook/SOP/dso.ftl create mode 100644 Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl index 39ed5243e9..46433b5b23 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/SOP.ftl @@ -19,6 +19,7 @@ guide-entry-sop-engineering = Инженерный отдел guide-entry-sop-security = Служба безопасности guide-entry-sop-command = Командование guide-entry-sop-centcomm = Центральное Командование +guide-entry-sop-dso = Департамент Специальных Операций guide-entry-sop-legal = Юридический департамент guide-entry-sop-general = Общее guide-entry-sop-codes = Уровни угроз diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl index 07d07dc36c..f5c74ce08e 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/codes.ftl @@ -12,10 +12,11 @@ guidebook-SOP-codes-green = {"[bold]Устанавливается по решению:[/bold] капитана или главы службы безопасности."} guidebook-SOP-codes-green-terms = - На станции отсутствует угроза безопасности — штатный режим работы. + - На станции отсутствуют угрозы безопасности. guidebook-SOP-codes-green-norms = - Экипаж действует в соответствии со стандартными рабочими процедурами. + - Экипаж может свободно передвигаться по станции в соответствии с уровнем доступа. + - Ношение ID-карты в специальном слоте комбинезона по усмотрению. # Синий код guidebook-SOP-codes-blue = @@ -24,10 +25,25 @@ guidebook-SOP-codes-blue = {"[bold]Устанавливается по решению:[/bold] капитана или главы службы безопасности."} guidebook-SOP-codes-blue-terms = - На станции существует угроза безопасности I уровня. + На станции существует угроза безопасности I уровня — угроза, не требующая содействия всего экипажа, которая может быть устранена силами службы безопасности. + + Примеры: + - Вторжение агрессивной фауны, за исключением космического дракона. + - Кража особо ценного имущества, исключая диск ядерной авторизации. + - Взлом защищенной стратегической точки. + - Угон приписанного к станции шаттла. + - Взлом станционного ИИ. + - Убийство должностного лица. + - Убийство 3-4 членов экипажа. + - Пропажа нескольких человек и т.д. guidebook-SOP-codes-blue-norms = - Экипаж должен следовать указаниям службы безопасности и докладывать о подозрительной активности. + - Экипаж может свободно передвигаться по станции в соответствии с уровнем доступа, капитан или глава службы безопасности уполномочены утвердить зоны, закрытые для посещения. + - Экипаж должен докладывать о любой подозрительной активности службе безопасности лично или в общий голосовой канал. + - Все члены экипажа обязаны носить ID-карты в соответствующем слоте комбинезона (внутри КПК, либо отдельно). + - Члены экипажа обязаны отреагировать на звук свистка и прекратить движение в ожидании распоряжений сотрудников службы безопасности. + - Разрешено продолжить движение в случае отсутствия указаний от сотрудников службы безопасности в течение 15 секунд. + - Сотрудникам медицинского и инженерного отделов разрешено игнорировать свисток при выполнении срочных задач. # Красный код guidebook-SOP-codes-red = diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl index 225ae583f7..f2b3942796 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/command.ftl @@ -114,7 +114,7 @@ guidebook-SOP-command-procedure-appoint-acting-captain = ## Процедура отстранения капитана от командования guidebook-SOP-command-procedure-remove-captain = 1. Капитан может быть уволен по собственному желанию, по приказу Центрального Командования или при решении не анонимного голосования глав в соответствии с причинами отстранения от должности. - 1. ВрИО капитан обязан вернуться на свою изначальную должность после прибытия нового капитана с Центрального Командования. + - ВрИО капитан обязан вернуться на свою изначальную должность после прибытия нового капитана с Центрального Командования. 1. При голосовании необходимо большинство голосов глав отделов «ЗА» отстранение капитана. - При равном количестве голосов «ЗА» и «ПРОТИВ» отстранение капитана отменяется. - Воздержание от голоса, отказ голосовать и попытка сорвать голосование считается голосом «ЗА». @@ -229,7 +229,7 @@ guidebook-SOP-command-procedure-reasons-for-removal = ## Протоколы эвакуации ### Окончание смены guidebook-SOP-command-protocol-evacuation-end-of-shift = - 1. После окончания смены начинается стандартный процесс отправки экипажа на станцию центрального командования для последующего распределения и отдыха. Отбытие производится спустя 2 часа после начала смены или после выполнения дополнительной цели смены. + После окончания смены начинается стандартный процесс отправки экипажа на станцию центрального командования для последующего распределения и отдыха. Отбытие производится спустя 2 часа после начала смены или после выполнения дополнительной цели смены. - Капитан обязан составить письменный отчёт о выполнении основной или дополнительной цели и отправить его на центральное командование. - Капитан обязан уведомить персонал соответствующим сообщением для подготовки к отбытию. - Службе безопасности необходимо организовать транспортировку всех заключённых; во время перелёта заключённые должны находиться в отсеке для заключённых. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/dso.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/dso.ftl new file mode 100644 index 0000000000..083c8cbc4b --- /dev/null +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/dso.ftl @@ -0,0 +1,248 @@ +# Общие СРП +guidebook-SOP-dso-general-must = + 1. Переключить датчики на комбинезоне в режим «координаты». При возможности держаться группой, если не дано других распоряжений от лидера. + 1. Исполнять приказы старшего по званию, соблюдать субординацию и использовать уставную речь. + - Допускается пренебрежение уставной речью при чрезвычайной ситуации. + 1. В случае пропажи лидера отряда, старший по званию обязан занять его место. + 1. Следить за своим снаряжением и не допускать его попадания в руки экипажа станции или противника. + 1. Выполнять свои обязанности по своей специализации, пока лидер ОБР не выдал других распоряжений. + +guidebook-SOP-dso-general-right = + 1. Отдавать приказы всем членам экипажа станции. + 1. Принимать любые меры для выполнения поставленной задачи. + 1. Нарушать стандартные процедуры экипажа при чрезвычайной ситуации. + 1. В полной мере руководить экипажем станции, если это необходимо для выполнения цели миссии, даже если исполнение приказа приведет к нарушению СРП со стороны члена экипажа. + +guidebook-SOP-dso-general-prohibited = + 1. Забирать снаряжение станции Центрального Командования, которое не находится в оружейной, лазарете или на складе материалов. + 1. Выполнять задания в нетрезвом виде. + 1. Менять свою рабочую форму. + +# Должностные СРП +## Командир РХБЗЗ +guidebook-SOP-CBURNCommander-must = + 1. Эффективно руководить отрядом для выполнения поставленной задачи. + 1. Поддерживать дисциплину среди оперативников и пресекать нарушения, вплоть до применения летальной силы. + 1. Инициировать и координировать карантинные меры на станции при подозрении на биологическую угрозу, включая закрытие секций станции и установление зон полной изоляции. + +guidebook-SOP-CBURNCommander-right = + 1. Взять управление объектом на себя. + 1. Единолично снять неприкосновенность с члена экипажа. + 1. Установить карантин на станции при любой угрозе биологического или радиологического характера, включая полную изоляцию членов экипажа. + 1. Переместить любого сотрудника станции в карантин с последующей передачей его на ЦК. + +guidebook-SOP-CBURNCommander-prohibited = + 1. Игнорировать или откладывать приказы Департамента Специальных Операций. + 1. Покидать сектор станции до завершения поставленной миссии. + 1. Превышать свои полномочия/использовать их для собственной выгоды. + +## Агент РХБЗЗ +guidebook-SOP-CBURN-must = + 1. Следовать специальным рабочим процедурам, пока командованием ДСО не даны иные указания. + 1. Проводить процедуру задержания с соблюдением уровней применения силы. + - Пункт об уровнях применения силы не актуален, если на станции действует режим чрезвычайной ситуации. + 1. Исполнять приказы командира, если это не противоречит указаниям командования ДСО. + 1. Выполнять поставленную задачу. + +guidebook-SOP-CBURN-right = + 1. Использовать любое, в том числе и вражеское, вооружение вне зависимости от уровня угрозы на станции. + 1. Использовать любые доступные средства для локализации биологических угроз. + +guidebook-SOP-CBURN-prohibited = + 1. Нарушать герметичность скафандра путём отсоединения шлема где-либо, кроме шаттла отряда. + 1. Эвакуировать персонал станции на шаттл отряда. + - Приказ командира может отменить данный запрет. + +## Лидер ОБР +guidebook-SOP-ERTLeader-must = + 1. Следить за состоянием всего отряда и оперативно отдавать приказы для достижения поставленной задачи. + 1. Поддерживать связь с Центральным Командованием/Департаментом Специальных Операций любыми доступными способами. + 1. Оповестить экипаж станции о прибытии шаттла ОБР в сектор станции. + - Данный пункт теряет свою актуальность, если вредит/идёт вразрез интересам миссии. + 1. Проверить нахождение каждого члена отряда на шаттле перед отправкой на станцию. + 1. Следить за функционированием своего шаттла и поддержанием на нём чистоты и порядка. + +guidebook-SOP-ERTLeader-right = + 1. Изъять диск ядерной аутентификации, если на станции чрезвычайная ситуация. + 1. Единолично снять неприкосновенность с члена экипажа. + 1. Взять на себя командование станцией на время проведения операции. + 1. При нарушении КЗ, ОПРС и СРП ОБР разжаловать и арестовать любого оперативника отряда с обязательной доставкой на станцию Центрального Командования. + - Во время действия режима чрезвычайной ситуации допускается расстрел на месте. + 1. Принять решение об окончании смены и отправке шаттла эвакуации, если ситуация на станции критическая. + +guidebook-SOP-ERTLeader-prohibited = + 1. Игнорировать или откладывать приказы Департамента Специальных Операций. + 1. Покидать сектор станции до завершения поставленной миссии. + 1. Превышать свои полномочия/использовать их для собственной выгоды. + +## Священник ОБР +guidebook-SOP-ERTChaplain-must = + 1. Оказывать духовную поддержку членам отряда, помогая им сохранять моральный дух в стрессовых ситуациях. + 1. Выполнять обряды, способствующие защите от сверхъестественных угроз. + 1. Поддерживать религиозное оборудование и предметы в исправном состоянии и готовности к использованию. + +guidebook-SOP-ERTChaplain-right = + 1. Использовать религиозные предметы и реликвии для защиты от сверхъестественных и других угроз, если это потребуется. + 1. Проводить обряды и ритуалы по запросу членов отряда или в случаях, когда ситуация на станции требует духовного вмешательства. + 1. Запрашивать поддержку от других подразделений для обеспечения безопасности при проведении обрядов. + +guidebook-SOP-ERTChaplain-prohibited = + 1. Навязывать религиозные взгляды членам отряда или экипажа станции. + 1. Использовать религиозные предметы или обряды для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + 1. Игнорировать запросы на духовную поддержку или проведение обрядов от членов отряда. + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + +## Инженер ОБР +guidebook-SOP-ERTEngineer-must = + 1. Обеспечивать техническую поддержку отряда, ремонтируя оборудование и восстанавливая инфраструктуру станции. + 1. Работать над обеспечением безопасности инженерных систем и предотвращением аварийных ситуаций. + 1. Участвовать в оперативных задачах, связанных с восстановлением повреждённых объектов и систем. + +guidebook-SOP-ERTEngineer-right = + 1. Использовать инженерные инструменты и оборудование для выполнения своих задач. + 1. Запрашивать ресурсы и помощь от других подразделений для выполнения технических задач. + 1. Вносить предложения по улучшению инженерных систем и процедур на станции. + +guidebook-SOP-ERTEngineer-prohibited = + 1. Самовольно отключать или изменять настройки критически важных систем станции без согласования с лидером ОБР или ОСО. + 1. Игнорировать аварийные ситуации, связанные с инженерными системами станции. + 1. Использовать инженерные ресурсы для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + +## Медик ОБР +guidebook-SOP-ERTMedical-must = + 1. Оказывать медицинскую помощь членам отряда и экипажу станции в случае ранений или заболеваний. + 1. Обязаны отдавать приоритет лечению в следующем порядке: оказание помощи себе > члены отряда и иные сотрудники ДСО > члены экипажа станции. + 1. Поддерживать медицинское оборудование и медикаменты в исправном состоянии и готовности к использованию. + 1. Работать в тесном взаимодействии с медиками станции для обеспечения своевременного лечения. + +guidebook-SOP-ERTMedical-right = + 1. Использовать медицинские ресурсы для оказания помощи членам отряда и экипажу. + 1. Запрашивать помощь от других подразделений и отрядов для проведения медицинских операций. + 1. Принимать решения по эвакуации и лечению пострадавших в случае критической угрозы. + +guidebook-SOP-ERTMedical-prohibited = + 1. Пренебрегать оказанием помощи раненым и больным членам отряда или экипажу станции. + 1. Использовать медицинские ресурсы для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + 1. Игнорировать медицинские протоколы и процедуры, установленные для станции. + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + +## Офицер Безопасности ОБР +guidebook-SOP-ERTSecurity-must = + 1. Обеспечивать безопасность отряда и защищать станцию от внутренних и внешних угроз. + 1. Проводить патрулирование и мониторинг ключевых зон станции для предотвращения нарушений и своевременного реагирования на угрозы. + 1. Работать в тесном взаимодействии с другими отрядами и службами безопасности станции для поддержания порядка и устранения угроз. + +guidebook-SOP-ERTSecurity-right = + 1. Использовать оружие и оборудование для защиты станции и членов отряда от любых угроз. + 1. Запрашивать помощь от других подразделений и отрядов в случае серьёзной угрозы безопасности. + 1. Принимать решения по задержанию, нейтрализации и ликвидации опасных элементов на станции. + +guidebook-SOP-ERTSecurity-prohibited = + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + 1. Игнорировать угрозы безопасности станции или членов отряда, включая угрозы от зомби и агрессивной фауны. + 1. Использовать оружие и оборудование для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + +## Уборщик ОБР +guidebook-SOP-ERTJanitor-must = + 1. Обеспечивать чистоту и порядок в зонах, где работает отряд, включая уборку биологических, химических и иных загрязнений, которые могут представлять угрозу для станции. + 1. Работать над предотвращением распространения загрязнения на станции во избежание создания опасных ситуаций для членов отряда и экипажа станции. + 1. Поддерживать уборочное оборудование, включая специализированные устройства для очистки особо опасных зон, в исправном состоянии и готовности к использованию. + +guidebook-SOP-ERTJanitor-right = + 1. Использовать специализированное оборудование и химикаты для очистки зон, подверженных загрязнению. + 1. Запрашивать ресурсы и помощь от других подразделений для выполнения задач по уборке. + 1. Принимать меры по изоляции и очистке зон с высоким уровнем загрязнения, в том числе биологических и химических угроз. + +guidebook-SOP-ERTJanitor-prohibited = + 1. Пренебрегать обязанностями по поддержанию чистоты и порядка в зонах, где работает отряд. + 1. Использовать химикаты и оборудование для целей, не связанных с выполнением задач ОБР. + - Данный пункт теряет свою актуальность, если мешает исполнению обязанностей. + 1. Игнорировать правила безопасности при работе с опасными веществами и биологическими угрозами. + 1. Применять чрезмерную силу без веской на то причины, особенно в отношении членов экипажа. + +# Лидер Эскадрона Смерти — Foxtrot +guidebook-SOP-DeathSquadFoxtrotLeader-must = + 1. Руководить операцией по спасению экипажа и устранению угроз с максимальной решимостью и ответственностью. + 1. Координировать действия бойцов, обеспечивая их взаимодействие и максимальную эффективность в бою. + 1. Поддерживать связь с ДСО для получения указаний и предоставления отчётов о ходе операции. + 1. Обеспечить сохранение ключевых объектов станции, предотвращая их разрушение. + 1. Проводить эвакуацию экипажа и важного имущества станции в случае, если ситуация выходит из-под контроля. + 1. Лично принимать важные стратегические решения в сложных и критических ситуациях, касающихся спасения персонала. + +guidebook-SOP-DeathSquadFoxtrotLeader-right = + 1. Принимать на себя командование станцией и отдавать приказы всем членам экипажа для координации действий по спасению. + 1. Координировать эвакуацию и принимать решение об отправке шаттла для спасения экипажа. + 1. Использовать любые доступные ресурсы станции для выполнения миссии, в том числе оборудование, защищённое или заблокированное для обычных сотрудников. + 1. Принимать решения по эвакуации экипажа и активации аварийных протоколов, если ситуация критическая. + 1. Использовать любые методы для устранения угроз, при этом минимизируя потери среди экипажа. + +guidebook-SOP-DeathSquadFoxtrotLeader-prohibited = + 1. Игнорировать приказы ДСО или ставить личные цели выше миссии спасения. + 1. Вступать в контакт с врагом, если это ставит под угрозу успех операции или жизнь экипажа. + +# Оперативник Эскадрона Смерти — Foxtrot +guidebook-SOP-DeathSquadFoxtrot-must = + 1. Исполнять приказы Лидера Отряда "Фокстрот" и строго следовать инструкциям по защите экипажа и уничтожению угроз. + 1. Обеспечивать безопасность и спасение экипажа станции любой ценой. + 1. Вступать в бой с любой угрозой, не колеблясь, и уничтожать её. + 1. Эвакуировать пострадавших и обеспечивать их безопасность до полной ликвидации угрозы. + 1. Следить за своим снаряжением и вооружением, обеспечивая его готовность к любым возможным ситуациям. + 1. Принимать участие в эвакуации членов экипажа и сохранении имущества станции в случае экстренных ситуаций. + +guidebook-SOP-DeathSquadFoxtrot-right = + 1. Отдавать приказы любому члену экипажа станции, если это необходимо для успешного выполнения задачи спасения. + 1. Использовать любое доступное оборудование или вооружение, включая закрытые зоны станции, для ликвидации угроз. + 1. Применять силу, если это необходимо для защиты членов экипажа или в случае прямой угрозы. + +guidebook-SOP-DeathSquadFoxtrot-prohibited = + 1. Покидать зону операции без разрешения Лидера Отряда. + 1. Отступать или отказываться от выполнения миссии по спасению, если существует хоть малейший шанс спасти кого-то из экипажа. + 1. Использовать боевое снаряжение для целей, не связанных с миссией спасения. + 1. Нарушать субординацию и не выполнять приказы Лидера, если это не угрожает непосредственной безопасности миссии. + +# Лидер Эскадрона Смерти — Tango +guidebook-SOP-DeathSquadTangoLeader-must = + 1. Руководить операцией по ликвидации всего экипажа станции, строго следуя приказам, полученным от Департамента Специальных Операций NanoTrasen. + 1. Обеспечивать слаженность и координацию действий всех членов эскадрона, гарантируя выполнение поставленных задач в кратчайшие сроки. + 1. Контролировать оснащение и боевую готовность оперативников эскадрона, включая использование имплантов, стимуляторов и новейших вооружений. + 1. Поддерживать оперативную связь с ОСО для получения дальнейших указаний в случае непредвиденных обстоятельств. + 1. В случае особых условий принимать решение о временном контакте с экипажем, если это необходимо для его более эффективного устранения. + +guidebook-SOP-DeathSquadTangoLeader-right = + 1. Отдавать приказы всем оперативникам эскадрона и требовать их безусловного исполнения. + 1. Единолично принимать решения о ходе выполнения миссии, включая изменение планов и приоритетов, если это не идёт вразрез с приказами ОСО. + 1. Использовать любые доступные ресурсы и оборудование станции для выполнения задачи, включая взлом, проникновение или захват стратегически важных объектов. + 1. Принимать тактические решения на месте, изменяя план операции в зависимости от обстоятельств, но всегда соблюдая основную цель — ликвидацию всех разумных существ и синтетиков. + 1. Послать запрос ОСО на полное уничтожение объекта. + - Уничтожение объекта допускается исключительно по приказу ОСО, как крайняя мера, если выполнение задачи и обеспечение секретности невозможно иным способом, поскольку это несёт значительный ущерб имуществу NanoTrasen. + +guidebook-SOP-DeathSquadTangoLeader-prohibited = + 1. Устанавливать контакт с экипажем станции, кроме как для более эффективного его устранения. + 1. Нарушать полученные приказы от Департамента Специальных Операций без особых обстоятельств, угрожающих выполнению миссии. + 1. Проявлять инициативу в уничтожении станции без прямого приказа, если это не является крайней мерой и не согласовано с командованием. + 1. Злоупотреблять использованием оборудования или имплантов, которые не имеют прямой необходимости в операции. + +# Оперативник Эскадрона Смерти — Tango +guidebook-SOP-DeathSquadTango-must = + 1. Ликвидировать всех свидетелей на станции, включая экипаж, персонал, любые формы жизни, и удалить ИИ у всех синтетов на станции, если таковые имеются — не оставляя ни одного свидетеля. + 1. Использовать всё доступное снаряжение, включая импланты, стимуляторы и любые виды вооружения, для выполнения своей задачи. + 1. Постоянно докладывать командующему офицеру о ходе выполнения миссии и всех возникших осложнениях. + 1. Устранять любые следы присутствия или действий отряда на станции, включая уничтожение данных и улик. + 1. При обнаружении несанкционированного доступа на станцию или посторонних лиц, оперативники Эскадрона Смерти обязаны уничтожить нарушителей без предупреждения. + +guidebook-SOP-DeathSquadTango-right = + 1. Может вступать в контакт с членами экипажа. + +guidebook-SOP-DeathSquadTango-prohibited = + 1. Вступать в длительные переговоры или сотрудничество с экипажем станции, за исключением случаев, когда это прямо способствует их уничтожению. + 1. Покидать станцию до полного выполнения задачи по ликвидации всех членов экипажа. + 1. Раскрывать информацию о цели миссии или структуре отряда. + +# Infobox +guidebook-SOP-dso-infobox = + {"[bold]Информацией на данной странице не обладает ни один сотрудник станции.[/bold]"} diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl index 7e9f742a12..5a2f7c0d99 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/service.ftl @@ -67,16 +67,17 @@ guidebook-SOP-RadioHost-prohibited = ## Сервисный работник guidebook-SOP-ServiceWorker-must = - 1. Пополнять торговые автоматы в пределах сервисного отдела. - 1. Своевременно отвечать на вызовы по рации для пополнения автоматов. - 1. Оказывать помощь в работе другим сотрудникам сервисного отдела с полным соблюдением их СРП. + 1. Выполнять поручения и задачи в пределах сервисного отдела. + 1. По решению главы персонала, капитана или по собственной инициативе в случае отсутствия сотрудников бара, кухни — исполнять обязанности шеф-повара, бармена. + - Допускается замещение других должностей сервисного отдела при согласовании с главой персонала. + - При замещении должности исполнять все СРП соответствующей роли. + - При замещении должности шеф-повара или бармена, сервисный работник обязан выполнять указания штатных сотрудников этой должности, если они не противоречат СРП и КЗ. + 1. Покинуть зону работы по требованию основного сотрудника этой зоны или по приказу главы персонала. guidebook-SOP-ServiceWorker-right = - 1. Запросить у главы персонала дополнительные доступы сервисного отдела для повышения эффективности работы. - 1. В свободное от обязанностей время проходить обучение навыкам сервисного отдела для повышения эффективности работы. - -guidebook-SOP-ServiceWorker-prohibited = - 1. Хранить при себе какие-либо предметы, регулируемые КЗ, разрешенные для использования другим сотрудникам отдела. + 1. Согласовать с главой персонала получение доступов в отделы сервиса, необходимые для эффективного выполнения обязанностей. + 1. Использовать оборудование, снаряжение и инвентарь замещаемой должности в рамках КЗ и СРП этой должности. + 1. Получить приписку к своей должности в соответствии с выполняемой ролью. Пример: "Повар — Сервисный работник". ## Клоун guidebook-SOP-Clown-must = diff --git a/Resources/Prototypes/Corvax/Guidebook/SOP.yml b/Resources/Prototypes/Corvax/Guidebook/SOP.yml index ba4cc9d75b..9a5306b500 100644 --- a/Resources/Prototypes/Corvax/Guidebook/SOP.yml +++ b/Resources/Prototypes/Corvax/Guidebook/SOP.yml @@ -10,6 +10,7 @@ - SOPEngineering - SOPCommand - SOPCentcomm + - SOPDSO - SOPLegal - SOPSecurity - SOPGeneral @@ -55,6 +56,11 @@ name: guide-entry-sop-centcomm text: "/ServerInfo/Corvax/Guidebook/SOP/Centcomm.xml" +- type: guideEntry + id: SOPDSO + name: guide-entry-sop-dso + text: "/ServerInfo/Corvax/Guidebook/SOP/DSO.xml" + - type: guideEntry id: SOPLegal name: guide-entry-sop-legal diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml new file mode 100644 index 0000000000..19d5ad7698 --- /dev/null +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/DSO.xml @@ -0,0 +1,138 @@ + + + + + + + + # Общие СРП + ### Оперативник ОБР обязан + + + ### Оперативник ОБР имеет право + + + ### Оперативнику ОБР запрещено + + + # Должностные СРП + ## СРП Командира РХБЗЗ + ### Командир РХБЗЗ обязан + + + ### Командир РХБЗЗ имеет право + + + ### Командиру РХБЗЗ запрещено + + + ## СРП Агента РХБЗЗ + ### Агент РХБЗЗ обязан + + + ### Агент РХБЗЗ имеет право + + + ### Агенту РХБЗЗ запрещено + + + ## СРП Лидера ОБР + ### Лидер ОБР обязан + + + ### Лидер ОБР имеет право + + + ### Лидеру ОБР запрещено + + + ## СРП Cвященника ОБР + ### Cвященник ОБР обязан + + + ### Cвященник ОБР имеет право + + + ### Cвященнику ОБР запрещено + + + ## СРП Инженера ОБР + ### Инженер ОБР обязан + + + ### Инженер ОБР имеет право + + + ### Инженеру ОБР запрещено + + + ## СРП Медика ОБР + ### Медик ОБР обязан + + + ### Медик ОБР право + + + ### Медику ОБР запрещено + + + ## СРП Офицера Безопасности ОБР + ### Офицер Безопасности ОБР обязан + + + ### Офицер Безопасности ОБР имеет право + + + ### Офицеру Безопасности ОБР запрещено + + + ## СРП Уборщика ОБР + ### Уборщик ОБР обязан + + + ### Уборщик ОБР имеет право + + + ### Уборщику ОБР запрещено + + + ## СРП Лидера Эскадрона Смерти — Foxtrot + ### Лидер Эскадрона Смерти обязан + + + ### Лидер Эскадрона Смерти имеет право + + + ### Лидеру Эскадрона Смерти запрещено + + + ## СРП Оперативника Эскадрона Смерти — Foxtrot + ### Оперативник Эскадрона Смерти обязан + + + ### Оперативник Эскадрона Смерти имеет право + + + ### Оперативнику Эскадрона Смерти запрещено + + + ## СРП Лидера Эскадрона Смерти — Tango + ### Лидер Эскадрона Смерти обязан + + + ### Лидер Эскадрона Смерти имеет право + + + ### Лидеру Эскадрона Смерти запрещено + + + ## СРП Оперативника Эскадрона Смерти — Tango + ### Оперативник Эскадрона Смерти обязан + + + ### Оперативник Эскадрона Смерти имеет право + + + ### Оперативнику Эскадрона Смерти запрещено + + diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml index 231ad0484e..58915ea46f 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Engineering.xml @@ -28,8 +28,6 @@ ### Ведущему инженеру запрещено - - ## СРП Инженера ### Инженер обязан diff --git a/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml b/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml index e55adf6313..ee4d5c2632 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/SOP/Service.xml @@ -27,9 +27,6 @@ ### Сервисный работник имеет право - ### Сервисному работнику запрещено - - ## СРП Клоуна ### Клоун обязан From eedae744f08e8d6227978cbec0dbfa8718d41581 Mon Sep 17 00:00:00 2001 From: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Date: Fri, 17 Apr 2026 20:39:11 +0100 Subject: [PATCH 196/247] Fixes Frezon in the guidebook (#43622) changes 8 to 50 --- Resources/ServerInfo/Guidebook/Engineering/Gasses.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml b/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml index a5fadbaa84..7e7afb4285 100644 --- a/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml +++ b/Resources/ServerInfo/Guidebook/Engineering/Gasses.xml @@ -98,7 +98,7 @@ Frezon is very complicated to create, requiring a mix of tritium, oxygen, and nitrogen, all tuned precisely to create the gas. If you're looking to create this gas yourself, here's what you need to know: - Frezon is produced by the combination of oxygen, tritium, and nitrogen. - - Tritium and oxygen are mixed at a ratio of around 1:8. + - Tritium and oxygen are mixed at a ratio of around 1:50. - Nitrogen is required as a catalyst for the reaction. The amount of nitrogen consumed is dependent on the efficiency of the reaction. - The frezon reaction only occurs at cryogenic temperatures, below 73.15 K. The efficency of the reaction (how much frezon is produced) is dependent on the temperature. The closer to 73.15 K, the more frezon is produced, and the less nitrogen is consumed. From 10257d5337c1323c4e2aab65e2518cd16261c5db Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 17 Apr 2026 19:53:04 +0000 Subject: [PATCH 197/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fa4653a690..cab43cbef1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4034,3 +4027,10 @@ 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 From 48c1f7753157f2caeb607ff68cfba0c28dda0e27 Mon Sep 17 00:00:00 2001 From: William Derksen Date: Sat, 18 Apr 2026 00:21:46 -0700 Subject: [PATCH 198/247] [Issue-43571] EmitterSystem should use DestructionEvent rather than DestructionAttempt Event (#43628) switched from DestructionAttemptEvent (which is cancellable) to DestructionEvent (which happens when the destruction was successful) --- Content.Server/Singularity/EntitySystems/EmitterSystem.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs index 6905b38b43..837ba05df1 100644 --- a/Content.Server/Singularity/EntitySystems/EmitterSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EmitterSystem.cs @@ -50,7 +50,7 @@ namespace Content.Server.Singularity.EntitySystems SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnAnchorStateChanged); SubscribeLocalEvent(OnSignalReceived); - SubscribeLocalEvent(OnDestructionAttempted); + SubscribeLocalEvent(OnDestruction); SubscribeLocalEvent(OnDeconstructed); // you shouldn't be able to deconstruct locked emitters but out of scope to fix SubscribeLocalEvent(OnLockToggled); } @@ -297,11 +297,9 @@ namespace Content.Server.Singularity.EntitySystems } } - private void OnDestructionAttempted(Entity ent, ref DestructionAttemptEvent args) + private void OnDestruction(Entity ent, ref DestructionEventArgs args) { - // warn engineering their containment engine needs IMMEDIATE repairs - // this doesn't change much for natural loosing through emitter destruction given any meteor warning serves the same purpose - // can also be used to scare engineering though given it broadcasts its location you need a renamed station beacon to really scare them + // Engineering needs to know if an emitter is destroyed so they can replace it before the engine looses. AlertRadio(ent, ent.Comp.LocDestroyed); } From ddefec17c3ad320ffa45e0b58a7b09f425bd79ae Mon Sep 17 00:00:00 2001 From: exo4ka <193421410+exo4ka@users.noreply.github.com> Date: Sat, 18 Apr 2026 12:06:53 +0300 Subject: [PATCH 199/247] pearl update 14.04.2026 (#3567) --- Resources/Maps/Corvax/corvax_pearl.yml | 1713 ++++++++++++++---------- 1 file changed, 969 insertions(+), 744 deletions(-) diff --git a/Resources/Maps/Corvax/corvax_pearl.yml b/Resources/Maps/Corvax/corvax_pearl.yml index fa22e6ffb4..4a65dd62b5 100644 --- a/Resources/Maps/Corvax/corvax_pearl.yml +++ b/Resources/Maps/Corvax/corvax_pearl.yml @@ -4,8 +4,8 @@ meta: engineVersion: 270.1.0 forkId: "" forkVersion: "" - time: 03/10/2026 11:10:13 - entityCount: 21696 + time: 04/15/2026 20:13:35 + entityCount: 21738 maps: - 1 grids: @@ -349,7 +349,7 @@ entities: version: 7 5,0: ind: 5,0 - tiles: BAAAAAAAAAEAAAAAAAAoAAAAAAAAgQAAAAAAAIEAAAAAAAAoAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAgQAAAAAAACgAAAAAAAAoAAAAAAAAgQAAAAAAACgAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAATAAAAAAAAEwAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAACBAAAAAAAAWAAAAAAAAFgAAAAAAABYAAAAAAAAgQAAAAAAAIEAAAAAAABMAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAgQAAAAAAAFgAAAAAAABYAAAAAAAAWAAAAAAAAFgAAAAAAACBAAAAAAAATAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAAIEAAAAAAABYAAAAAAAAWAAAAAAAAFgAAAAAAABYAAAAAAAAgQAAAAAAAEwAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAACgAAAAAAAIEAAAAAAAAKAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAWgAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== + tiles: BAAAAAAAAAEAAAAAAAAoAAAAAAAAgQAAAAAAAIEAAAAAAAAoAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAgQAAAAAAACgAAAAAAAAoAAAAAAAAgQAAAAAAACgAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAATAAAAAAAAEwAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAACBAAAAAAAAWAAAAAAAAFgAAAAAAABYAAAAAAAAgQAAAAAAAIEAAAAAAABMAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAAAzAAAAAAAAgQAAAAAAAFgAAAAAAABYAAAAAAAAWAAAAAAAAFgAAAAAAACBAAAAAAAATAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAoAAAAAAAAKAAAAAAAACgAAAAAAAIEAAAAAAABYAAAAAAAAWAAAAAAAAFgAAAAAAABYAAAAAAAAgQAAAAAAAEwAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAACgAAAAAAAIEAAAAAAAAKAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAWgAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== version: 7 5,1: ind: 5,1 @@ -369,7 +369,7 @@ entities: version: 7 6,1: ind: 6,1 - tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAACBAAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAA+AAAAAAAAPgAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAgQAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 4,-3: ind: 4,-3 @@ -389,7 +389,7 @@ entities: version: 7 6,0: ind: 6,0 - tiles: BAAAAAAAAAQAAAAAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAAA+AAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAPgAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAABMAAAAAAAATAAAAAAAAEwAAAAAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: BAAAAAAAAAQAAAAAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAEAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAATAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAAA+AAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAPgAAAAAAAEwAAAAAAABMAAAAAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 6,-1: ind: 6,-1 @@ -43391,22 +43391,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 11 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 101.5,21.5 - parent: 2 - - type: Fixtures - fixtures: {} - - uid: 12 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 101.5,19.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 13 components: - type: Transform @@ -43542,8 +43526,6 @@ entities: - 13632 - 13739 - 7174 - - 7167 - - 7175 - 7176 - 7168 - 7166 @@ -44872,6 +44854,22 @@ entities: - 13800 - type: Fixtures fixtures: {} + - uid: 11833 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 104.5,19.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 16991 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 104.5,21.5 + parent: 2 + - type: Fixtures + fixtures: {} - proto: AirAlarmFreezer entities: - uid: 112 @@ -45306,22 +45304,30 @@ entities: parent: 20154 - proto: AirlockExternalGlassAtmosphericsLocked entities: - - uid: 181 - components: - - type: Transform - pos: 97.5,21.5 - parent: 2 - - uid: 182 - components: - - type: Transform - pos: 97.5,19.5 - parent: 2 - uid: 183 components: - type: Transform rot: -1.5707963267948966 rad pos: 97.5,26.5 parent: 2 + - uid: 16992 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,15.5 + parent: 2 + - uid: 21727 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 100.5,19.5 + parent: 2 + - uid: 21728 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 100.5,21.5 + parent: 2 - proto: AirlockExternalGlassShuttleArrivals entities: - uid: 184 @@ -47000,6 +47006,11 @@ entities: - type: Transform pos: -10.444789,53.65996 parent: 2 + - uid: 13744 + components: + - type: Transform + pos: 100.33421,25.563883 + parent: 2 - proto: Ashtray entities: - uid: 391 @@ -48026,12 +48037,13 @@ entities: - uid: 544 components: - type: Transform + rot: 1.5707963267948966 rad pos: 97.5,31.5 parent: 2 - - uid: 545 + - uid: 16311 components: - type: Transform - rot: 3.141592653589793 rad + rot: 1.5707963267948966 rad pos: 96.5,12.5 parent: 2 - proto: BlastDoorBridgeOpen @@ -48193,6 +48205,24 @@ entities: - type: Transform pos: 27.5,28.5 parent: 2 + - uid: 14626 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 93.5,13.5 + parent: 2 + - uid: 16993 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 94.5,13.5 + parent: 2 + - uid: 18190 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 95.5,13.5 + parent: 2 - uid: 20214 components: - type: Transform @@ -48211,6 +48241,24 @@ entities: rot: 3.141592653589793 rad pos: 12.5,-9.5 parent: 20154 + - uid: 21734 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 96.5,30.5 + parent: 2 + - uid: 21735 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 95.5,30.5 + parent: 2 + - uid: 21736 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 94.5,30.5 + parent: 2 - proto: BlockGameArcade entities: - uid: 576 @@ -49128,6 +49176,18 @@ entities: rot: 1.5707963267948966 rad pos: -15.5,-23.5 parent: 2 + - uid: 14625 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,30.5 + parent: 2 + - uid: 16989 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 96.5,13.5 + parent: 2 - proto: ButtonFrameCautionSecurity entities: - uid: 694 @@ -49234,6 +49294,11 @@ entities: parent: 2 - proto: CableApcExtension entities: + - uid: 181 + components: + - type: Transform + pos: 101.5,20.5 + parent: 2 - uid: 709 components: - type: Transform @@ -57029,6 +57094,46 @@ entities: - type: Transform pos: -22.5,28.5 parent: 2 + - uid: 13943 + components: + - type: Transform + pos: 99.5,20.5 + parent: 2 + - uid: 14094 + components: + - type: Transform + pos: 103.5,20.5 + parent: 2 + - uid: 16285 + components: + - type: Transform + pos: 102.5,20.5 + parent: 2 + - uid: 17139 + components: + - type: Transform + pos: 100.5,20.5 + parent: 2 + - uid: 18138 + components: + - type: Transform + pos: 95.5,20.5 + parent: 2 + - uid: 18170 + components: + - type: Transform + pos: 97.5,20.5 + parent: 2 + - uid: 18189 + components: + - type: Transform + pos: 98.5,20.5 + parent: 2 + - uid: 18203 + components: + - type: Transform + pos: 96.5,20.5 + parent: 2 - uid: 20230 components: - type: Transform @@ -69118,6 +69223,12 @@ entities: buckledEntities: [] - proto: Catwalk entities: + - uid: 182 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,17.5 + parent: 2 - uid: 4260 components: - type: Transform @@ -70825,6 +70936,84 @@ entities: - type: Transform pos: 64.5,46.5 parent: 2 + - uid: 7167 + components: + - type: Transform + pos: 101.5,27.5 + parent: 2 + - uid: 11830 + components: + - type: Transform + pos: 100.5,25.5 + parent: 2 + - uid: 11835 + components: + - type: Transform + pos: 100.5,27.5 + parent: 2 + - uid: 12126 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,26.5 + parent: 2 + - uid: 13441 + components: + - type: Transform + pos: 101.5,26.5 + parent: 2 + - uid: 13454 + components: + - type: Transform + pos: 100.5,26.5 + parent: 2 + - uid: 13604 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,27.5 + parent: 2 + - uid: 13745 + components: + - type: Transform + pos: 101.5,25.5 + parent: 2 + - uid: 13747 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,25.5 + parent: 2 + - uid: 13942 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,18.5 + parent: 2 + - uid: 14095 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,21.5 + parent: 2 + - uid: 14920 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,23.5 + parent: 2 + - uid: 16272 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,20.5 + parent: 2 + - uid: 16276 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,22.5 + parent: 2 - uid: 20617 components: - type: Transform @@ -71364,6 +71553,18 @@ entities: rot: 3.141592653589793 rad pos: 86.894714,26.663887 parent: 2 + - uid: 11834 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 99.5,25.5 + parent: 2 + - uid: 13749 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,25.5 + parent: 2 - uid: 20628 components: - type: Transform @@ -72800,6 +73001,16 @@ entities: - type: Transform pos: -15.240647,-37.425087 parent: 2 + - uid: 13443 + components: + - type: Transform + pos: 100.55296,25.548258 + parent: 2 + - uid: 13746 + components: + - type: Transform + pos: 100.50609,25.657633 + parent: 2 - uid: 20642 components: - type: Transform @@ -74929,6 +75140,17 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-24.5 parent: 2 + - uid: 5169 + components: + - type: Transform + pos: 20.5,27.5 + parent: 2 + - uid: 5170 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 27.5,-46.5 + parent: 2 - proto: ComputerCriminalRecords entities: - uid: 5157 @@ -75015,19 +75237,6 @@ entities: rot: 1.5707963267948966 rad pos: 17.5,23.5 parent: 2 -- proto: ComputerMedicalRecords - entities: - - uid: 5169 - components: - - type: Transform - pos: 20.5,27.5 - parent: 2 - - uid: 5170 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 27.5,-46.5 - parent: 2 - proto: ComputerPowerMonitoring entities: - uid: 5171 @@ -75280,6 +75489,12 @@ entities: - type: Transform pos: 0.5,60.5 parent: 2 + - uid: 21737 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,48.5 + parent: 2 - proto: CorporateCircuitBoard entities: - uid: 5209 @@ -76479,10 +76694,8 @@ entities: - uid: 5394 components: - type: Transform - pos: 94.5,21.5 + pos: 96.5,20.5 parent: 2 - - type: WarpPoint - location: Атмос - proto: DefaultStationBeaconBar entities: - uid: 5395 @@ -82722,6 +82935,16 @@ entities: - type: Transform pos: 26.461136,-53.451836 parent: 2 + - uid: 13453 + components: + - type: Transform + pos: 100.81859,25.548258 + parent: 2 + - uid: 18213 + components: + - type: Transform + pos: 100.25609,25.720133 + parent: 2 - proto: DrinkMilkCarton entities: - uid: 6410 @@ -88044,15 +88267,6 @@ entities: deviceLists: - 21 - 13 - - uid: 7167 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 97.5,21.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 21 - uid: 7168 components: - type: Transform @@ -88119,15 +88333,6 @@ entities: - type: DeviceNetwork deviceLists: - 21 - - uid: 7175 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 97.5,19.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 21 - uid: 7176 components: - type: Transform @@ -89998,11 +90203,6 @@ entities: - type: Transform pos: 91.5,9.5 parent: 2 - - uid: 7400 - components: - - type: Transform - pos: 92.5,10.5 - parent: 2 - uid: 7401 components: - type: Transform @@ -109923,11 +110123,6 @@ entities: - type: Transform pos: 98.5,14.5 parent: 2 - - uid: 11001 - components: - - type: Transform - pos: 98.5,15.5 - parent: 2 - uid: 11002 components: - type: Transform @@ -112619,11 +112814,6 @@ entities: - type: Transform pos: 91.5,7.5 parent: 2 - - uid: 11481 - components: - - type: Transform - pos: 94.5,10.5 - parent: 2 - uid: 11482 components: - type: Transform @@ -112649,21 +112839,6 @@ entities: - type: Transform pos: 89.5,4.5 parent: 2 - - uid: 11487 - components: - - type: Transform - pos: 100.5,15.5 - parent: 2 - - uid: 11488 - components: - - type: Transform - pos: 101.5,15.5 - parent: 2 - - uid: 11489 - components: - - type: Transform - pos: 99.5,15.5 - parent: 2 - uid: 11490 components: - type: Transform @@ -112675,21 +112850,11 @@ entities: rot: -1.5707963267948966 rad pos: -9.5,25.5 parent: 2 - - uid: 11492 - components: - - type: Transform - pos: 93.5,10.5 - parent: 2 - uid: 11493 components: - type: Transform pos: 91.5,6.5 parent: 2 - - uid: 11494 - components: - - type: Transform - pos: 95.5,10.5 - parent: 2 - uid: 11495 components: - type: Transform @@ -116315,21 +116480,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 11816 - components: - - type: Transform - pos: 99.5,20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 11817 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 97.5,20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - uid: 11818 components: - type: Transform @@ -116403,69 +116553,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 11828 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 97.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 11829 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 98.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 11830 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 97.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 11831 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 99.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 11832 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 99.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 11833 - components: - - type: Transform - pos: 99.5,24.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 11834 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 97.5,24.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 11835 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 98.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - uid: 11836 components: - type: Transform @@ -118697,6 +118784,76 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' + - uid: 14792 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 16279 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 102.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 17955 + components: + - type: Transform + pos: 102.5,20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18191 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 100.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18205 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 101.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18324 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,24.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19722 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 101.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19723 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 102.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20016 + components: + - type: Transform + pos: 102.5,24.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - uid: 21069 components: - type: Transform @@ -118738,20 +118895,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF1212FF' - - uid: 12126 - components: - - type: Transform - pos: 97.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 12127 - components: - - type: Transform - pos: 97.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - uid: 12128 components: - type: Transform @@ -118878,6 +119021,13 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' + - uid: 18141 + components: + - type: Transform + pos: 100.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' - uid: 21071 components: - type: Transform @@ -129200,61 +129350,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 13437 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13438 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13439 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13440 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 97.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13441 - components: - - type: Transform - pos: 98.5,20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13442 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13443 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - uid: 13444 components: - type: Transform @@ -129318,45 +129413,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' - - uid: 13452 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13453 - components: - - type: Transform - pos: 98.5,24.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13454 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 97.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13455 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13456 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - uid: 13457 components: - type: Transform @@ -130398,6 +130454,116 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF1212FF' + - uid: 16990 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 101.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 17954 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18136 + components: + - type: Transform + pos: 101.5,20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18204 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18208 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 101.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18210 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18381 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 100.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 18409 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 101.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 18410 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 101.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19717 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19718 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19720 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 19721 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20015 + components: + - type: Transform + pos: 101.5,24.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - uid: 21121 components: - type: Transform @@ -130510,22 +130676,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#03FCD3FF' - - uid: 13604 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 96.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13605 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 96.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - uid: 13606 components: - type: Transform @@ -130542,6 +130692,22 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF1212FF' + - uid: 14820 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 99.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 21726 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 99.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - proto: GasThermoMachineFreezer entities: - uid: 13608 @@ -130556,6 +130722,11 @@ entities: - type: Transform pos: 49.5,76.5 parent: 2 + - uid: 21729 + components: + - type: Transform + pos: 99.5,23.5 + parent: 2 - proto: GasThermoMachineFreezerEnabled entities: - uid: 13610 @@ -130566,6 +130737,13 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0335FCFF' +- proto: GasThermoMachineHeater + entities: + - uid: 21730 + components: + - type: Transform + pos: 98.5,23.5 + parent: 2 - proto: GasValve entities: - uid: 13611 @@ -131923,150 +132101,6 @@ entities: - 21 - type: AtmosPipeColor color: '#FF1212FF' - - uid: 13740 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13741 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13742 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13743 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13744 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13745 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13746 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13747 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13748 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13749 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 98.5,23.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13750 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13751 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,22.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13752 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,21.5 - parent: 2 - - type: AtmosPipeColor - color: '#6666FFFF' - - uid: 13753 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13754 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 99.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13755 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13756 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - - uid: 13757 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 100.5,17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF6666FF' - uid: 13758 components: - type: Transform @@ -133042,6 +133076,110 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF1212FF' + - uid: 14833 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18214 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18216 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18217 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18218 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18265 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18288 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18298 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 18315 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF4444FF' + - uid: 20017 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20018 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20019 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 101.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 20020 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - uid: 21124 components: - type: Transform @@ -133092,6 +133230,46 @@ entities: parent: 20154 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 21721 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 21722 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 102.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 21723 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,23.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 21724 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,22.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' + - uid: 21725 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 103.5,21.5 + parent: 2 + - type: AtmosPipeColor + color: '#1E90FFFF' - proto: GasVentScrubberFreezer entities: - uid: 13851 @@ -133187,6 +133365,26 @@ entities: parent: 20154 - proto: Grille entities: + - uid: 12 + components: + - type: Transform + pos: 100.5,18.5 + parent: 2 + - uid: 11828 + components: + - type: Transform + pos: 100.5,17.5 + parent: 2 + - uid: 13755 + components: + - type: Transform + pos: 100.5,22.5 + parent: 2 + - uid: 13756 + components: + - type: Transform + pos: 100.5,23.5 + parent: 2 - uid: 13859 components: - type: Transform @@ -133612,16 +133810,6 @@ entities: - type: Transform pos: 88.5,11.5 parent: 2 - - uid: 13942 - components: - - type: Transform - pos: 97.5,18.5 - parent: 2 - - uid: 13943 - components: - - type: Transform - pos: 97.5,23.5 - parent: 2 - uid: 13944 components: - type: Transform @@ -134406,16 +134594,6 @@ entities: - type: Transform pos: 91.5,20.5 parent: 2 - - uid: 14094 - components: - - type: Transform - pos: 97.5,22.5 - parent: 2 - - uid: 14095 - components: - - type: Transform - pos: 97.5,17.5 - parent: 2 - uid: 14096 components: - type: Transform @@ -136174,7 +136352,7 @@ entities: - uid: 14382 components: - type: Transform - pos: 95.5,21.5 + pos: 94.5,24.5 parent: 2 - proto: HolopadEngineeringFront entities: @@ -137798,31 +137976,65 @@ entities: fixtures: {} - proto: LockableButtonAtmospherics entities: - - uid: 14625 + - uid: 545 components: - - type: MetaData - desc: Эта кнопка переключает гермозатвор камеры смешивания. - - type: Transform - pos: 96.5,28.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 544: - - - Pressed - - Toggle - - type: Fixtures - fixtures: {} - - uid: 14626 - components: - - type: MetaData - desc: Эта кнопка переключает гермозатвор камеры смешивания. - type: Transform rot: 3.141592653589793 rad pos: 96.5,15.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 545: + 14626: + - - Pressed + - Toggle + 16993: + - - Pressed + - Toggle + 18190: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 16296 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 96.5,13.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 16311: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 21732 + components: + - type: Transform + pos: 96.5,28.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 21734: + - - Pressed + - Toggle + 21735: + - - Pressed + - Toggle + 21736: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 21733 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,30.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 544: - - Pressed - Toggle - type: Fixtures @@ -139457,11 +139669,6 @@ entities: parent: 2 - proto: NitrogenCanister entities: - - uid: 14792 - components: - - type: Transform - pos: 96.5,18.5 - parent: 2 - uid: 14793 components: - type: Transform @@ -139654,11 +139861,6 @@ entities: parent: 2 - proto: OxygenCanister entities: - - uid: 14820 - components: - - type: Transform - pos: 96.5,22.5 - parent: 2 - uid: 14821 components: - type: Transform @@ -139743,34 +139945,6 @@ entities: "я устал, я ухожу" и ухожу я не просто так, а потому что я правда устал. - - uid: 14833 - components: - - type: Transform - pos: 96.51457,20.54572 - parent: 2 - - type: Paper - content: >- - Добрый сосед атмос с прошлой смены передает привет! - - - Так как данная станция окружена нормальной атмосферой, а газодобытчиков кроме плазмы нам и не установили, мной было принято решение соорудить свой газодобытчик на основе скрубберов! - - - Вот только настроить я его не успел, срочно собрали на эвакуацию по неизвестным причинам, а потому пишу данное послание перед отлетом! - - - Настройки камер достаточно простые! - - 1) Подключите скрубберы к воздушной сигнализации своей камеры. - - 2) Настройте фильтрацию скрубберов на кислород и азот соответственно. - - 3) Наслаждайтесь стабильным потоком свежих газов прямиком из атмосферы! - - - При желании вы даже можете подключить её к системе проложенной дистры, какой-то безумец проложил её по всей станции!!! - - [italic]Приятной вам смены, от доброго соседа атмоса.[/italic] - uid: 14834 components: - type: Transform @@ -140206,6 +140380,13 @@ entities: - type: Transform pos: -12.382263,-13.281158 parent: 20154 +- proto: PartRodMetal + entities: + - uid: 17953 + components: + - type: Transform + pos: 102.46753,26.483398 + parent: 2 - proto: Pen entities: - uid: 14881 @@ -141466,11 +141647,11 @@ entities: parent: 2 - proto: PoweredDimSmallLight entities: - - uid: 15065 + - uid: 14919 components: - type: Transform rot: 3.141592653589793 rad - pos: 94.5,12.5 + pos: 94.5,11.5 parent: 2 - uid: 15066 components: @@ -141611,12 +141792,6 @@ entities: rot: -1.5707963267948966 rad pos: 96.5,25.5 parent: 2 - - uid: 15090 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 96.5,20.5 - parent: 2 - uid: 15091 components: - type: Transform @@ -144103,6 +144278,18 @@ entities: parent: 2 - proto: Railing entities: + - uid: 13456 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,25.5 + parent: 2 + - uid: 15065 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 101.5,27.5 + parent: 2 - uid: 15454 components: - type: Transform @@ -145153,8 +145340,8 @@ entities: - uid: 15635 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,25.5 + rot: 3.141592653589793 rad + pos: 100.5,27.5 parent: 2 - uid: 15636 components: @@ -145344,18 +145531,6 @@ entities: rot: 3.141592653589793 rad pos: 34.5,12.5 parent: 2 - - uid: 15669 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,27.5 - parent: 2 - - uid: 15670 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,26.5 - parent: 2 - uid: 15671 components: - type: Transform @@ -145960,6 +146135,12 @@ entities: parent: 20154 - proto: RailingCorner entities: + - uid: 13743 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 102.5,27.5 + parent: 2 - uid: 15727 components: - type: Transform @@ -146351,6 +146532,12 @@ entities: parent: 20154 - proto: RailingCornerSmall entities: + - uid: 15090 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 99.5,27.5 + parent: 2 - uid: 15792 components: - type: Transform @@ -148995,6 +149182,23 @@ entities: parent: 2 - proto: ReinforcedPlasmaWindow entities: + - uid: 15669 + components: + - type: Transform + pos: 95.5,30.5 + parent: 2 + - uid: 15670 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 94.5,13.5 + parent: 2 + - uid: 16233 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 95.5,13.5 + parent: 2 - uid: 16235 components: - type: Transform @@ -149025,8 +149229,44 @@ entities: rot: -1.5707963267948966 rad pos: 83.5,10.5 parent: 2 + - uid: 16259 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 93.5,13.5 + parent: 2 + - uid: 16270 + components: + - type: Transform + pos: 94.5,30.5 + parent: 2 + - uid: 16271 + components: + - type: Transform + pos: 96.5,30.5 + parent: 2 - proto: ReinforcedWindow entities: + - uid: 13439 + components: + - type: Transform + pos: 100.5,23.5 + parent: 2 + - uid: 13452 + components: + - type: Transform + pos: 100.5,18.5 + parent: 2 + - uid: 13605 + components: + - type: Transform + pos: 100.5,22.5 + parent: 2 + - uid: 13757 + components: + - type: Transform + pos: 100.5,17.5 + parent: 2 - uid: 16240 components: - type: Transform @@ -149127,11 +149367,6 @@ entities: - type: Transform pos: 66.5,84.5 parent: 2 - - uid: 16259 - components: - - type: Transform - pos: 95.5,30.5 - parent: 2 - uid: 16260 components: - type: Transform @@ -149182,21 +149417,6 @@ entities: - type: Transform pos: 95.5,15.5 parent: 2 - - uid: 16270 - components: - - type: Transform - pos: 94.5,13.5 - parent: 2 - - uid: 16271 - components: - - type: Transform - pos: 93.5,13.5 - parent: 2 - - uid: 16272 - components: - - type: Transform - pos: 95.5,13.5 - parent: 2 - uid: 16273 components: - type: Transform @@ -149214,11 +149434,6 @@ entities: rot: 3.141592653589793 rad pos: -11.5,25.5 parent: 2 - - uid: 16276 - components: - - type: Transform - pos: 96.5,30.5 - parent: 2 - uid: 16277 components: - type: Transform @@ -149229,11 +149444,6 @@ entities: - type: Transform pos: 63.5,24.5 parent: 2 - - uid: 16279 - components: - - type: Transform - pos: 94.5,30.5 - parent: 2 - uid: 16280 components: - type: Transform @@ -149262,21 +149472,11 @@ entities: - type: Transform pos: 93.5,15.5 parent: 2 - - uid: 16285 - components: - - type: Transform - pos: 97.5,18.5 - parent: 2 - uid: 16286 components: - type: Transform pos: 86.5,11.5 parent: 2 - - uid: 16287 - components: - - type: Transform - pos: 97.5,17.5 - parent: 2 - uid: 16288 components: - type: Transform @@ -149318,11 +149518,6 @@ entities: rot: 3.141592653589793 rad pos: -7.5,59.5 parent: 2 - - uid: 16296 - components: - - type: Transform - pos: 97.5,23.5 - parent: 2 - uid: 16297 components: - type: Transform @@ -149399,11 +149594,6 @@ entities: - type: Transform pos: 86.5,19.5 parent: 2 - - uid: 16311 - components: - - type: Transform - pos: 97.5,22.5 - parent: 2 - uid: 16312 components: - type: Transform @@ -156759,6 +156949,12 @@ entities: parent: 2 - type: SurveillanceCamera id: Брифинговая + - uid: 21731 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 99.5,20.5 + parent: 2 - proto: SurveillanceCameraGeneral entities: - uid: 17380 @@ -157830,6 +158026,12 @@ entities: parent: 2 - proto: Table entities: + - uid: 13442 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 100.5,25.5 + parent: 2 - uid: 17491 components: - type: Transform @@ -160661,10 +160863,9 @@ entities: - Reverse - - Middle - Off - - uid: 17954 + - uid: 16287 components: - type: Transform - rot: 3.141592653589793 rad pos: -13.5,47.5 parent: 2 - type: DeviceLinkSource @@ -160676,7 +160877,7 @@ entities: - Forward - - Middle - Off - 16234: + 21737: - - Left - Reverse - - Right @@ -160704,6 +160905,13 @@ entities: - Forward - - Middle - Off + 16234: + - - Right + - Reverse + - - Left + - Forward + - - Middle + - Off - proto: UniformPrinter entities: - uid: 17956 @@ -161540,6 +161748,11 @@ entities: fixtures: {} - proto: WallReinforced entities: + - uid: 11 + components: + - type: Transform + pos: 101.5,20.5 + parent: 2 - uid: 65 components: - type: Transform @@ -161555,6 +161768,17 @@ entities: - type: Transform pos: -0.5,58.5 parent: 2 + - uid: 7175 + components: + - type: Transform + pos: 104.5,18.5 + parent: 2 + - uid: 7400 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 92.5,10.5 + parent: 2 - uid: 8829 components: - type: Transform @@ -161575,6 +161799,139 @@ entities: - type: Transform pos: -0.5,60.5 parent: 2 + - uid: 11001 + components: + - type: Transform + pos: 99.5,15.5 + parent: 2 + - uid: 11481 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 94.5,10.5 + parent: 2 + - uid: 11487 + components: + - type: Transform + pos: 97.5,24.5 + parent: 2 + - uid: 11488 + components: + - type: Transform + pos: 98.5,24.5 + parent: 2 + - uid: 11489 + components: + - type: Transform + pos: 101.5,24.5 + parent: 2 + - uid: 11492 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 93.5,10.5 + parent: 2 + - uid: 11494 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 95.5,10.5 + parent: 2 + - uid: 11816 + components: + - type: Transform + pos: 103.5,16.5 + parent: 2 + - uid: 11817 + components: + - type: Transform + pos: 104.5,16.5 + parent: 2 + - uid: 11829 + components: + - type: Transform + pos: 99.5,24.5 + parent: 2 + - uid: 11831 + components: + - type: Transform + pos: 103.5,24.5 + parent: 2 + - uid: 11832 + components: + - type: Transform + pos: 104.5,20.5 + parent: 2 + - uid: 12127 + components: + - type: Transform + pos: 104.5,17.5 + parent: 2 + - uid: 13437 + components: + - type: Transform + pos: 102.5,16.5 + parent: 2 + - uid: 13438 + components: + - type: Transform + pos: 101.5,16.5 + parent: 2 + - uid: 13440 + components: + - type: Transform + pos: 103.5,20.5 + parent: 2 + - uid: 13455 + components: + - type: Transform + pos: 104.5,23.5 + parent: 2 + - uid: 13740 + components: + - type: Transform + pos: 104.5,24.5 + parent: 2 + - uid: 13741 + components: + - type: Transform + pos: 102.5,20.5 + parent: 2 + - uid: 13742 + components: + - type: Transform + pos: 100.5,24.5 + parent: 2 + - uid: 13748 + components: + - type: Transform + pos: 100.5,15.5 + parent: 2 + - uid: 13750 + components: + - type: Transform + pos: 104.5,22.5 + parent: 2 + - uid: 13751 + components: + - type: Transform + pos: 104.5,21.5 + parent: 2 + - uid: 13752 + components: + - type: Transform + pos: 104.5,19.5 + parent: 2 + - uid: 13753 + components: + - type: Transform + pos: 100.5,16.5 + parent: 2 + - uid: 13754 + components: + - type: Transform + pos: 100.5,20.5 + parent: 2 - uid: 18067 components: - type: Transform @@ -161935,21 +162292,11 @@ entities: rot: -1.5707963267948966 rad pos: 81.5,81.5 parent: 2 - - uid: 18136 - components: - - type: Transform - pos: 95.5,11.5 - parent: 2 - uid: 18137 components: - type: Transform pos: 96.5,11.5 parent: 2 - - uid: 18138 - components: - - type: Transform - pos: 93.5,11.5 - parent: 2 - uid: 18139 components: - type: Transform @@ -161960,11 +162307,6 @@ entities: - type: Transform pos: 96.5,13.5 parent: 2 - - uid: 18141 - components: - - type: Transform - pos: 94.5,11.5 - parent: 2 - uid: 18142 components: - type: Transform @@ -162108,11 +162450,6 @@ entities: - type: Transform pos: 89.5,12.5 parent: 2 - - uid: 18170 - components: - - type: Transform - pos: 98.5,24.5 - parent: 2 - uid: 18171 components: - type: Transform @@ -162201,24 +162538,8 @@ entities: - uid: 18188 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 99.5,20.5 - parent: 2 - - uid: 18189 - components: - - type: Transform - pos: 101.5,21.5 - parent: 2 - - uid: 18190 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 100.5,20.5 - parent: 2 - - uid: 18191 - components: - - type: Transform - pos: 101.5,22.5 + rot: -1.5707963267948966 rad + pos: 96.5,10.5 parent: 2 - uid: 18192 components: @@ -162277,46 +162598,21 @@ entities: - type: Transform pos: 85.5,13.5 parent: 2 - - uid: 18203 - components: - - type: Transform - pos: 101.5,20.5 - parent: 2 - - uid: 18204 - components: - - type: Transform - pos: 99.5,16.5 - parent: 2 - - uid: 18205 - components: - - type: Transform - pos: 101.5,16.5 - parent: 2 - uid: 18206 components: - type: Transform - pos: 100.5,16.5 + pos: 98.5,15.5 parent: 2 - uid: 18207 components: - type: Transform - pos: 101.5,19.5 - parent: 2 - - uid: 18208 - components: - - type: Transform - pos: 99.5,24.5 + pos: 84.5,10.5 parent: 2 - uid: 18209 components: - type: Transform pos: 93.5,30.5 parent: 2 - - uid: 18210 - components: - - type: Transform - pos: 101.5,18.5 - parent: 2 - uid: 18211 components: - type: Transform @@ -162325,39 +162621,13 @@ entities: - uid: 18212 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 98.5,20.5 - parent: 2 - - uid: 18213 - components: - - type: Transform - pos: 101.5,23.5 - parent: 2 - - uid: 18214 - components: - - type: Transform - pos: 101.5,17.5 + pos: 102.5,24.5 parent: 2 - uid: 18215 components: - type: Transform pos: 90.5,13.5 parent: 2 - - uid: 18216 - components: - - type: Transform - pos: 98.5,16.5 - parent: 2 - - uid: 18217 - components: - - type: Transform - pos: 101.5,24.5 - parent: 2 - - uid: 18218 - components: - - type: Transform - pos: 100.5,24.5 - parent: 2 - uid: 18219 components: - type: Transform @@ -162588,11 +162858,6 @@ entities: - type: Transform pos: 60.5,81.5 parent: 2 - - uid: 18265 - components: - - type: Transform - pos: 97.5,16.5 - parent: 2 - uid: 18266 components: - type: Transform @@ -162761,11 +163026,6 @@ entities: - type: Transform pos: 84.5,30.5 parent: 2 - - uid: 18298 - components: - - type: Transform - pos: 97.5,15.5 - parent: 2 - uid: 18299 components: - type: Transform @@ -162846,11 +163106,6 @@ entities: - type: Transform pos: 79.5,11.5 parent: 2 - - uid: 18315 - components: - - type: Transform - pos: 84.5,10.5 - parent: 2 - uid: 18316 components: - type: Transform @@ -162891,11 +163146,6 @@ entities: - type: Transform pos: 97.5,28.5 parent: 2 - - uid: 18324 - components: - - type: Transform - pos: 97.5,20.5 - parent: 2 - uid: 18325 components: - type: Transform @@ -163177,11 +163427,6 @@ entities: rot: -1.5707963267948966 rad pos: 96.5,28.5 parent: 2 - - uid: 18381 - components: - - type: Transform - pos: 97.5,24.5 - parent: 2 - uid: 18382 components: - type: Transform @@ -171448,26 +171693,6 @@ entities: showEnts: False occludes: True ent: null -- proto: WarningN2 - entities: - - uid: 19717 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 97.5,16.5 - parent: 2 - - type: Fixtures - fixtures: {} -- proto: WarningO2 - entities: - - uid: 19718 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 97.5,24.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: WarningPlasma entities: - uid: 19719 From fa8a4a6cd20060e3ab230a6e922cb36cac55ff87 Mon Sep 17 00:00:00 2001 From: exo4ka <193421410+exo4ka@users.noreply.github.com> Date: Sat, 18 Apr 2026 13:26:57 +0300 Subject: [PATCH 200/247] Silly tweak 18.04.2026 (#3571) --- Resources/Maps/Corvax/corvax_silly.yml | 1222 ++++++------------------ 1 file changed, 268 insertions(+), 954 deletions(-) diff --git a/Resources/Maps/Corvax/corvax_silly.yml b/Resources/Maps/Corvax/corvax_silly.yml index 0d58c69d0b..7cb40d3e76 100644 --- a/Resources/Maps/Corvax/corvax_silly.yml +++ b/Resources/Maps/Corvax/corvax_silly.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 267.3.0 + engineVersion: 270.1.0 forkId: "" forkVersion: "" - time: 11/08/2025 06:48:43 - entityCount: 8631 + time: 04/18/2026 07:55:59 + entityCount: 8634 maps: - 1 grids: @@ -38,7 +38,6 @@ tilemap: 22: FloorBrokenWood 28: FloorClown 29: FloorConcrete - 18: FloorConcreteOutside 32: FloorDark 33: FloorDarkDiagonal 34: FloorDarkDiagonalMini @@ -53,9 +52,7 @@ tilemap: 53: FloorGrassLight 54: FloorGrayConcrete 55: FloorGrayConcreteMono - 5: FloorGrayConcreteOutside - 6: FloorGrayConcreteOutsideSmooth - 56: FloorGrayConcreteSmooth + 6: FloorGrayConcreteSmooth 61: FloorHydro 20: FloorIce 23: FloorJungleAstroGrass @@ -159,7 +156,7 @@ entities: version: 7 -1,-1: ind: -1,-1 - tiles: fgAAAAAAAH0AAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAIABgAAAAACAH0AAAAAAAB9AAAAAAAABgAAAAAAAAYAAAAAAwAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAADAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAgAGAAAAAAIABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAGAAAAAAMAfgAAAAAAAAUAAAAAAAAGAAAAAAIABgAAAAABAAUAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAABgAAAAADAH4AAAAAAAAFAAAAAAAABgAAAAADAAYAAAAAAQAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAYAAAAAAgB+AAAAAAAABQAAAAAAAAYAAAAAAQAGAAAAAAIABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAGAAAAAAIABQAAAAABAAUAAAAAAQAGAAAAAAEABgAAAAACAAUAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAABgAAAAABAAYAAAAAAQAGAAAAAAEABgAAAAADAAYAAAAAAwAFAAAAAAMABQAAAAADAAUAAAAAAgAFAAAAAAAABQAAAAADAAUAAAAAAAAFAAAAAAMABQAAAAABAAUAAAAAAgAFAAAAAAMABQAAAAADAAYAAAAAAwAGAAAAAAMABgAAAAABAAYAAAAAAAAGAAAAAAMABgAAAAACAAYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAQAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAABAAYAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAgAGAAAAAAIABgAAAAADAAYAAAAAAwAGAAAAAAIABgAAAAACAAYAAAAAAwAGAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAMABgAAAAADADgAAAAAAgCBAAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAgQAAAAAAAHMAAAAAAAB3AAAAAAAAeQAAAAAAAGAAAAAAAgBgAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAIEAAAAAAABzAAAAAAAAdwAAAAAAAHcAAAAAAABgAAAAAAEAawAAAAABAIEAAAAAAABSAAAAAAAAUgAAAAAAAIEAAAAAAABgAAAAAAAAOAAAAAAAADgAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAACBAAAAAAAAcwAAAAAAAHcAAAAAAAB3AAAAAAAAagAAAAABAGAAAAAAAgCBAAAAAAAAUgAAAAAAAFIAAAAAAABgAAAAAAMAYAAAAAAAADgAAAAAAAA4AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAwBgAAAAAAEAgQAAAAAAAFIAAAAAAABSAAAAAAAAgQAAAAAAAGAAAAAAAgA2AAAAAAIANgAAAAAAADYAAAAAAACBAAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAMAfAAAAAADAHwAAAAAAgBgAAAAAAMAawAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAH0AAAAAAgB8AAAAAAMAfAAAAAAAAHwAAAAAAgB8AAAAAAMAYAAAAAAAAGoAAAAAAQCBAAAAAAAAYAAAAAACAGAAAAAAAwCBAAAAAAAAYAAAAAADAA== + tiles: fgAAAAAAAH0AAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAIABgAAAAACAH0AAAAAAAB9AAAAAAAABgAAAAAAAAYAAAAAAwA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAADAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAgAGAAAAAAIANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAGAAAAAAMAfgAAAAAAADYAAAAAAAAGAAAAAAIABgAAAAABADYAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAABgAAAAADAH4AAAAAAAA2AAAAAAAABgAAAAADAAYAAAAAAQA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACAAYAAAAAAgB+AAAAAAAANgAAAAAAAAYAAAAAAQAGAAAAAAIANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAGAAAAAAIANgAAAAABADYAAAAAAQAGAAAAAAEABgAAAAACADYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAABgAAAAABAAYAAAAAAQAGAAAAAAEABgAAAAADAAYAAAAAAwA2AAAAAAMANgAAAAADADYAAAAAAgA2AAAAAAAANgAAAAADADYAAAAAAAA2AAAAAAMANgAAAAABADYAAAAAAgA2AAAAAAMANgAAAAADAAYAAAAAAwAGAAAAAAMABgAAAAABAAYAAAAAAAAGAAAAAAMABgAAAAACAAYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAQAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAABAAYAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAgAGAAAAAAIABgAAAAADAAYAAAAAAwAGAAAAAAIABgAAAAACAAYAAAAAAwAGAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAMABgAAAAADAAYAAAAAAgCBAAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAgQAAAAAAAHMAAAAAAAB3AAAAAAAAeQAAAAAAAGAAAAAAAgBgAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAIEAAAAAAABzAAAAAAAAdwAAAAAAAHcAAAAAAABgAAAAAAEAawAAAAABAIEAAAAAAABSAAAAAAAAUgAAAAAAAIEAAAAAAABgAAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAACBAAAAAAAAcwAAAAAAAHcAAAAAAAB3AAAAAAAAagAAAAABAGAAAAAAAgCBAAAAAAAAUgAAAAAAAFIAAAAAAABgAAAAAAMAYAAAAAAAAAYAAAAAAAAGAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAwBgAAAAAAEAgQAAAAAAAFIAAAAAAABSAAAAAAAAgQAAAAAAAGAAAAAAAgA2AAAAAAIANgAAAAAAADYAAAAAAACBAAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAMAfAAAAAADAHwAAAAAAgBgAAAAAAMAawAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAH0AAAAAAgB8AAAAAAMAfAAAAAAAAHwAAAAAAgB8AAAAAAMAYAAAAAAAAGoAAAAAAQCBAAAAAAAAYAAAAAACAGAAAAAAAwCBAAAAAAAAYAAAAAADAA== version: 7 0,-1: ind: 0,-1 @@ -171,31 +168,31 @@ entities: version: 7 1,0: ind: 1,0 - tiles: YAAAAAAAAGAAAAAAAwCBAAAAAAAAYAAAAAAAAGAAAAAAAQBgAAAAAAIAYAAAAAACAGAAAAAAAgBgAAAAAAMAYAAAAAACAGAAAAAAAgBgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABsAAAAAAAAYAAAAAAAAGAAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAbAAAAAAAAGwAAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAwCBAAAAAAAAYAAAAAACAGAAAAAAAgBgAAAAAAMAgQAAAAAAAGAAAAAAAQCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABqAAAAAAEAgQAAAAAAAIEAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAAAAAUAAAAAAQBqAAAAAAMAagAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAACBAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAQAGAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAABAGAAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAAABgAAAAADAH0AAAAAAQB9AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGoAAAAAAwBqAAAAAAEAawAAAAACAGAAAAAAAQCBAAAAAAAABgAAAAADAAYAAAAAAgAGAAAAAAMABgAAAAACAAYAAAAAAAB9AAAAAAMAfQAAAAADAIEAAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAABgAAAAAAAAYAAAAAABAGAAAAAAAQBgAAAAAAMAgQAAAAAAAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAQAGAAAAAAIAYAAAAAADAIEAAAAAAACBAAAAAAAAfQAAAAAAAH0AAAAAAwB9AAAAAAIAYAAAAAABAGAAAAAAAwBgAAAAAAMAagAAAAACAIEAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAQAGAAAAAAIABgAAAAADAGAAAAAAAQBgAAAAAAEAagAAAAABAGoAAAAAAQBrAAAAAAMAYAAAAAAAAGAAAAAAAQBgAAAAAAEAYAAAAAADAGsAAAAAAAA2AAAAAAEABgAAAAADAAYAAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAQBgAAAAAAMAYAAAAAACAGAAAAAAAwBgAAAAAAAAYAAAAAAAAGoAAAAAAABrAAAAAAAAYAAAAAAAAGAAAAAAAwBgAAAAAAMAgQAAAAAAAAYAAAAAAQAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAMAfQAAAAABAH0AAAAAAwB9AAAAAAEAagAAAAAAAFAAAAAAAwBoAAAAAAEAagAAAAACAFAAAAAAAQBrAAAAAAIAYAAAAAABADYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAIABgAAAAAAAH0AAAAAAwB9AAAAAAMAfQAAAAABAFAAAAAAAQBqAAAAAAIAUAAAAAACAGgAAAAAAABoAAAAAAAAagAAAAACAGoAAAAAAACBAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAMABgAAAAACAAYAAAAAAAB9AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACkAAAAAAgCBAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAQAGAAAAAAMAfQAAAAAAAH0AAAAAAAB9AAAAAAAAKQAAAAABAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAADAAYAAAAAAgAGAAAAAAAABgAAAAACAA== + tiles: YAAAAAAAAGAAAAAAAwCBAAAAAAAAYAAAAAAAAGAAAAAAAQBgAAAAAAIAYAAAAAACAGAAAAAAAgBgAAAAAAMAYAAAAAACAGAAAAAAAgBgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABsAAAAAAAAYAAAAAAAAGAAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAbAAAAAAAAGwAAAAAAACBAAAAAAAAYAAAAAADAGAAAAAAAwCBAAAAAAAAYAAAAAACAGAAAAAAAgBgAAAAAAMAgQAAAAAAAGAAAAAAAQCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABqAAAAAAEAgQAAAAAAAIEAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAABgAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAAAADYAAAAAAQBqAAAAAAMAagAAAAAAAIEAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAACBAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAQAGAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAABAGAAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAAABgAAAAADAH0AAAAAAQB9AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAGoAAAAAAwBqAAAAAAEAawAAAAACAGAAAAAAAQCBAAAAAAAABgAAAAADAAYAAAAAAgAGAAAAAAMABgAAAAACAAYAAAAAAAB9AAAAAAMAfQAAAAADAIEAAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAABgAAAAAAAAYAAAAAABAGAAAAAAAQBgAAAAAAMAgQAAAAAAAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAQAGAAAAAAIAYAAAAAADAIEAAAAAAACBAAAAAAAAfQAAAAAAAH0AAAAAAwB9AAAAAAIAYAAAAAABAGAAAAAAAwBgAAAAAAMAagAAAAACAIEAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAQAGAAAAAAIABgAAAAADAGAAAAAAAQBgAAAAAAEAagAAAAABAGoAAAAAAQBrAAAAAAMAYAAAAAAAAGAAAAAAAQBgAAAAAAEAYAAAAAADAGsAAAAAAAA2AAAAAAEABgAAAAADAAYAAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAQBgAAAAAAMAYAAAAAACAGAAAAAAAwBgAAAAAAAAYAAAAAAAAGoAAAAAAABrAAAAAAAAYAAAAAAAAGAAAAAAAwBgAAAAAAMAgQAAAAAAAAYAAAAAAQAGAAAAAAIABgAAAAAAAAYAAAAAAgAGAAAAAAMAfQAAAAABAH0AAAAAAwB9AAAAAAEAagAAAAAAAFAAAAAAAwBoAAAAAAEAagAAAAACAFAAAAAAAQBrAAAAAAIAYAAAAAABADYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAIABgAAAAAAAH0AAAAAAwB9AAAAAAMAfQAAAAABAFAAAAAAAQBqAAAAAAIAUAAAAAACAGgAAAAAAABoAAAAAAAAagAAAAACAGoAAAAAAACBAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAMABgAAAAACAAYAAAAAAAB9AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAACkAAAAAAgCBAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAADAAYAAAAAAQAGAAAAAAMAfQAAAAAAAH0AAAAAAAB9AAAAAAAAKQAAAAABAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAADAAYAAAAAAgAGAAAAAAAABgAAAAACAA== version: 7 -1,1: ind: -1,1 - tiles: KQAAAAADACkAAAAAAwAkAAAAAAMAJAAAAAACACkAAAAAAgAXAAAAAAAAFwAAAAAAAGsAAAAAAQBnAAAAAAAAZwAAAAAAAGAAAAAAAwBgAAAAAAAAYAAAAAABAGAAAAAAAwBgAAAAAAEAgQAAAAAAACkAAAAAAQApAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAJAAAAAACAIEAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAkAAAAAAMAgQAAAAAAAIEAAAAAAAApAAAAAAEAKQAAAAACACkAAAAAAQApAAAAAAMAKQAAAAABACUAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAXAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAIAKQAAAAADACkAAAAAAQApAAAAAAAAKQAAAAADACQAAAAAAgApAAAAAAMAgQAAAAAAAIEAAAAAAABqAAAAAAAAYAAAAAAAABcAAAAAAABgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAABACkAAAAAAgApAAAAAAIAKQAAAAABACIAAAAAAAApAAAAAAIAKQAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAIAKQAAAAACACkAAAAAAgApAAAAAAIAJAAAAAACACkAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAAYAAAAAAAAGAAAAAAAAfgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAFAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAABQAAAAAAAH4AAAAAAwB+AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAIAfgAAAAACAAcAAAAAAwBPAAAAAAAATwAAAAAAAE8AAAAAAAAFAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAABgAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAAcAAAAAAAAHAAAAAAAATgAAAAAAAAYAAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAHAAcAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAA== + tiles: KQAAAAADACkAAAAAAwAkAAAAAAMAJAAAAAACACkAAAAAAgAXAAAAAAAAFwAAAAAAAGsAAAAAAQBnAAAAAAAAZwAAAAAAAGAAAAAAAwBgAAAAAAAAYAAAAAABAGAAAAAAAwBgAAAAAAEAgQAAAAAAACkAAAAAAQApAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAJAAAAAACAIEAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAACBAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAkAAAAAAMAgQAAAAAAAIEAAAAAAAApAAAAAAEAKQAAAAACACkAAAAAAQApAAAAAAMAKQAAAAABACUAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAXAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAIAKQAAAAADACkAAAAAAQApAAAAAAAAKQAAAAADACQAAAAAAgApAAAAAAMAgQAAAAAAAIEAAAAAAABqAAAAAAAAYAAAAAAAABcAAAAAAABgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAABACkAAAAAAgApAAAAAAIAKQAAAAABACIAAAAAAAApAAAAAAIAKQAAAAAAAIEAAAAAAABgAAAAAAMAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAApAAAAAAIAKQAAAAACACkAAAAAAgApAAAAAAIAJAAAAAACACkAAAAAAACBAAAAAAAAgQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAAYAAAAAAAAGAAAAAAAAfgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAANgAAAAAAAH4AAAAAAwB+AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAIAfgAAAAACAAcAAAAAAwBPAAAAAAAATwAAAAAAAE8AAAAAAAA2AAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAABgAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAAcAAAAAAAAHAAAAAAAATgAAAAAAAAYAAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAHAAcAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAA== version: 7 0,1: ind: 0,1 - tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAcwAAAAAAAIEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAACBAAAAAAAAfQAAAAAAAIEAAAAAAABzAAAAAAAAcwAAAAAAAHMAAAAAAACBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAIEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAgQAAAAAAAH0AAAAAAACBAAAAAAAAeAAAAAAAAHgAAAAAAABzAAAAAAAAgQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAACBAAAAAAAAQQAAAAAAACkAAAAAAAApAAAAAAIAgQAAAAAAAIEAAAAAAAApAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABPAAAAAAAATwAAAAAAAGAAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAACBAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAgQAAAAAAAIEAAAAAAAAFAAAAAAAABQAAAAAAAAYAAAAAAAAFAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAIEAAAAAAAAFAAAAAAAATwAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAAApAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAACQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAFAAAAAAAABgAAAAAAAAYAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAAAFAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAABQAAAAAAAAYAAAAAAAAGAAAAAAAAKQAAAAAAACkAAAAAAAAkAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAABwAAAAAAAAMAAAAAAABOAAAAAAAATwAAAAAAAH4AAAAAAAAaAAAAAAAAGgAAAAAAAAUAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAAMAAAAAAAADAAAAAAAATwAAAAAAAH4AAAAAAAB9AAAAAAAAGgAAAAAAAAAAAAAAAAAFAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAIAAAAAAAAB9AAAAAAEAgAAAAAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: gQAAAAAAAIEAAAAAAACBAAAAAAAAcwAAAAAAAIEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAACBAAAAAAAAfQAAAAAAAIEAAAAAAABzAAAAAAAAcwAAAAAAAHMAAAAAAACBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAIEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAABBAAAAAAAAgQAAAAAAAH0AAAAAAACBAAAAAAAAeAAAAAAAAHgAAAAAAABzAAAAAAAAgQAAAAAAAEEAAAAAAABBAAAAAAAAQQAAAAAAAEEAAAAAAACBAAAAAAAAQQAAAAAAACkAAAAAAAApAAAAAAIAgQAAAAAAAIEAAAAAAAApAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABPAAAAAAAATwAAAAAAAGAAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAACBAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAACBAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAgQAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAAANgAAAAAAAAYAAAAAAAA2AAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAIEAAAAAAAA2AAAAAAAATwAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAAApAAAAAAAABgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAACQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAA2AAAAAAAABgAAAAAAAAYAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAAA2AAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAANgAAAAAAAAYAAAAAAAAGAAAAAAAAKQAAAAAAACkAAAAAAAAkAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAE8AAAAAAABOAAAAAAAATwAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAABwAAAAAAAAMAAAAAAABOAAAAAAAATwAAAAAAAH4AAAAAAAAaAAAAAAAAGgAAAAAAADYAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAAABwAAAAAAAAMAAAAAAAADAAAAAAAATwAAAAAAAH4AAAAAAAB9AAAAAAAAGgAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAIAAAAAAAAB9AAAAAAEAgAAAAAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 -2,0: ind: -2,0 - tiles: fgAAAAAAAE8AAAAAAACBAAAAAAAAcAAAAAAAAGAAAAAAAgCBAAAAAAAAcAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAAIEAAAAAAACBAAAAAAAAdwAAAAACAHwAAAAAAQB8AAAAAAAAgQAAAAAAAH4AAAAAAABPAAAAAAAATwAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAAUAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAACBAAAAAAAAagAAAAACAGAAAAAAAwCBAAAAAAAAaAAAAAADAIEAAAAAAAB+AAAAAAAATwAAAAAAAE4AAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAAFAAAAAAIAOAAAAAAAADgAAAAAAAA4AAAAAAAAYAAAAAAAAHAAAAAAAABgAAAAAAAAaAAAAAACAGAAAAAAAgCBAAAAAAAAfgAAAAAAAE8AAAAAAABOAAAAAAAANgAAAAAAADYAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAABgAAAAAAIAagAAAAACAIEAAAAAAABrAAAAAAMAagAAAAABAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAwCBAAAAAAAAgQAAAAAAAGcAAAAAAABwAAAAAAAAagAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAaAAAAAACAIEAAAAAAABPAAAAAAAATgAAAAAAAAYAAAAAAgAGAAAAAAAAgQAAAAAAAGoAAAAAAgBwAAAAAAAAgQAAAAAAAGcAAAAAAABnAAAAAAAAgQAAAAAAAH0AAAAAAgCBAAAAAAAAHAAAAAAAABwAAAAAAACBAAAAAAAATwAAAAAAAE4AAAAAAAAGAAAAAAAABgAAAAAAAIEAAAAAAABgAAAAAAIAZwAAAAAAAHAAAAAAAABnAAAAAAAAgQAAAAAAAIEAAAAAAAB9AAAAAAIAgQAAAAAAABwAAAAAAAAcAAAAAAAAgQAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAIAKQAAAAABAHAAAAAAAABqAAAAAAMAYAAAAAADAGoAAAAAAQCBAAAAAAAAfQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABOAAAAAAAATwAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAgApAAAAAAAAYAAAAAABAIEAAAAAAABnAAAAAAAAgQAAAAAAAH0AAAAAAAB9AAAAAAIAgQAAAAAAAH4AAAAAAgCBAAAAAAAATwAAAAAAAE8AAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgAGAAAAAAEABgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAB9AAAAAAIAfQAAAAAAAIEAAAAAAAB9AAAAAAMAgQAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAABAAYAAAAAAQAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAAABgAAAAADAIEAAAAAAAB9AAAAAAMAFgAAAAABAH0AAAAAAgB9AAAAAAMAfQAAAAADAIEAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAAAGAAAAAAEABgAAAAADAAYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAwCBAAAAAAAAfQAAAAABAH0AAAAAAwB9AAAAAAAAfQAAAAACAH0AAAAAAQCBAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAfQAAAAADAH0AAAAAAwAVAAAAAAAAgQAAAAAAAIEAAAAAAAAWAAAAAAMAgQAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAACkAAAAAAwApAAAAAAEAgQAAAAAAAIEAAAAAAAAWAAAAAAAAfQAAAAADAH0AAAAAAwBOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAIAfgAAAAADAH4AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAAfgAAAAACAH4AAAAAAgApAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== + tiles: fgAAAAAAAE8AAAAAAACBAAAAAAAAcAAAAAAAAGAAAAAAAgCBAAAAAAAAcAAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAIEAAAAAAACBAAAAAAAAdwAAAAACAHwAAAAAAQB8AAAAAAAAgQAAAAAAAH4AAAAAAABPAAAAAAAATwAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAACBAAAAAAAAagAAAAACAGAAAAAAAwCBAAAAAAAAaAAAAAADAIEAAAAAAAB+AAAAAAAATwAAAAAAAE4AAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAAAYAAAAAAAAHAAAAAAAABgAAAAAAAAaAAAAAACAGAAAAAAAgCBAAAAAAAAfgAAAAAAAE8AAAAAAABOAAAAAAAANgAAAAAAADYAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABgAAAAAAMAgQAAAAAAAIEAAAAAAABgAAAAAAIAagAAAAACAIEAAAAAAABrAAAAAAMAagAAAAABAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAwCBAAAAAAAAgQAAAAAAAGcAAAAAAABwAAAAAAAAagAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAaAAAAAACAIEAAAAAAABPAAAAAAAATgAAAAAAAAYAAAAAAgAGAAAAAAAAgQAAAAAAAGoAAAAAAgBwAAAAAAAAgQAAAAAAAGcAAAAAAABnAAAAAAAAgQAAAAAAAH0AAAAAAgCBAAAAAAAAHAAAAAAAABwAAAAAAACBAAAAAAAATwAAAAAAAE4AAAAAAAAGAAAAAAAABgAAAAAAAIEAAAAAAABgAAAAAAIAZwAAAAAAAHAAAAAAAABnAAAAAAAAgQAAAAAAAIEAAAAAAAB9AAAAAAIAgQAAAAAAABwAAAAAAAAcAAAAAAAAgQAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAIAKQAAAAABAHAAAAAAAABqAAAAAAMAYAAAAAADAGoAAAAAAQCBAAAAAAAAfQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAABOAAAAAAAATwAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAgApAAAAAAAAYAAAAAABAIEAAAAAAABnAAAAAAAAgQAAAAAAAH0AAAAAAAB9AAAAAAIAgQAAAAAAAH4AAAAAAgCBAAAAAAAATwAAAAAAAE8AAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgAGAAAAAAEABgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAB9AAAAAAIAfQAAAAAAAIEAAAAAAAB9AAAAAAMAgQAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAABAAYAAAAAAQAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAAABgAAAAADAIEAAAAAAAB9AAAAAAMAFgAAAAABAH0AAAAAAgB9AAAAAAMAfQAAAAADAIEAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAAAGAAAAAAEABgAAAAADAAYAAAAAAgAGAAAAAAAABgAAAAACAAYAAAAAAwCBAAAAAAAAfQAAAAABAH0AAAAAAwB9AAAAAAAAfQAAAAACAH0AAAAAAQCBAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAfQAAAAADAH0AAAAAAwAVAAAAAAAAgQAAAAAAAIEAAAAAAAAWAAAAAAMAgQAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAACkAAAAAAwApAAAAAAEAgQAAAAAAAIEAAAAAAAAWAAAAAAAAfQAAAAADAH0AAAAAAwBOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAIAfgAAAAADAH4AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAAfgAAAAACAH4AAAAAAgApAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAA== version: 7 -2,-1: ind: -2,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAB9AAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAfgAAAAAAAIAAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAYAAAAAAAB+AAAAAAAAfQAAAAAAAH4AAAAAAABwAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAgQAAAAAAAH0AAAAAAAB+AAAAAAAABgAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAagAAAAAAAGAAAAAAAAB+AAAAAAAAfQAAAAAAAAYAAAAAAAAFAAAAAAAABgAAAAAAAAUAAAAAAAAGAAAAAAAABQAAAAAAAAYAAAAAAAAGAAAAAAAAgAAAAAAAAHAAAAAAAABwAAAAAAAAagAAAAAAAGsAAAAAAACBAAAAAAAAfgAAAAAAAH0AAAAAAAAGAAAAAAAABQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAH4AAAAAAABwAAAAAAAAcAAAAAAAAGAAAAAAAABgAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAB+AAAAAAAAYAAAAAAAAGAAAAAAAAAmAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAEABQAAAAAAAAUAAAAAAgAFAAAAAAAABQAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAFQAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAfQAAAAADAH0AAAAAAwCBAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAQAGAAAAAAEATgAAAAAAAE4AAAAAAACBAAAAAAAAfQAAAAABAH0AAAAAAgB9AAAAAAMAgQAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAVAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAE4AAAAAAABOAAAAAAAAgQAAAAAAAH0AAAAAAwB9AAAAAAMAfQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAIEAAAAAAABOAAAAAAAATgAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAANwAAAAACADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAAwAAAAAAAAMAAAAAAABwAAAAAAAAZwAAAAAAAHAAAAAAAABwAAAAAAAAgQAAAAAAAIEAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAAAMAAAAAAAADAAAAAAAAcAAAAAAAAGoAAAAAAgBnAAAAAAAAagAAAAADAHAAAAAAAABwAAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAADAAAAAAAATgAAAAAAAHAAAAAAAABnAAAAAAAAcAAAAAAAAGoAAAAAAQBwAAAAAAAAcAAAAAAAADgAAAAAAAA4AAAAAAAAOAAAAAAAADgAAAAAAAA4AAAAAAAAfAAAAAADAHwAAAAAAACBAAAAAAAATgAAAAAAAE4AAAAAAABwAAAAAAAAagAAAAACAGcAAAAAAABnAAAAAAAAcAAAAAAAAHAAAAAAAAA4AAAAAAAAOAAAAAAAADYAAAAAAAA2AAAAAAAAfAAAAAADAHcAAAAAAwB8AAAAAAAAgQAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAB9AAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAAfgAAAAAAAIAAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAAYAAAAAAAB+AAAAAAAAfQAAAAAAAH4AAAAAAABwAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAgQAAAAAAAH0AAAAAAAB+AAAAAAAABgAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAagAAAAAAAGAAAAAAAAB+AAAAAAAAfQAAAAAAAAYAAAAAAAA2AAAAAAAABgAAAAAAADYAAAAAAAAGAAAAAAAANgAAAAAAAAYAAAAAAAAGAAAAAAAAgAAAAAAAAHAAAAAAAABwAAAAAAAAagAAAAAAAGsAAAAAAACBAAAAAAAAfgAAAAAAAH0AAAAAAAAGAAAAAAAANgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAH4AAAAAAABwAAAAAAAAcAAAAAAAAGAAAAAAAABgAAAAAAAAgQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAB+AAAAAAAAYAAAAAAAAGAAAAAAAAAmAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAEANgAAAAAAADYAAAAAAgA2AAAAAAAANgAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAFQAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAABAAYAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAfQAAAAADAH0AAAAAAwCBAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAQAGAAAAAAEATgAAAAAAAE4AAAAAAACBAAAAAAAAfQAAAAABAH0AAAAAAgB9AAAAAAMAgQAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAVAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAE4AAAAAAABOAAAAAAAAgQAAAAAAAH0AAAAAAwB9AAAAAAMAfQAAAAACAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAIEAAAAAAABOAAAAAAAATgAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAANwAAAAACADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAAwAAAAAAAAMAAAAAAABwAAAAAAAAZwAAAAAAAHAAAAAAAABwAAAAAAAAgQAAAAAAAIEAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAMAAAAAAAADAAAAAAAAcAAAAAAAAGoAAAAAAgBnAAAAAAAAagAAAAADAHAAAAAAAABwAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAADAAAAAAAATgAAAAAAAHAAAAAAAABnAAAAAAAAcAAAAAAAAGoAAAAAAQBwAAAAAAAAcAAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAfAAAAAADAHwAAAAAAACBAAAAAAAATgAAAAAAAE4AAAAAAABwAAAAAAAAagAAAAACAGcAAAAAAABnAAAAAAAAcAAAAAAAAHAAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAA2AAAAAAAAfAAAAAADAHcAAAAAAwB8AAAAAAAAgQAAAAAAAA== version: 7 1,1: ind: 1,1 - tiles: fQAAAAAAAH0AAAAAAAB9AAAAAAAAKQAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAADAAYAAAAAAAAGAAAAAAEABQAAAAADAH0AAAAAAAB9AAAAAAAAfQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAQAGAAAAAAAABQAAAAAAAAUAAAAAAAApAAAAAAAAKQAAAAAAAAUAAAAAAQAGAAAAAAIABgAAAAACAAYAAAAAAQAGAAAAAAMABgAAAAACAAYAAAAAAAAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAIABgAAAAAAAAUAAAAAAAADAAAAAAAATwAAAAAAAE8AAAAAAAAFAAAAAAMABgAAAAABAAYAAAAAAQAGAAAAAAEABgAAAAACAAYAAAAAAQAGAAAAAAIABgAAAAAAAAYAAAAAAwAGAAAAAAEABgAAAAADAAYAAAAAAgAFAAAAAAMAAwAAAAAAAE8AAAAAAABPAAAAAAAABQAAAAACAAYAAAAAAgAGAAAAAAIABgAAAAACAAYAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAACAAYAAAAAAAAGAAAAAAIABQAAAAACAAAAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAGAAAAAAEABgAAAAAAAAYAAAAAAQAFAAAAAAMABQAAAAAAAAUAAAAAAgAFAAAAAAAABQAAAAACAAUAAAAAAwAFAAAAAAEABQAAAAABAAUAAAAAAgAFAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAAAAAYAAAAAAwApAAAAAAAABQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAJAAAAAAAACkAAAAAAAAlAAAAAAAAKQAAAAAAAAYAAAAAAAAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAAAGAAAAAAMABQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAADAAUAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAKQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAQAFAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKQAAAAAAACkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: fQAAAAAAAH0AAAAAAAB9AAAAAAAAKQAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAADAAYAAAAAAAAGAAAAAAEANgAAAAADAH0AAAAAAAB9AAAAAAAAfQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAQAGAAAAAAAANgAAAAAAADYAAAAAAAApAAAAAAAAKQAAAAAAADYAAAAAAQAGAAAAAAIABgAAAAACAAYAAAAAAQAGAAAAAAMABgAAAAACAAYAAAAAAAAGAAAAAAAABgAAAAACAAYAAAAAAAAGAAAAAAIABgAAAAAAADYAAAAAAAADAAAAAAAATwAAAAAAAE8AAAAAAAA2AAAAAAMABgAAAAABAAYAAAAAAQAGAAAAAAEABgAAAAACAAYAAAAAAQAGAAAAAAIABgAAAAAAAAYAAAAAAwAGAAAAAAEABgAAAAADAAYAAAAAAgA2AAAAAAMAAwAAAAAAAE8AAAAAAABPAAAAAAAANgAAAAACAAYAAAAAAgAGAAAAAAIABgAAAAACAAYAAAAAAAAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAMABgAAAAACAAYAAAAAAAAGAAAAAAIANgAAAAACAAAAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAGAAAAAAEABgAAAAAAAAYAAAAAAQA2AAAAAAMANgAAAAAAADYAAAAAAgA2AAAAAAAANgAAAAACADYAAAAAAwA2AAAAAAEANgAAAAABADYAAAAAAgA2AAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAKQAAAAAAAAYAAAAAAwApAAAAAAAANgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAJAAAAAAAACkAAAAAAAAlAAAAAAAAKQAAAAAAAAYAAAAAAAA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKQAAAAAAACkAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAAAGAAAAAAMANgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUAAAAAAAApAAAAAAAAKQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAADADYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAKQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAQA2AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKQAAAAAAACkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkAAAAAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 0,-2: ind: 0,-2 - tiles: AwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAAHAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAcAAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAAbAAAAAAAAfgAAAAAAAA0AAAAAAAB9AAAAAAAABwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAGwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAB+AAAAAAAADQAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH4AAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH4AAAAAAAB+AAAAAAAABwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAYAAAAAAAAGAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAAABQAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAABwAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAMABwAAAAAAAAcAAAAAAAAHAAAAAAAABgAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAAAFAAAAAAMABQAAAAAAAAUAAAAAAAAFAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAGAAAAAAMABQAAAAACAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAADAAUAAAAAAABPAAAAAAAATwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAHAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAABOAAAAAAAATgAAAAAAAAYAAAAAAAAFAAAAAAMABQAAAAACAAUAAAAAAQAkAAAAAAMAJAAAAAADACQAAAAAAAAkAAAAAAAAgQAAAAAAAGAAAAAAAgB9AAAAAAEAfQAAAAAAAH0AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAAGAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAEAIAAAAAAAACAAAAAAAQAgAAAAAAAAJAAAAAADAIEAAAAAAABgAAAAAAIAYAAAAAAAAGAAAAAAAQBgAAAAAAMAYAAAAAADAE8AAAAAAABPAAAAAAAABgAAAAACAAUAAAAAAAAFAAAAAAEABQAAAAACACQAAAAAAQAkAAAAAAMAJAAAAAADACQAAAAAAwCBAAAAAAAAYAAAAAACAGAAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAABPAAAAAAAATwAAAAAAAA== + tiles: AwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAAHAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAcAAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAAbAAAAAAAAfgAAAAAAAA0AAAAAAAB9AAAAAAAABwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAGwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAB+AAAAAAAADQAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH4AAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH4AAAAAAAB+AAAAAAAABwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAYAAAAAAAAGAAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAGAAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAAANgAAAAAAAAcAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAABwAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAMABwAAAAAAAAcAAAAAAAAHAAAAAAAABgAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAABOAAAAAAAATgAAAAAAAE8AAAAAAABOAAAAAAAABwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAYAAAAAAAA2AAAAAAMANgAAAAAAADYAAAAAAAA2AAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAAGAAAAAAMANgAAAAACAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAABgAAAAADADYAAAAAAABPAAAAAAAATwAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAHAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAABOAAAAAAAATgAAAAAAAAYAAAAAAAA2AAAAAAMANgAAAAACADYAAAAAAQAkAAAAAAMAJAAAAAADACQAAAAAAAAkAAAAAAAAgQAAAAAAAGAAAAAAAgB9AAAAAAEAfQAAAAAAAH0AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAAGAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAEAIAAAAAAAACAAAAAAAQAgAAAAAAAAJAAAAAADAIEAAAAAAABgAAAAAAIAYAAAAAAAAGAAAAAAAQBgAAAAAAMAYAAAAAADAE8AAAAAAABPAAAAAAAABgAAAAACADYAAAAAAAA2AAAAAAEANgAAAAACACQAAAAAAQAkAAAAAAMAJAAAAAADACQAAAAAAwCBAAAAAAAAYAAAAAACAGAAAAAAAABgAAAAAAIAYAAAAAABAIEAAAAAAABPAAAAAAAATwAAAAAAAA== version: 7 1,-2: ind: 1,-2 @@ -203,23 +200,23 @@ entities: version: 7 -2,1: ind: -2,1 - tiles: TgAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAACkAAAAAAAApAAAAAAMAgQAAAAAAAIEAAAAAAAApAAAAAAAAKQAAAAADAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAfgAAAAAAAH4AAAAAAAB9AAAAAAAAfgAAAAAAAH4AAAAAAAApAAAAAAIAKQAAAAABACkAAAAAAAApAAAAAAMAKQAAAAAAACkAAAAAAgBPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAH4AAAAAAAB9AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAKQAAAAAAACkAAAAAAQCBAAAAAAAAgQAAAAAAACkAAAAAAgApAAAAAAIATgAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAE4AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAABOAAAAAAAAfgAAAAAAAH4AAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAfgAAAAADAH4AAAAAAQAWAAAAAAQAfgAAAAADAH0AAAAAAwB+AAAAAAMATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAH4AAAAAAQB+AAAAAAMAfQAAAAACAH4AAAAAAwB+AAAAAAIAfQAAAAADABkAAAAAAAAZAAAAAAAAGQAAAAAAABkAAAAAAAAZAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAFgAAAAABAH4AAAAAAgAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAJwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAAB+AAAAAAEAfgAAAAABAH8AAAAAAQB/AAAAAAAAfwAAAAACAH4AAAAAAQB+AAAAAAMAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABkAAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAACBAAAAAAAAfgAAAAAAAH0AAAAAAAB9AAAAAAIAfgAAAAADAH4AAAAAAgB+AAAAAAMATgAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAZAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAAfgAAAAADAH8AAAAAAQB9AAAAAAIAfwAAAAABAH4AAAAAAAAHAAAAAAAATgAAAAAAAE4AAAAAAAAZAAAAAAAAKAAAAAAAABkAAAAAAAAoAAAAAAAAKAAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAH4AAAAAAwB+AAAAAAIAfgAAAAADAH4AAAAAAgB+AAAAAAEATgAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAABIAAAAAAAASAAAAAAIATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAAcAAAAAAgAHAAAAAAMABwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAABAH4AAAAAAABPAAAAAAAATwAAAAAAAAcAAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAEAAwAAAAAAAAMAAAAAAAB+AAAAAAAAfgAAAAABAH4AAAAAAQB+AAAAAAEATgAAAAAAAE8AAAAAAABPAAAAAAAABwAAAAADAAcAAAAABgAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAfgAAAAAAAH0AAAAAAAB+AAAAAAIAfgAAAAABAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAkACAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== + tiles: TgAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAACkAAAAAAAApAAAAAAMAgQAAAAAAAIEAAAAAAAApAAAAAAAAKQAAAAADAE4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAfgAAAAAAAH4AAAAAAAB9AAAAAAAAfgAAAAAAAH4AAAAAAAApAAAAAAIAKQAAAAABACkAAAAAAAApAAAAAAMAKQAAAAAAACkAAAAAAgBPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAH4AAAAAAAB9AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAKQAAAAAAACkAAAAAAQCBAAAAAAAAgQAAAAAAACkAAAAAAgApAAAAAAIATgAAAAAAAE8AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAE4AAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAABOAAAAAAAAfgAAAAAAAH4AAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAE4AAAAAAABPAAAAAAAAfgAAAAADAH4AAAAAAQAWAAAAAAQAfgAAAAADAH0AAAAAAwB+AAAAAAMATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABPAAAAAAAATwAAAAAAAH4AAAAAAQB+AAAAAAMAfQAAAAACAH4AAAAAAwB+AAAAAAIAfQAAAAADABkAAAAAAAAZAAAAAAAAGQAAAAAAABkAAAAAAAAZAAAAAAAATwAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAFgAAAAABAH4AAAAAAgAYAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAAAJwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE8AAAAAAAB+AAAAAAEAfgAAAAABAH8AAAAAAQB/AAAAAAAAfwAAAAACAH4AAAAAAQB+AAAAAAMAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABkAAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAACBAAAAAAAAfgAAAAAAAH0AAAAAAAB9AAAAAAIAfgAAAAADAH4AAAAAAgB+AAAAAAMATgAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAAZAAAAAAAATgAAAAAAAE8AAAAAAABPAAAAAAAAfgAAAAADAH8AAAAAAQB9AAAAAAIAfwAAAAABAH4AAAAAAAAHAAAAAAAATgAAAAAAAE4AAAAAAAAZAAAAAAAAKAAAAAAAABkAAAAAAAAoAAAAAAAAKAAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAH4AAAAAAwB+AAAAAAIAfgAAAAADAH4AAAAAAgB+AAAAAAEATgAAAAAAAE4AAAAAAABOAAAAAAAATwAAAAAAAB0AAAAAAAAdAAAAAAIATwAAAAAAAE8AAAAAAABPAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAAcAAAAAAgAHAAAAAAMABwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAABAH4AAAAAAABPAAAAAAAATwAAAAAAAAcAAAAAAABOAAAAAAAATgAAAAAAAE4AAAAAAAAHAAAAAAAABwAAAAAAAAcAAAAAAAAHAAAAAAEAAwAAAAAAAAMAAAAAAAB+AAAAAAAAfgAAAAABAH4AAAAAAQB+AAAAAAEATgAAAAAAAE8AAAAAAABPAAAAAAAABwAAAAADAAcAAAAABgAHAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAfgAAAAAAAH0AAAAAAAB+AAAAAAIAfgAAAAABAE8AAAAAAABOAAAAAAAATwAAAAAAAE8AAAAAAAAHAAAAAAkACAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== version: 7 -1,-2: ind: -1,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAABQAAAAAAAAAAAAAAAAAFAAAAAAIABQAAAAAAAAUAAAAAAAAFAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAADAAYAAAAAAAAAAAAAAAAABQAAAAADAAYAAAAAAQAGAAAAAAEABQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAQAGAAAAAAIAAAAAAAAAAAUAAAAAAgAGAAAAAAMABgAAAAAAAAUAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAMABgAAAAAAAAAAAAAAAAAFAAAAAAEABgAAAAAAAAYAAAAAAAAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAYAAAAAAQAAAAAAAAAABQAAAAABAAYAAAAAAQAGAAAAAAEABQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAgAGAAAAAAEAAAAAAAAAAAUAAAAAAQAGAAAAAAMABgAAAAABAAUAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAEABgAAAAADAAUAAAAAAAAFAAAAAAEABgAAAAAAAAYAAAAAAwAFAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAYAAAAAAQAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAIABQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAgAGAAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAABAAUAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAMABgAAAAABAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAAAAAAAAAAAA2AAAAAAIANgAAAAAAADYAAAAAAAA2AAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAADAAYAAAAAAAAAAAAAAAAANgAAAAADAAYAAAAAAQAGAAAAAAEANgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAQAGAAAAAAIAAAAAAAAAADYAAAAAAgAGAAAAAAMABgAAAAAAADYAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAMABgAAAAAAAAAAAAAAAAA2AAAAAAEABgAAAAAAAAYAAAAAAAA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACAAYAAAAAAQAAAAAAAAAANgAAAAABAAYAAAAAAQAGAAAAAAEANgAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAgAGAAAAAAEAAAAAAAAAADYAAAAAAQAGAAAAAAMABgAAAAABADYAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAEABgAAAAADADYAAAAAAAA2AAAAAAEABgAAAAAAAAYAAAAAAwA2AAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACAAYAAAAAAQAGAAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAIANgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAgAGAAAAAAIABgAAAAAAAAYAAAAAAAAGAAAAAAIABgAAAAABADYAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAMABgAAAAABAA== version: 7 2,0: ind: 2,0 - tiles: BgAAAAAAAAYAAAAAAwAFAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABgAAAAACAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAwAGAAAAAAIABgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAMABQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAUAAAAAAwADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAgAFAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAMABQAAAAADAAUAAAAAAwADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAADAAYAAAAAAgAFAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAQAGAAAAAAMABgAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABgAAAAACAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAYAAAAAAwAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAwAGAAAAAAAABQAAAAAAAAUAAAAAAgAFAAAAAAAABQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABQAAAAADAAUAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAUAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: BgAAAAAAAAYAAAAAAwA2AAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAgAGAAAAAAIABgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABgAAAAACAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAYAAAAAAgAGAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAwAGAAAAAAIABgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAMANgAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABADYAAAAAAwADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAgA2AAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAMANgAAAAADADYAAAAAAwADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAADAAYAAAAAAgA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAQAGAAAAAAMABgAAAAADAAYAAAAAAAAGAAAAAAAABgAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIABgAAAAACAAYAAAAAAwAGAAAAAAAABgAAAAAAAAYAAAAAAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAABAAYAAAAAAwAGAAAAAAMABgAAAAAAAAYAAAAAAAAGAAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAwAGAAAAAAAANgAAAAAAADYAAAAAAgA2AAAAAAAANgAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAIANgAAAAADADYAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACADYAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 2,1: ind: 2,1 - tiles: BQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: NgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 -3,-1: ind: -3,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAawAAAAAAAHAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAGAAAAAAAAAmAAAAAAAAawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAABgAAAAAAAAYAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAYAAAAAAAAGsAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAIEAAAAAAABqAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAgQAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAAAAAE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAawAAAAAAAHAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAGAAAAAAAAAmAAAAAAAAawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEAAAAAAABgAAAAAAAAYAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBAAAAAAAAYAAAAAAAAGsAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAAAAIEAAAAAAABqAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAAgQAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAAAAAIEAAAAAAACBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4AAAAAAABOAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAAAAAE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAA== version: 7 -3,0: ind: -3,0 @@ -255,7 +252,7 @@ entities: version: 7 2,-1: ind: 2,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4AAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAACAAUAAAAAAAAFAAAAAAIAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE4AAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAAMAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAADAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAACADYAAAAAAAA2AAAAAAIAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 2,-2: ind: 2,-2 @@ -271,7 +268,7 @@ entities: version: 7 -2,-2: ind: -2,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABQAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAYAAAAAAAAGAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB+AAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAGAAAAAAAABgAAAAAAAH0AAAAAAAB9AAAAAAAAfgAAAAAAAIAAAAAAAAB+AAAAAAAAfQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAAAAAYAAAAAAAAGAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB+AAAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYAAAAAAAAGAAAAAAAABgAAAAAAAH0AAAAAAAB9AAAAAAAAfgAAAAAAAIAAAAAAAAB+AAAAAAAAfQAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAA== version: 7 - type: Broadphase - type: Physics @@ -4499,6 +4496,8 @@ entities: - type: FTLDestination - type: GravityShake shakeTimes: 10 + - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 5 components: - type: MetaData @@ -4590,7 +4589,7 @@ entities: chunks: 0,0: ind: 0,0 - tiles: LgAAAAAAAC4AAAAAAgAuAAAAAAIALgAAAAACAC4AAAAAAAAuAAAAAAMALgAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAC4AAAAAAwAuAAAAAAEALgAAAAABAC4AAAAAAQAuAAAAAAAAbwAAAAAAAC4AAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAIAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAAAuAAAAAAAALgAAAAAAAC4AAAAAAgAuAAAAAAEALgAAAAACAC4AAAAAAAAuAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAANgAAAAACAIQAAAAAAACEAAAAAAAANgAAAAAAADYAAAAAAgA2AAAAAAAALgAAAAABAC4AAAAAAQAuAAAAAAMALgAAAAACAC4AAAAAAwAuAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAwA2AAAAAAEAhAAAAAAAADgAAAAAAwA2AAAAAAEANgAAAAABAIEAAAAAAACBAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAA2AAAAAAEAgQAAAAAAADYAAAAAAQA2AAAAAAEANgAAAAAAADgAAAAAAACBAAAAAAAAgQAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAOAAAAAAAAIEAAAAAAACEAAAAAAAANgAAAAACADYAAAAAAgA2AAAAAAMAgQAAAAAAAIEAAAAAAACEAAAAAAAANgAAAAACADgAAAAAAQA2AAAAAAIANgAAAAADADYAAAAAAgCBAAAAAAAAgQAAAAAAADYAAAAAAgCEAAAAAAAAhAAAAAAAADYAAAAAAwA2AAAAAAAANgAAAAADAIEAAAAAAACBAAAAAAAAhAAAAAAAADYAAAAAAQA2AAAAAAEANgAAAAADADYAAAAAAAA2AAAAAAIAgQAAAAAAAIEAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAAuAAAAAAMANgAAAAACADYAAAAAAAA4AAAAAAAANgAAAAACAIEAAAAAAAA2AAAAAAIANgAAAAABAIQAAAAAAAA4AAAAAAAANgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACEAAAAAAAALgAAAAADAC4AAAAAAQA2AAAAAAMANgAAAAABAIEAAAAAAACBAAAAAAAALgAAAAABADYAAAAAAACEAAAAAAAANgAAAAABADYAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAANgAAAAABADYAAAAAAAA4AAAAAAIANgAAAAACADYAAAAAAgCBAAAAAAAAgQAAAAAAAC4AAAAAAgAuAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAMANgAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAMAAAAAAAADAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAE4AAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAATgAAAAAAAAwAAAAAAAAMAAAAAAAAAwAAAAAAAA== + tiles: LgAAAAAAAC4AAAAAAgAuAAAAAAIALgAAAAACAC4AAAAAAAAuAAAAAAMALgAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAC4AAAAAAwAuAAAAAAEALgAAAAABAC4AAAAAAQAuAAAAAAAAbwAAAAAAAC4AAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAIAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAAAuAAAAAAAALgAAAAAAAC4AAAAAAgAuAAAAAAEALgAAAAACAC4AAAAAAAAuAAAAAAEAgQAAAAAAAIEAAAAAAACBAAAAAAAANgAAAAACAIQAAAAAAACEAAAAAAAANgAAAAAAADYAAAAAAgA2AAAAAAAALgAAAAABAC4AAAAAAQAuAAAAAAMALgAAAAACAC4AAAAAAwAuAAAAAAIAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAwA2AAAAAAEAhAAAAAAAAAYAAAAAAwA2AAAAAAEANgAAAAABAIEAAAAAAACBAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAA2AAAAAAEAgQAAAAAAADYAAAAAAQA2AAAAAAEANgAAAAAAAAYAAAAAAACBAAAAAAAAgQAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAABgAAAAAAAIEAAAAAAACEAAAAAAAANgAAAAACADYAAAAAAgA2AAAAAAMAgQAAAAAAAIEAAAAAAACEAAAAAAAANgAAAAACAAYAAAAAAQA2AAAAAAIANgAAAAADADYAAAAAAgCBAAAAAAAAgQAAAAAAADYAAAAAAgCEAAAAAAAAhAAAAAAAADYAAAAAAwA2AAAAAAAANgAAAAADAIEAAAAAAACBAAAAAAAAhAAAAAAAADYAAAAAAQA2AAAAAAEANgAAAAADADYAAAAAAAA2AAAAAAIAgQAAAAAAAIEAAAAAAACEAAAAAAAAhAAAAAAAAIQAAAAAAACEAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAADYAAAAAAAAuAAAAAAMANgAAAAACADYAAAAAAAAGAAAAAAAANgAAAAACAIEAAAAAAAA2AAAAAAIANgAAAAABAIQAAAAAAAAGAAAAAAAANgAAAAADAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACEAAAAAAAALgAAAAADAC4AAAAAAQA2AAAAAAMANgAAAAABAIEAAAAAAACBAAAAAAAALgAAAAABADYAAAAAAACEAAAAAAAANgAAAAABADYAAAAAAgCBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAANgAAAAABADYAAAAAAAAGAAAAAAIANgAAAAACADYAAAAAAgCBAAAAAAAAgQAAAAAAAC4AAAAAAgAuAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAA2AAAAAAMANgAAAAABAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAAAMAAAAAAAADAAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAIEAAAAAAACBAAAAAAAAgQAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAE4AAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAATgAAAAAAAAwAAAAAAAAMAAAAAAAAAwAAAAAAAA== version: 7 -1,0: ind: -1,0 @@ -6389,6 +6388,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 7784 components: - type: MetaData @@ -6500,6 +6500,7 @@ entities: - type: IFF flags: Hide - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8115 components: - type: MetaData @@ -6681,6 +6682,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8173 components: - type: MetaData @@ -6763,6 +6765,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8185 components: - type: MetaData @@ -6855,6 +6858,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8200 components: - type: MetaData @@ -7177,6 +7181,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8246 components: - type: MetaData @@ -7252,6 +7257,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8264 components: - type: MetaData @@ -7329,6 +7335,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8280 components: - type: MetaData @@ -7406,6 +7413,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8320 components: - type: MetaData @@ -7525,6 +7533,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8389 components: - type: MetaData @@ -7655,6 +7664,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 8406 components: - type: MetaData @@ -8006,6 +8016,7 @@ entities: - type: GravityShake shakeTimes: 10 - type: ImplicitRoof + - type: ExplosionAirtightGrid - proto: AcousticGuitarInstrument entities: - uid: 21 @@ -8205,8 +8216,8 @@ entities: - 3523 - 3529 - 3570 - - 3522 - - 3569 + - 3406 + - 3407 - 3571 - 3527 - 3528 @@ -8224,6 +8235,10 @@ entities: - 2789 - 2787 - 2786 + - 8633 + - 114 + - 8634 + - 3522 - type: Fixtures fixtures: {} - proto: Airlock @@ -8954,11 +8969,6 @@ entities: - type: Transform pos: -1.5,10.5 parent: 2 - - uid: 114 - components: - - type: Transform - pos: 6.5,11.5 - parent: 2 - uid: 115 components: - type: Transform @@ -9019,6 +9029,15 @@ entities: - type: DeviceNetwork deviceLists: - 2760 + - uid: 8634 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 4.5,12.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 - proto: AirTankFilled entities: - uid: 125 @@ -27690,18 +27709,6 @@ entities: parent: 6776 - type: Physics canCollide: False -- proto: DrinkKiraSpecial - entities: - - uid: 2663 - components: - - type: Transform - pos: -19.440605,-5.1425204 - parent: 2 - - uid: 2664 - components: - - type: Transform - pos: -18.596855,-5.382104 - parent: 2 - proto: DrinkMugHeart entities: - uid: 2665 @@ -27735,6 +27742,18 @@ entities: parent: 6776 - type: Physics canCollide: False +- proto: DrinkOrangeLimeSodaGlass + entities: + - uid: 2663 + components: + - type: Transform + pos: -19.440605,-5.1425204 + parent: 2 + - uid: 2664 + components: + - type: Transform + pos: -18.596855,-5.382104 + parent: 2 - proto: DrinkShakeMeat entities: - uid: 6790 @@ -28393,9 +28412,6 @@ entities: rot: -1.5707963267948966 rad pos: 9.5,13.5 parent: 2 - - type: DeviceList - devices: - - 114 - type: Fixtures fixtures: {} - uid: 2754 @@ -31164,20 +31180,8 @@ entities: - type: Transform pos: 7.5,16.5 parent: 2 - - uid: 3027 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,10.5 - parent: 2 - - uid: 3028 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,10.5 - parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' + color: '#0000FFFF' - uid: 3029 components: - type: Transform @@ -31218,14 +31222,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 3034 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 3.5,11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 3035 components: - type: Transform @@ -31375,14 +31371,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3054 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,9.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 3055 components: - type: Transform @@ -31405,6 +31393,21 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' + - uid: 3509 + components: + - type: Transform + pos: -2.5,11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3569 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 0.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPipeFourway entities: - uid: 3058 @@ -31463,13 +31466,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3066 - components: - - type: Transform - pos: -4.5,9.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 3067 components: - type: Transform @@ -31486,26 +31482,58 @@ entities: color: '#FF0000FF' - proto: GasPipeStraight entities: + - uid: 3027 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3028 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 4.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3054 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 1.5,15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 3069 components: - type: Transform pos: 3.5,16.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3070 components: - type: Transform pos: 3.5,15.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3071 components: - type: Transform pos: 3.5,14.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3072 components: - type: Transform pos: 8.5,15.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 3073 components: - type: Transform @@ -31722,11 +31750,11 @@ entities: - uid: 3101 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 6.5,11.5 + rot: 3.141592653589793 rad + pos: 1.5,13.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' + color: '#FF0000FF' - uid: 3102 components: - type: Transform @@ -31848,7 +31876,7 @@ entities: - uid: 3117 components: - type: Transform - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad pos: 5.5,11.5 parent: 2 - type: AtmosPipeColor @@ -31918,8 +31946,7 @@ entities: - uid: 3126 components: - type: Transform - rot: 3.141592653589793 rad - pos: 3.5,13.5 + pos: 3.5,12.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' @@ -33753,29 +33780,14 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3359 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 7.5,12.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 3360 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 6.5,12.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 3361 components: - type: Transform - pos: 5.5,11.5 + rot: 1.5707963267948966 rad + pos: 2.5,11.5 parent: 2 - type: AtmosPipeColor - color: '#FF0000FF' + color: '#0000FFFF' - uid: 3362 components: - type: Transform @@ -34105,35 +34117,19 @@ entities: - uid: 3404 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -3.5,9.5 + rot: 3.141592653589793 rad + pos: 1.5,17.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - uid: 3405 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,9.5 + rot: 3.141592653589793 rad + pos: 1.5,16.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3406 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 3407 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 3408 components: - type: Transform @@ -34296,8 +34292,7 @@ entities: - uid: 3428 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -0.5,10.5 + pos: 5.5,11.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -34396,8 +34391,8 @@ entities: - uid: 3441 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,10.5 + rot: 1.5707963267948966 rad + pos: 3.5,12.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -34425,8 +34420,63 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 3464 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 7.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3505 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 6.5,11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3526 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 1.5,14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPipeTJunction entities: + - uid: 3034 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3066 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,9.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3359 + components: + - type: Transform + pos: 4.5,11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3360 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 3.5,11.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3445 components: - type: Transform @@ -34562,7 +34612,8 @@ entities: - uid: 3462 components: - type: Transform - pos: 4.5,11.5 + rot: -1.5707963267948966 rad + pos: 3.5,13.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' @@ -34574,14 +34625,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 3464 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 3.5,12.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 3465 components: - type: Transform @@ -34888,14 +34931,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3505 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 5.5,12.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 3506 components: - type: Transform @@ -34920,13 +34955,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 3509 - components: - - type: Transform - pos: -2.5,11.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 3510 components: - type: Transform @@ -34965,6 +34993,21 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' + - uid: 3568 + components: + - type: Transform + pos: 5.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8632 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 1.5,12.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPort entities: - uid: 3515 @@ -35019,20 +35062,24 @@ entities: parent: 2 - proto: GasVentPump entities: - - uid: 3522 + - uid: 3407 components: - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,11.5 + rot: 1.5707963267948966 rad + pos: 1.5,11.5 parent: 2 - type: DeviceNetwork deviceLists: - 32 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3523 components: - type: Transform pos: 3.5,17.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35042,6 +35089,8 @@ entities: rot: -1.5707963267948966 rad pos: 12.5,14.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35051,18 +35100,16 @@ entities: rot: 3.141592653589793 rad pos: 7.5,8.5 parent: 2 - - uid: 3526 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 4.5,12.5 - parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 3527 components: - type: Transform rot: 1.5707963267948966 rad pos: 6.5,16.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35071,6 +35118,8 @@ entities: - type: Transform pos: 11.5,16.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35080,6 +35129,8 @@ entities: rot: 3.141592653589793 rad pos: 4.5,9.5 parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - type: DeviceNetwork deviceLists: - 32 @@ -35379,26 +35430,57 @@ entities: - 24 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 8633 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 2.5,13.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#0000FFFF' - proto: GasVentScrubber entities: + - uid: 114 + components: + - type: Transform + pos: 6.5,13.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3406 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,11.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3522 + components: + - type: Transform + pos: 1.5,18.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 32 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 3567 components: - type: Transform pos: 12.5,13.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 32 - - uid: 3568 - components: - - type: Transform - pos: 5.5,13.5 - parent: 2 - - uid: 3569 - components: - - type: Transform - pos: 1.5,11.5 - parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - type: DeviceNetwork deviceLists: - 32 @@ -35408,6 +35490,8 @@ entities: rot: 3.141592653589793 rad pos: 5.5,9.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - type: DeviceNetwork deviceLists: - 32 @@ -35416,6 +35500,8 @@ entities: - type: Transform pos: 8.5,16.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - type: DeviceNetwork deviceLists: - 32 @@ -35425,6 +35511,8 @@ entities: rot: 1.5707963267948966 rad pos: 7.5,7.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - type: DeviceNetwork deviceLists: - 32 @@ -38049,13 +38137,13 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage + removedMasks: 20 air: volume: 200 immutable: False temperature: 293.1496 moles: {} open: True - removedMasks: 20 - type: PlaceableSurface isPlaceable: True - proto: LockerWallMedicalFilled @@ -38700,43 +38788,31 @@ entities: - type: Transform pos: 5.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6938 components: - type: Transform pos: 3.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6939 components: - type: Transform pos: 4.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6940 components: - type: Transform pos: 4.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6941 components: - type: Transform pos: 3.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 6942 components: - type: Transform pos: 5.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - proto: Mirror entities: - uid: 3959 @@ -43196,6 +43272,8 @@ entities: - type: RandomSpawner prototypes: - MobBreadDog + rarePrototypes: [] + gameRules: [] - uid: 4516 components: - type: MetaData @@ -43206,6 +43284,8 @@ entities: - type: RandomSpawner prototypes: - MobCatCake + rarePrototypes: [] + gameRules: [] - proto: RandomFloraTree entities: - uid: 4517 @@ -43503,99 +43583,73 @@ entities: - type: Transform pos: 30.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4564 components: - type: Transform rot: 1.5707963267948966 rad pos: 29.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4565 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4566 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4567 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4568 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,-2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4569 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,-3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4570 components: - type: Transform rot: 1.5707963267948966 rad pos: 31.5,-4.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4571 components: - type: Transform pos: 23.5,5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4572 components: - type: Transform pos: 23.5,4.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4573 components: - type: Transform pos: 22.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4574 components: - type: Transform pos: 23.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4575 components: - type: Transform pos: 24.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: ReinforcedWindow entities: - uid: 4576 @@ -43603,705 +43657,517 @@ entities: - type: Transform pos: 9.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4577 components: - type: Transform rot: 3.141592653589793 rad pos: -16.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4578 components: - type: Transform rot: 3.141592653589793 rad pos: -15.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4579 components: - type: Transform rot: 3.141592653589793 rad pos: -18.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4580 components: - type: Transform rot: 3.141592653589793 rad pos: -19.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4581 components: - type: Transform rot: 3.141592653589793 rad pos: -17.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4582 components: - type: Transform pos: -3.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4583 components: - type: Transform rot: 1.5707963267948966 rad pos: 11.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4584 components: - type: Transform pos: -3.5,21.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4585 components: - type: Transform pos: -4.5,21.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4586 components: - type: Transform pos: 5.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4587 components: - type: Transform rot: -1.5707963267948966 rad pos: 15.5,29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4588 components: - type: Transform rot: -1.5707963267948966 rad pos: 14.5,29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4589 components: - type: Transform rot: -1.5707963267948966 rad pos: 16.5,29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4590 components: - type: Transform rot: -1.5707963267948966 rad pos: 17.5,28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4591 components: - type: Transform rot: -1.5707963267948966 rad pos: -4.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4592 components: - type: Transform rot: -1.5707963267948966 rad pos: 13.5,25.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4593 components: - type: Transform rot: 3.141592653589793 rad pos: 20.5,22.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4594 components: - type: Transform pos: 8.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4595 components: - type: Transform rot: 1.5707963267948966 rad pos: 14.5,25.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4596 components: - type: Transform pos: 6.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4597 components: - type: Transform pos: 9.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4598 components: - type: Transform pos: 7.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4599 components: - type: Transform rot: 3.141592653589793 rad pos: -26.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4600 components: - type: Transform rot: -1.5707963267948966 rad pos: 11.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4601 components: - type: Transform rot: 1.5707963267948966 rad pos: 17.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4602 components: - type: Transform rot: 3.141592653589793 rad pos: -25.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4603 components: - type: Transform rot: 1.5707963267948966 rad pos: 16.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4604 components: - type: Transform rot: 1.5707963267948966 rad pos: 15.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4605 components: - type: Transform rot: 3.141592653589793 rad pos: 23.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4606 components: - type: Transform rot: 3.141592653589793 rad pos: 24.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4607 components: - type: Transform rot: -1.5707963267948966 rad pos: 10.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4608 components: - type: Transform pos: 2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4609 components: - type: Transform pos: 1.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4610 components: - type: Transform pos: 0.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4611 components: - type: Transform pos: -0.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4612 components: - type: Transform pos: 5.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4613 components: - type: Transform pos: 3.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4614 components: - type: Transform pos: 12.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4615 components: - type: Transform pos: -9.5,5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4616 components: - type: Transform pos: -7.5,5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4617 components: - type: Transform pos: 26.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4618 components: - type: Transform pos: 19.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4619 components: - type: Transform pos: 3.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4620 components: - type: Transform pos: 5.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4621 components: - type: Transform pos: 12.5,-3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4622 components: - type: Transform pos: 8.5,10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4623 components: - type: Transform pos: 26.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4624 components: - type: Transform pos: 12.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4625 components: - type: Transform pos: 7.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4626 components: - type: Transform pos: 10.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4627 components: - type: Transform pos: 8.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4628 components: - type: Transform pos: 18.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4629 components: - type: Transform pos: 5.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4630 components: - type: Transform pos: 12.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4631 components: - type: Transform pos: 7.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4632 components: - type: Transform rot: 3.141592653589793 rad pos: 20.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4633 components: - type: Transform rot: 1.5707963267948966 rad pos: 25.5,-14.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4634 components: - type: Transform pos: 12.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4635 components: - type: Transform pos: 9.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4636 components: - type: Transform pos: -29.5,-2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4637 components: - type: Transform pos: -29.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4638 components: - type: Transform pos: -7.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4639 components: - type: Transform pos: 26.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4640 components: - type: Transform pos: -29.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4641 components: - type: Transform pos: 26.5,11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4642 components: - type: Transform pos: 26.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4643 components: - type: Transform rot: 3.141592653589793 rad pos: 10.5,12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4644 components: - type: Transform rot: 3.141592653589793 rad pos: 25.5,-12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4645 components: - type: Transform rot: 3.141592653589793 rad pos: 26.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4646 components: - type: Transform rot: 3.141592653589793 rad pos: 27.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4647 components: - type: Transform pos: 9.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4648 components: - type: Transform rot: -1.5707963267948966 rad pos: 21.5,14.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4649 components: - type: Transform rot: 3.141592653589793 rad pos: 22.5,14.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4650 components: - type: Transform rot: 3.141592653589793 rad pos: 23.5,14.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4651 components: - type: Transform rot: 3.141592653589793 rad pos: 17.5,27.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4652 components: - type: Transform rot: 3.141592653589793 rad pos: 18.5,26.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4653 components: - type: Transform rot: 1.5707963267948966 rad pos: 15.5,24.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4654 components: - type: Transform rot: -1.5707963267948966 rad pos: 19.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4655 components: - type: Transform rot: 1.5707963267948966 rad pos: 0.5,-30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4656 components: - type: Transform rot: 1.5707963267948966 rad pos: -0.5,-29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4657 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,-28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4658 components: - type: Transform rot: 1.5707963267948966 rad pos: 6.5,-27.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4659 components: - type: Transform rot: 1.5707963267948966 rad pos: 6.5,-28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4660 components: - type: Transform rot: 1.5707963267948966 rad pos: 6.5,-29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4661 components: - type: Transform pos: -29.5,-13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4662 components: - type: Transform rot: 3.141592653589793 rad pos: -26.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4663 components: - type: Transform rot: 3.141592653589793 rad pos: -25.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4664 components: - type: Transform rot: 3.141592653589793 rad pos: -24.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4665 components: - type: Transform rot: 3.141592653589793 rad pos: -23.5,-18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4666 components: - type: Transform pos: -28.5,-13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4667 components: - type: Transform pos: -33.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4668 components: - type: Transform pos: -34.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4669 components: - type: Transform pos: -35.5,-10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: ReinforcedWindowDiagonal entities: - uid: 4670 @@ -44310,131 +44176,97 @@ entities: rot: -1.5707963267948966 rad pos: 18.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4671 components: - type: Transform rot: 1.5707963267948966 rad pos: 18.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4672 components: - type: Transform rot: -1.5707963267948966 rad pos: 19.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4673 components: - type: Transform pos: -27.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4674 components: - type: Transform rot: 3.141592653589793 rad pos: -25.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4675 components: - type: Transform rot: 3.141592653589793 rad pos: -24.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4676 components: - type: Transform pos: -26.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4677 components: - type: Transform rot: 3.141592653589793 rad pos: -26.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4678 components: - type: Transform pos: -25.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4679 components: - type: Transform rot: 3.141592653589793 rad pos: 13.5,-19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4680 components: - type: Transform rot: 1.5707963267948966 rad pos: 14.5,24.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4681 components: - type: Transform rot: -1.5707963267948966 rad pos: 17.5,29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4682 components: - type: Transform rot: -1.5707963267948966 rad pos: 18.5,27.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4683 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,-29.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4684 components: - type: Transform rot: 1.5707963267948966 rad pos: -0.5,-30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4685 components: - type: Transform pos: -34.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 4686 components: - type: Transform pos: -35.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: ResearchAndDevelopmentServer entities: - uid: 4687 @@ -44981,8 +44813,6 @@ entities: - type: Transform pos: 28.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: SignalButton entities: - uid: 4739 @@ -59335,8 +59165,6 @@ entities: - type: Transform pos: 4.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorBarKitchenLocked entities: - uid: 6119 @@ -59345,8 +59173,6 @@ entities: rot: 1.5707963267948966 rad pos: 5.5,-12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorChapelLocked entities: - uid: 6120 @@ -59355,8 +59181,6 @@ entities: rot: 3.141592653589793 rad pos: -51.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorKitchenHydroponicsLocked entities: - uid: 6121 @@ -59365,8 +59189,6 @@ entities: rot: 3.141592653589793 rad pos: 9.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecure entities: - uid: 6122 @@ -59374,75 +59196,55 @@ entities: - type: Transform pos: -8.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6123 components: - type: Transform pos: -15.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6124 components: - type: Transform pos: -14.5,-1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6125 components: - type: Transform pos: 26.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6126 components: - type: Transform pos: -5.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6127 components: - type: Transform rot: 1.5707963267948966 rad pos: -7.5,-4.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6128 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6129 components: - type: Transform pos: -6.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6130 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7743 components: - type: Transform rot: 3.141592653589793 rad pos: 4.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - type: AccessReader access: - - Engineering @@ -59457,71 +59259,53 @@ entities: rot: 1.5707963267948966 rad pos: -11.5,20.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6132 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6133 components: - type: Transform rot: -1.5707963267948966 rad pos: -12.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6134 components: - type: Transform rot: 3.141592653589793 rad pos: -8.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6135 components: - type: Transform rot: -1.5707963267948966 rad pos: -12.5,20.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6136 components: - type: Transform pos: -8.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7744 components: - type: Transform rot: 3.141592653589793 rad pos: 15.5,2.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7745 components: - type: Transform rot: 3.141592653589793 rad pos: 13.5,2.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7746 components: - type: Transform rot: 3.141592653589793 rad pos: 14.5,2.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - proto: WindoorSecureBrigLocked entities: - uid: 6137 @@ -59530,23 +59314,17 @@ entities: rot: -1.5707963267948966 rad pos: -16.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6138 components: - type: Transform rot: 3.141592653589793 rad pos: -1.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6139 components: - type: Transform pos: -1.5,11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureCargoLocked entities: - uid: 6140 @@ -59555,16 +59333,12 @@ entities: rot: 3.141592653589793 rad pos: 13.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6141 components: - type: Transform rot: 1.5707963267948966 rad pos: 12.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureChemistryLocked entities: - uid: 6142 @@ -59573,15 +59347,11 @@ entities: rot: 3.141592653589793 rad pos: 4.5,6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6143 components: - type: Transform pos: 5.5,10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureEngineeringLocked entities: - uid: 6144 @@ -59590,8 +59360,6 @@ entities: rot: 1.5707963267948966 rad pos: 12.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureHeadOfPersonnelLocked entities: - uid: 6145 @@ -59600,8 +59368,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureMedicalLocked entities: - uid: 6146 @@ -59610,8 +59376,6 @@ entities: rot: -1.5707963267948966 rad pos: 2.5,12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureSalvageLocked entities: - uid: 6147 @@ -59620,8 +59384,6 @@ entities: rot: 3.141592653589793 rad pos: 19.5,22.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorSecureScienceLocked entities: - uid: 6148 @@ -59630,16 +59392,12 @@ entities: rot: 1.5707963267948966 rad pos: 12.5,-2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7747 components: - type: Transform rot: 3.141592653589793 rad pos: 7.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - type: AccessReader access: - - Engineering @@ -59654,8 +59412,6 @@ entities: rot: 3.141592653589793 rad pos: -8.5,5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindoorServiceLocked entities: - uid: 6150 @@ -59663,8 +59419,6 @@ entities: - type: Transform pos: -20.5,24.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: Window entities: - uid: 6151 @@ -59673,240 +59427,174 @@ entities: rot: 3.141592653589793 rad pos: -35.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6152 components: - type: Transform rot: 3.141592653589793 rad pos: -34.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6153 components: - type: Transform rot: 1.5707963267948966 rad pos: -25.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6154 components: - type: Transform pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6155 components: - type: Transform pos: -24.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6156 components: - type: Transform pos: -22.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6157 components: - type: Transform pos: -21.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6158 components: - type: Transform pos: -23.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6159 components: - type: Transform rot: -1.5707963267948966 rad pos: -7.5,1.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6160 components: - type: Transform pos: -29.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6161 components: - type: Transform pos: -30.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6162 components: - type: Transform pos: -31.5,31.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6163 components: - type: Transform pos: -31.5,30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6164 components: - type: Transform pos: 12.5,15.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6165 components: - type: Transform pos: 10.5,15.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6166 components: - type: Transform pos: -37.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6167 components: - type: Transform pos: -37.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6168 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6169 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6170 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6171 components: - type: Transform pos: -35.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6172 components: - type: Transform pos: -32.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6173 components: - type: Transform pos: -33.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 8373 components: - type: Transform pos: -3.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8374 components: - type: Transform pos: -2.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8375 components: - type: Transform pos: -1.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8376 components: - type: Transform pos: -0.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8585 components: - type: Transform pos: -11.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8586 components: - type: Transform pos: -9.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8587 components: - type: Transform pos: -8.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8588 components: - type: Transform pos: -10.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8589 components: - type: Transform rot: -1.5707963267948966 rad pos: -9.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8590 components: - type: Transform rot: -1.5707963267948966 rad pos: -8.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - proto: WindowClockworkDirectional entities: - uid: 6174 @@ -59915,8 +59603,6 @@ entities: rot: -1.5707963267948966 rad pos: -19.5,24.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - proto: WindowDirectional entities: - uid: 6175 @@ -59924,558 +59610,414 @@ entities: - type: Transform pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6176 components: - type: Transform rot: -1.5707963267948966 rad pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6177 components: - type: Transform rot: 1.5707963267948966 rad pos: -31.5,28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6178 components: - type: Transform rot: -1.5707963267948966 rad pos: -28.5,28.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6179 components: - type: Transform rot: -1.5707963267948966 rad pos: 7.5,-16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6180 components: - type: Transform pos: -31.5,30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6181 components: - type: Transform rot: 3.141592653589793 rad pos: -31.5,31.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6182 components: - type: Transform rot: 1.5707963267948966 rad pos: -31.5,31.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6183 components: - type: Transform rot: 1.5707963267948966 rad pos: -31.5,30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6184 components: - type: Transform rot: -1.5707963267948966 rad pos: -31.5,30.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6185 components: - type: Transform rot: -1.5707963267948966 rad pos: -31.5,31.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6186 components: - type: Transform rot: 3.141592653589793 rad pos: -30.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6187 components: - type: Transform rot: 3.141592653589793 rad pos: -29.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6188 components: - type: Transform rot: 1.5707963267948966 rad pos: -29.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6189 components: - type: Transform pos: -30.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6190 components: - type: Transform rot: -1.5707963267948966 rad pos: -30.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6191 components: - type: Transform pos: -29.5,32.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6192 components: - type: Transform rot: -1.5707963267948966 rad pos: -37.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6193 components: - type: Transform pos: -35.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6194 components: - type: Transform pos: -34.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6195 components: - type: Transform rot: -1.5707963267948966 rad pos: -35.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6196 components: - type: Transform rot: 1.5707963267948966 rad pos: -34.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6197 components: - type: Transform rot: 3.141592653589793 rad pos: -34.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6198 components: - type: Transform rot: 3.141592653589793 rad pos: -35.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6199 components: - type: Transform rot: 1.5707963267948966 rad pos: -37.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6200 components: - type: Transform rot: 1.5707963267948966 rad pos: -37.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6201 components: - type: Transform rot: -1.5707963267948966 rad pos: -37.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6202 components: - type: Transform pos: -37.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6203 components: - type: Transform rot: 3.141592653589793 rad pos: -37.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6204 components: - type: Transform rot: 3.141592653589793 rad pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6205 components: - type: Transform rot: 1.5707963267948966 rad pos: -23.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6206 components: - type: Transform rot: -1.5707963267948966 rad pos: -26.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6207 components: - type: Transform rot: -1.5707963267948966 rad pos: -26.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6208 components: - type: Transform rot: -1.5707963267948966 rad pos: -26.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6209 components: - type: Transform rot: 3.141592653589793 rad pos: -26.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6210 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,19.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6211 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6212 components: - type: Transform rot: 1.5707963267948966 rad pos: -26.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6213 components: - type: Transform pos: -26.5,17.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6214 components: - type: Transform rot: 3.141592653589793 rad pos: -35.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6215 components: - type: Transform rot: 1.5707963267948966 rad pos: -35.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6216 components: - type: Transform rot: -1.5707963267948966 rad pos: -35.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6217 components: - type: Transform rot: 1.5707963267948966 rad pos: -32.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6218 components: - type: Transform rot: 3.141592653589793 rad pos: -32.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6219 components: - type: Transform rot: 3.141592653589793 rad pos: -33.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6220 components: - type: Transform pos: -32.5,0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 8377 components: - type: Transform pos: -3.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8378 components: - type: Transform pos: -2.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8379 components: - type: Transform pos: -1.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8380 components: - type: Transform pos: -0.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8381 components: - type: Transform rot: 3.141592653589793 rad pos: -3.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8382 components: - type: Transform rot: 3.141592653589793 rad pos: -2.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8383 components: - type: Transform rot: 3.141592653589793 rad pos: -1.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8384 components: - type: Transform rot: 3.141592653589793 rad pos: -0.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8385 components: - type: Transform rot: 1.5707963267948966 rad pos: -0.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8386 components: - type: Transform rot: -1.5707963267948966 rad pos: -3.5,-7.5 parent: 8320 - - type: DeltaPressure - gridUid: 8320 - uid: 8591 components: - type: Transform pos: -11.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8592 components: - type: Transform pos: -10.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8593 components: - type: Transform pos: -9.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8594 components: - type: Transform pos: -8.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8595 components: - type: Transform rot: 3.141592653589793 rad pos: -11.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8596 components: - type: Transform rot: 3.141592653589793 rad pos: -10.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8597 components: - type: Transform rot: 3.141592653589793 rad pos: -9.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8598 components: - type: Transform rot: 3.141592653589793 rad pos: -8.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8599 components: - type: Transform rot: 1.5707963267948966 rad pos: -8.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8600 components: - type: Transform rot: -1.5707963267948966 rad pos: -11.5,2.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8601 components: - type: Transform rot: -1.5707963267948966 rad pos: -9.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8602 components: - type: Transform rot: 1.5707963267948966 rad pos: -8.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8603 components: - type: Transform pos: -8.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8604 components: - type: Transform pos: -9.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8605 components: - type: Transform rot: 3.141592653589793 rad pos: -9.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - uid: 8606 components: - type: Transform rot: 3.141592653589793 rad pos: -8.5,-1.5 parent: 8406 - - type: DeltaPressure - gridUid: 8406 - proto: WindowFrostedDirectional entities: - uid: 6221 @@ -60484,233 +60026,173 @@ entities: rot: 3.141592653589793 rad pos: -52.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7748 components: - type: Transform rot: 1.5707963267948966 rad pos: 0.5,-6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7749 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7750 components: - type: Transform rot: 1.5707963267948966 rad pos: -8.5,0.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7751 components: - type: Transform rot: -1.5707963267948966 rad pos: 2.5,10.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7752 components: - type: Transform rot: 3.141592653589793 rad pos: 3.5,11.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7753 components: - type: Transform rot: 3.141592653589793 rad pos: 4.5,11.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7754 components: - type: Transform rot: 1.5707963267948966 rad pos: 4.5,11.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7755 components: - type: Transform rot: -1.5707963267948966 rad pos: 3.5,11.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7756 components: - type: Transform rot: 3.141592653589793 rad pos: 2.5,10.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7757 components: - type: Transform pos: 2.5,10.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7758 components: - type: Transform rot: -1.5707963267948966 rad pos: -1.5,-6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7759 components: - type: Transform rot: -1.5707963267948966 rad pos: 9.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7760 components: - type: Transform rot: 3.141592653589793 rad pos: 9.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7761 components: - type: Transform pos: 9.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7762 components: - type: Transform rot: 3.141592653589793 rad pos: 5.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7763 components: - type: Transform pos: 3.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7764 components: - type: Transform rot: 3.141592653589793 rad pos: 4.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7765 components: - type: Transform pos: 4.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7766 components: - type: Transform rot: 3.141592653589793 rad pos: 3.5,5.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7767 components: - type: Transform pos: 5.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7768 components: - type: Transform rot: 1.5707963267948966 rad pos: 17.5,0.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7769 components: - type: Transform pos: -1.5,9.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7770 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,9.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7771 components: - type: Transform rot: -1.5707963267948966 rad pos: 15.5,8.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7772 components: - type: Transform pos: 15.5,8.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7773 components: - type: Transform rot: 3.141592653589793 rad pos: 15.5,8.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7774 components: - type: Transform rot: 1.5707963267948966 rad pos: 15.5,8.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7775 components: - type: Transform rot: -1.5707963267948966 rad pos: -1.5,9.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7776 components: - type: Transform rot: 3.141592653589793 rad pos: -1.5,9.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - proto: WindowReinforcedDirectional entities: - uid: 6222 @@ -60718,202 +60200,150 @@ entities: - type: Transform pos: -13.5,20.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6223 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6224 components: - type: Transform rot: 1.5707963267948966 rad pos: -11.5,21.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6225 components: - type: Transform pos: -17.5,18.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6226 components: - type: Transform pos: 12.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6227 components: - type: Transform rot: 1.5707963267948966 rad pos: -7.5,-3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6228 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6229 components: - type: Transform rot: -1.5707963267948966 rad pos: 6.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6230 components: - type: Transform rot: 1.5707963267948966 rad pos: 5.5,-13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6231 components: - type: Transform rot: 1.5707963267948966 rad pos: 4.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6232 components: - type: Transform rot: -1.5707963267948966 rad pos: 2.5,-11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6233 components: - type: Transform rot: 3.141592653589793 rad pos: -17.5,16.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6234 components: - type: Transform rot: 3.141592653589793 rad pos: 1.5,-12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6235 components: - type: Transform rot: 3.141592653589793 rad pos: 1.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6236 components: - type: Transform rot: 1.5707963267948966 rad pos: 1.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6237 components: - type: Transform pos: 1.5,-13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6238 components: - type: Transform pos: 1.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6239 components: - type: Transform pos: -9.5,-5.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6240 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6241 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6242 components: - type: Transform rot: 1.5707963267948966 rad pos: -3.5,12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6243 components: - type: Transform rot: 1.5707963267948966 rad pos: -3.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6244 components: - type: Transform rot: 3.141592653589793 rad pos: -2.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6245 components: - type: Transform rot: 1.5707963267948966 rad pos: -3.5,11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6246 components: - type: Transform pos: -2.5,11.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6247 components: - type: Transform rot: 3.141592653589793 rad pos: -10.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - type: Occluder boundingBox: -0.5,-0.5,0.5,-0.3 - uid: 6248 @@ -60922,8 +60352,6 @@ entities: rot: 3.141592653589793 rad pos: -8.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - type: Occluder boundingBox: -0.5,-0.5,0.5,-0.3 - uid: 6249 @@ -60932,8 +60360,6 @@ entities: rot: 3.141592653589793 rad pos: -9.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - type: Occluder boundingBox: -0.5,-0.5,0.5,-0.3 - uid: 6250 @@ -60942,442 +60368,330 @@ entities: rot: -1.5707963267948966 rad pos: 11.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6251 components: - type: Transform rot: 3.141592653589793 rad pos: 11.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6252 components: - type: Transform rot: 1.5707963267948966 rad pos: 11.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6253 components: - type: Transform pos: 11.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6254 components: - type: Transform pos: 7.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6255 components: - type: Transform rot: -1.5707963267948966 rad pos: 7.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6256 components: - type: Transform rot: 3.141592653589793 rad pos: 7.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6257 components: - type: Transform rot: 1.5707963267948966 rad pos: 7.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6258 components: - type: Transform rot: 1.5707963267948966 rad pos: -2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6259 components: - type: Transform pos: -2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6260 components: - type: Transform rot: -1.5707963267948966 rad pos: -2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6261 components: - type: Transform rot: 3.141592653589793 rad pos: -2.5,3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6262 components: - type: Transform rot: 3.141592653589793 rad pos: 21.5,-3.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6263 components: - type: Transform rot: 3.141592653589793 rad pos: 12.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6264 components: - type: Transform rot: 1.5707963267948966 rad pos: 12.5,7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6265 components: - type: Transform rot: 1.5707963267948966 rad pos: 12.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6266 components: - type: Transform rot: 1.5707963267948966 rad pos: 18.5,13.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6267 components: - type: Transform rot: 1.5707963267948966 rad pos: 18.5,12.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6268 components: - type: Transform rot: 1.5707963267948966 rad pos: 21.5,8.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6269 components: - type: Transform rot: 1.5707963267948966 rad pos: 21.5,9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6270 components: - type: Transform pos: 7.5,-10.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6271 components: - type: Transform rot: 3.141592653589793 rad pos: 7.5,-9.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6272 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,-0.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6273 components: - type: Transform rot: 3.141592653589793 rad pos: 8.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6274 components: - type: Transform rot: 3.141592653589793 rad pos: 9.5,2.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6275 components: - type: Transform rot: 1.5707963267948966 rad pos: 33.5,-7.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 6276 components: - type: Transform pos: 34.5,-6.5 parent: 2 - - type: DeltaPressure - gridUid: 2 - uid: 7777 components: - type: Transform rot: -1.5707963267948966 rad pos: 6.5,6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7778 components: - type: Transform rot: -1.5707963267948966 rad pos: 6.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7779 components: - type: Transform rot: 3.141592653589793 rad pos: 6.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7780 components: - type: Transform rot: 3.141592653589793 rad pos: 3.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7781 components: - type: Transform rot: 3.141592653589793 rad pos: 5.5,7.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7782 components: - type: Transform rot: 1.5707963267948966 rad pos: 16.5,4.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 7783 components: - type: Transform rot: 3.141592653589793 rad pos: -0.5,-6.5 parent: 6350 - - type: DeltaPressure - gridUid: 6350 - uid: 8093 components: - type: Transform rot: -1.5707963267948966 rad pos: 3.5,7.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8094 components: - type: Transform rot: -1.5707963267948966 rad pos: 2.5,10.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8095 components: - type: Transform rot: -1.5707963267948966 rad pos: -1.5,10.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8096 components: - type: Transform rot: -1.5707963267948966 rad pos: -2.5,7.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8097 components: - type: Transform rot: -1.5707963267948966 rad pos: -3.5,4.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8098 components: - type: Transform rot: 1.5707963267948966 rad pos: -3.5,4.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8099 components: - type: Transform rot: 1.5707963267948966 rad pos: -2.5,7.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8100 components: - type: Transform rot: 1.5707963267948966 rad pos: 4.5,4.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8101 components: - type: Transform rot: -1.5707963267948966 rad pos: 4.5,4.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8102 components: - type: Transform rot: 1.5707963267948966 rad pos: 3.5,7.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8103 components: - type: Transform rot: 1.5707963267948966 rad pos: 2.5,10.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8104 components: - type: Transform rot: 1.5707963267948966 rad pos: 1.5,13.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8105 components: - type: Transform rot: 1.5707963267948966 rad pos: 1.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8106 components: - type: Transform pos: 0.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8107 components: - type: Transform rot: -1.5707963267948966 rad pos: -0.5,13.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8108 components: - type: Transform rot: -1.5707963267948966 rad pos: -0.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8109 components: - type: Transform rot: 3.141592653589793 rad pos: -0.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8110 components: - type: Transform rot: 3.141592653589793 rad pos: 0.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8111 components: - type: Transform rot: 3.141592653589793 rad pos: 1.5,14.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8112 components: - type: Transform rot: -1.5707963267948966 rad pos: 1.5,13.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8113 components: - type: Transform rot: 1.5707963267948966 rad pos: -0.5,13.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - uid: 8114 components: - type: Transform rot: 1.5707963267948966 rad pos: -1.5,10.5 parent: 7784 - - type: DeltaPressure - gridUid: 7784 - proto: Wirecutter entities: - uid: 6277 From e0e852e17d04cad1012a009d7ec595d5432c975e Mon Sep 17 00:00:00 2001 From: piskaczek <192605039+piskaczek@users.noreply.github.com> Date: Sat, 18 Apr 2026 12:42:33 +0200 Subject: [PATCH 201/247] Lower amount of wirebrushes (#43629) waaaaaaah --- Resources/Prototypes/Catalog/Fills/Crates/service.yml | 1 - Resources/Prototypes/Catalog/Fills/Lockers/service.yml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Resources/Prototypes/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/Catalog/Fills/Crates/service.yml index 9d53c1e2b4..1f1432e709 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/service.yml @@ -24,7 +24,6 @@ amount: 2 - id: BoxCleanerGrenades - id: WireBrush - amount: 2 - type: entity parent: CratePlastic diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml index 31f4d1d12c..6228c1a229 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml @@ -92,6 +92,7 @@ - id: LightReplacer - id: BoxLightMixed - id: Holoprojector + - id: WireBrush - !type:AllSelector rolls: 2 children: @@ -101,7 +102,6 @@ - id: SoapNT - id: FlashlightLantern - id: Plunger - - id: WireBrush - id: PlushieLizardJobJanitor prob: 0.02 From eb2e73f74701f8aee8366d38cef2895cba2085ac Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 18 Apr 2026 10:57:52 +0000 Subject: [PATCH 202/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index cab43cbef1..fd7d037379 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4034,3 +4027,10 @@ 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 From 4e4f75cbd3c2e9267eb9d3c2cb3bcf6eadd1a366 Mon Sep 17 00:00:00 2001 From: exo4ka <193421410+exo4ka@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:04:18 +0300 Subject: [PATCH 203/247] =?UTF-8?q?Awesome=20update=2018.04.2026=20?= =?UTF-8?q?=E2=84=961=20(#3572)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Resources/Maps/Corvax/corvax_awesome.yml | 13986 +++++++++++---------- 1 file changed, 7377 insertions(+), 6609 deletions(-) diff --git a/Resources/Maps/Corvax/corvax_awesome.yml b/Resources/Maps/Corvax/corvax_awesome.yml index db6bcb024f..4129560b78 100644 --- a/Resources/Maps/Corvax/corvax_awesome.yml +++ b/Resources/Maps/Corvax/corvax_awesome.yml @@ -1,15 +1,16 @@ meta: format: 7 category: Map - engineVersion: 266.0.0 + engineVersion: 270.1.0 forkId: "" forkVersion: "" - time: 09/13/2025 05:42:01 - entityCount: 19355 + time: 04/18/2026 11:25:32 + entityCount: 19544 maps: - 1 grids: - 2 +- 14229 - 16832 - 16838 - 16911 @@ -20,6 +21,7 @@ tilemap: 0: Space 1: FloorArcadeBlue 10: FloorAsteroidSandUnvariantized + 8: FloorAstroGrass 14: FloorAstroSnow 15: FloorBar 17: FloorBlue @@ -27,6 +29,7 @@ tilemap: 23: FloorCarpetClown 28: FloorClown 29: FloorConcrete + 6: FloorDark 36: FloorDarkDiagonal 38: FloorDarkHerringbone 39: FloorDarkMini @@ -48,6 +51,8 @@ tilemap: 70: FloorKitchen 76: FloorMime 78: FloorMiningDark + 3: FloorOldConcrete + 2: FloorOldConcreteMono 91: FloorRGlass 93: FloorReinforced 94: FloorReinforcedHardened @@ -83,9 +88,12 @@ tilemap: 133: FloorWhitePavementVertical 134: FloorWhitePlastic 135: FloorWood + 7: FloorWoodChess + 5: FloorWoodChessDark 140: FloorWoodChessLight 141: FloorWoodChessRed 143: FloorWoodLarge + 4: FloorWoodLargeDark 146: FloorWoodLargeLight 147: FloorWoodLargeRed 148: FloorWoodLight @@ -131,11 +139,11 @@ entities: version: 7 -1,-1: ind: -1,-1 - tiles: dwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHQAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAGsAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAABrAAAAAAMAawAAAAACAGsAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAGsAAAAAAgCdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAHgAAAAAAABrAAAAAAMAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAACAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAAAzAAAAAAAAeAAAAAAAADMAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACUAAAAAAEAlAAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAGAAAAAAAACdAAAAAAAAnQAAAAAAAJQAAAAAAgCGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAACUAAAAAAEAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAHgAAAAAAABrAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAlAAAAAADAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAIwAAAAAAACNAAAAAAAAjAAAAAAAAI0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAAAzAAAAAAAAeAAAAAAAADMAAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACNAAAAAAIAjAAAAAABAI0AAAAAAwCMAAAAAAMAgQAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIEAAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAjAAAAAACAI0AAAAAAQCMAAAAAAIAjQAAAAACAIUAAAAAAAASAAAAAAAAPwAAAAAAABIAAAAAAACFAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACBAAAAAAAAPwAAAAAAABIAAAAAAAA/AAAAAAAAgQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAA== + tiles: dwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAHQAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAFsAAAAAAABbAAAAAAAAWwAAAAAAAFsAAAAAAABbAAAAAAAAawAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAGsAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAABrAAAAAAMAawAAAAACAGsAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAGsAAAAAAgCdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAHgAAAAAAABrAAAAAAMAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAACAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAAAzAAAAAAAAeAAAAAAAADMAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACUAAAAAAEAlAAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAGAAAAAAAACdAAAAAAAAnQAAAAAAAJQAAAAAAgCGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAABgAAAAAAAAYAAAAAAAAGAAAAAAAACUAAAAAAEAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAHgAAAAAAABrAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAlAAAAAADAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAIwAAAAAAACNAAAAAAAAjAAAAAAAAI0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAAAzAAAAAAAAeAAAAAAAADMAAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACNAAAAAAIAjAAAAAABAI0AAAAAAwCMAAAAAAMAgQAAAAAAAIQAAAAAAACEAAAAAAAAhAAAAAAAAIEAAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAjAAAAAACAI0AAAAAAQCMAAAAAAIAjQAAAAACAIUAAAAAAAASAAAAAAAAPwAAAAAAABIAAAAAAACFAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACBAAAAAAAAPwAAAAAAABIAAAAAAAA/AAAAAAAAgQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAA== version: 7 -1,-2: ind: -1,-2 - tiles: XgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAnQAAAAAAAF4AAAAAAABBAAAAAAAAXgAAAAAAAEEAAAAAAABeAAAAAAAAQQAAAAAAAF4AAAAAAACdAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAMAnQAAAAAAAGsAAAAAAABrAAAAAAIAawAAAAAAAJ0AAAAAAABeAAAAAAAAQQAAAAAAAF4AAAAAAABBAAAAAAAAXgAAAAAAAEEAAAAAAABeAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAACAGsAAAAAAQBrAAAAAAMAawAAAAACAGsAAAAAAgCdAAAAAAAAXgAAAAAAAEEAAAAAAABeAAAAAAAAQQAAAAAAAF4AAAAAAABBAAAAAAAAXgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAEEAAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAawAAAAABAJ0AAAAAAABrAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAABBAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAGsAAAAAAQBrAAAAAAMAawAAAAAAAJ0AAAAAAABkAAAAAAEAawAAAAABAGQAAAAAAwCdAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAAAAAQQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAABrAAAAAAMAnQAAAAAAAGsAAAAAAQCdAAAAAAAAZAAAAAACAGQAAAAAAgBkAAAAAAEAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAEEAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAADAA== + tiles: nQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAgAAAAAAAAMAAAAAAAACAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAIAAAAAAAADAAAAAAAAAgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAACAAAAAAAAAwAAAAAAAAIAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAAJAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAACwAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAABwAAAAAAAAcAAAAAAACdAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAMAJAAAAAAAACQAAAAAAAAkAAAAAAAALAAAAAAAACwAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAALAAAAAAAAAcAAAAAAAAHAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAACACwAAAAAAAAkAAAAAAAALAAAAAAAACwAAAAAAACdAAAAAAAALAAAAAAAACcAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAHAAAAAAAABwAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAABAJ0AAAAAAABrAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAB3AAAAAAAAdAAAAAAAAGsAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAAnQAAAAAAAGsAAAAAAQBrAAAAAAMAawAAAAAAACQAAAAAAAAsAAAAAAAAJAAAAAAAACwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAJ0AAAAAAABrAAAAAAMAnQAAAAAAAGsAAAAAAQAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAHcAAAAAAAB0AAAAAAAAawAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAAAADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAADAA== version: 7 1,-1: ind: 1,-1 @@ -143,11 +151,11 @@ entities: version: 7 0,-2: ind: 0,-2 - tiles: XgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAACAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAQBrAAAAAAAAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAADAGsAAAAAAgCdAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAAAAGsAAAAAAABrAAAAAAIAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAmQAAAAABAJoAAAAAAgBrAAAAAAEAawAAAAADAJ0AAAAAAABrAAAAAAAAawAAAAADAJ0AAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAAAawAAAAABAJ0AAAAAAABrAAAAAAAAmgAAAAABAJoAAAAAAQCaAAAAAAMAawAAAAADAGsAAAAAAwCdAAAAAAAAawAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAQCdAAAAAAAAawAAAAAAAJ0AAAAAAACaAAAAAAAAmQAAAAADAGsAAAAAAgBrAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAEAnQAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAnQAAAAAAAGsAAAAAAQCdAAAAAAAAmgAAAAAAAJkAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAHoAAAAAAABrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAawAAAAABADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAADADMAAAAAAAAzAAAAAAAAMwAAAAAAAA== + tiles: nQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAABgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAAYAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAAGAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAACAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAQBrAAAAAAAAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAADAGsAAAAAAgCdAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAAAAGsAAAAAAABrAAAAAAIAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAmQAAAAABAJoAAAAAAgBrAAAAAAEAawAAAAADAJ0AAAAAAABrAAAAAAAAawAAAAADAJ0AAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAAAawAAAAABAJ0AAAAAAABrAAAAAAAAmgAAAAABAJoAAAAAAQCaAAAAAAMAawAAAAADAGsAAAAAAwCdAAAAAAAAawAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAQCdAAAAAAAAawAAAAAAAJ0AAAAAAACaAAAAAAAAmQAAAAADAGsAAAAAAgBrAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAEAnQAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAnQAAAAAAAGsAAAAAAQCdAAAAAAAAmgAAAAAAAJkAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAHoAAAAAAABrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAawAAAAABADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAADADMAAAAAAAAzAAAAAAAAMwAAAAAAAA== version: 7 1,-2: ind: 1,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAgB4AAAAAAAAnQAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAnQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAAEEAAAAAAABBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAADAHgAAAAAAACdAAAAAAAAnQAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAACdAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAnQAAAAAAAEIAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAACoAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAnAAAAAAAAKgAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAJ0AAAAAAAB6AAAAAAAAegAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAABrAAAAAAEAawAAAAADAGsAAAAAAQCdAAAAAAAAegAAAAAAAHoAAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAJwAAAAAAACwAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAB6AAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAACsAAAAAAAAsAAAAAAAAKwAAAAAAACwAAAAAAABOAAAAAAAATgAAAAAAAJoAAAAAAgCaAAAAAAIAmQAAAAACAJ0AAAAAAAB6AAAAAAAAoAAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACaAAAAAAAAmgAAAAAAAJoAAAAAAwCdAAAAAAAAegAAAAAAAJ0AAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAeQAAAAAAAJ0AAAAAAACdAAAAAAAAmQAAAAACAJoAAAAAAQCaAAAAAAAAnQAAAAAAAHoAAAAAAACgAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJkAAAAAAQCaAAAAAAEAmgAAAAAAAJ0AAAAAAACdAAAAAAAAoAAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAbAAAAAAAAGwAAAAAAABsAAAAAAAAbAAAAAAAAJ0AAAAAAAB5AAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAEAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAGwAAAAAAABsAAAAAAAAbAAAAAAAAGwAAAAAAACdAAAAAAAAeQAAAAAAAA== + tiles: awAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAdAAAAAAAAHgAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAQgAAAAAAAGsAAAAAAAAGAAAAAAAABgAAAAAAAAYAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAAB4AAAAAAAAnQAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAnQAAAAAAAEIAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAAB0AAAAAAAAeAAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAAEEAAAAAAABBAAAAAAAAawAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAADAHgAAAAAAACdAAAAAAAAnQAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAACdAAAAAAAAQgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAAnQAAAAAAAEIAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAkAAAAAAAAJAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAACoAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAnAAAAAAAAKgAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAJ0AAAAAAAB6AAAAAAAAegAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAABrAAAAAAEAawAAAAADAGsAAAAAAQCdAAAAAAAAegAAAAAAAHoAAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAJwAAAAAAACwAAAAAAAAnAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAB6AAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAACsAAAAAAAAsAAAAAAAAKwAAAAAAACwAAAAAAABOAAAAAAAATgAAAAAAAJoAAAAAAgCaAAAAAAIAmQAAAAACAJ0AAAAAAAB6AAAAAAAAoAAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACaAAAAAAAAmgAAAAAAAJoAAAAAAwCdAAAAAAAAegAAAAAAAJ0AAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAeQAAAAAAAJ0AAAAAAACdAAAAAAAAmQAAAAACAJoAAAAAAQCaAAAAAAAAnQAAAAAAAHoAAAAAAACgAAAAAAAAnQAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJkAAAAAAQCaAAAAAAEAmgAAAAAAAJ0AAAAAAACdAAAAAAAAoAAAAAAAAJ0AAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAbAAAAAAAAGwAAAAAAABsAAAAAAAAbAAAAAAAAJ0AAAAAAAB5AAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAEAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAGwAAAAAAABsAAAAAAAAbAAAAAAAAGwAAAAAAACdAAAAAAAAeQAAAAAAAA== version: 7 -2,0: ind: -2,0 @@ -155,23 +163,23 @@ entities: version: 7 -2,-1: ind: -2,-1 - tiles: nQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAhgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACGAAAAAAAAWwAAAAAAAGsAAAAAAwCdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAABrAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAA5AAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAB7AAAAAAIAnQAAAAAAAHkAAAAAAAB6AAAAAAAAegAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAB7AAAAAAMAOgAAAAADAJ0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHAAAAAAAAB5AAAAAAAAnQAAAAAAAHQAAAAAAQCdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAZQAAAAAAAGUAAAAAAABlAAAAAAAAZQAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAnQAAAAAAAGUAAAAAAACGAAAAAAAAhgAAAAAAAGUAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABlAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAADAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAB5AAAAAAAAnQAAAAAAAGUAAAAAAABlAAAAAAAAZQAAAAAAAGUAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAA== + tiles: nQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAhgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACGAAAAAAAAWwAAAAAAAGsAAAAAAwCdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAABrAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAA5AAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAB7AAAAAAIAnQAAAAAAAHkAAAAAAAB6AAAAAAAAegAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAB7AAAAAAMAOgAAAAADAJ0AAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHAAAAAAAAB5AAAAAAAAnQAAAAAAAHQAAAAAAQCdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAZQAAAAAAAGUAAAAAAABlAAAAAAAAZQAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAnQAAAAAAAGUAAAAAAACGAAAAAAAAhgAAAAAAAGUAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABlAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAADAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACGAAAAAAAAhgAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAB5AAAAAAAAnQAAAAAAAGUAAAAAAABlAAAAAAAAZQAAAAAAAGUAAAAAAACdAAAAAAAAhgAAAAAAAIYAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAIYAAAAAAACGAAAAAAAAnQAAAAAAAA== version: 7 -2,-2: ind: -2,-2 - tiles: nQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAIAawAAAAADAGsAAAAAAQCdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAACAGsAAAAAAwBrAAAAAAAAawAAAAADAGsAAAAAAQBrAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAADAJ0AAAAAAABvAAAAAAMAawAAAAADAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAMAnQAAAAAAAGsAAAAAAQCdAAAAAAAAbwAAAAADAGsAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAADAGsAAAAAAABrAAAAAAAAnQAAAAAAAG8AAAAAAwBrAAAAAAAAawAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAACAJ0AAAAAAABvAAAAAAMAawAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAawAAAAAAAGsAAAAAAwCdAAAAAAAAbwAAAAADAGsAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAADAJ0AAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAMAawAAAAADAF0AAAAAAABdAAAAAAAAXQAAAAAAAGsAAAAAAQBrAAAAAAIAawAAAAADAGsAAAAAAgBrAAAAAAEAawAAAAAAAGsAAAAAAgBrAAAAAAAAawAAAAABAGsAAAAAAABrAAAAAAEAawAAAAADAGsAAAAAAQBdAAAAAAAAXQAAAAAAAF0AAAAAAABrAAAAAAAAnQAAAAAAAGsAAAAAAABkAAAAAAIAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAawAAAAAAAGsAAAAAAgBrAAAAAAIAZAAAAAADAGsAAAAAAQCcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAawAAAAAAAGsAAAAAAQCdAAAAAAAAawAAAAADAGQAAAAAAABrAAAAAAMAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAMAnQAAAAAAAGQAAAAAAABkAAAAAAIAZAAAAAACAJwAAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAZAAAAAACAGsAAAAAAwCcAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAgBrAAAAAAMAnAAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAGsAAAAAAACdAAAAAAAAawAAAAAAAA== + tiles: nQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAJ0AAAAAAAADAAAAAAAAnQAAAAAAAAMAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAkAAAAAAAAJwAAAAAAAIwAAAAAAACdAAAAAAAAAwAAAAAAAJ0AAAAAAAADAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAAnQAAAAAAACwAAAAAAACdAAAAAAAAJAAAAAAAACcAAAAAAACMAAAAAAAAnQAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAAFsAAAAAAAAsAAAAAAAAnQAAAAAAACQAAAAAAAAnAAAAAAAAjAAAAAAAAJ0AAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAACwAAAAAAABbAAAAAAAALAAAAAAAAJ0AAAAAAAAkAAAAAAAAJwAAAAAAAIwAAAAAAACdAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAAWwAAAAAAACwAAAAAAACdAAAAAAAAJAAAAAAAACcAAAAAAACMAAAAAAAAnQAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAAJ0AAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAADAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAACwAAAAAAABrAAAAAAIALAAAAAAAACwAAAAAAAAsAAAAAAAAawAAAAAAAGsAAAAAAgBrAAAAAAAAawAAAAABAGsAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAAAsAAAAAAAAnQAAAAAAACwAAAAAAAAkAAAAAAAAJAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAALAAAAAAAAGsAAAAAAgAsAAAAAAAALAAAAAAAACwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAALAAAAAAAACwAAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAJwAAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAAJAAAAAAAACwAAAAAAACcAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAnAAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAAA== version: 7 -2,-3: ind: -2,-3 - tiles: AAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAB5AAAAAAAAeQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAACAGsAAAAAAgBrAAAAAAEAawAAAAADAGsAAAAAAQB5AAAAAAAAeQAAAAAAAHkAAAAAAAB5AAAAAAAAnQAAAAAAAJwAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAQBrAAAAAAAAawAAAAAAAGsAAAAAAgBrAAAAAAIAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAMAawAAAAADAGsAAAAAAQBrAAAAAAMAawAAAAACAGsAAAAAAQBrAAAAAAIAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAADAGsAAAAAAwBrAAAAAAEAawAAAAABAGsAAAAAAABrAAAAAAMAawAAAAABAJ0AAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAgBrAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAMAawAAAAAAAGsAAAAAAwCdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAawAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAACAGsAAAAAAQBrAAAAAAIAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAABAGsAAAAAAQBrAAAAAAIAawAAAAAAAGsAAAAAAQBrAAAAAAMAawAAAAAAAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAwBrAAAAAAAAawAAAAACAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAawAAAAADAGsAAAAAAABrAAAAAAEAawAAAAABAGsAAAAAAABrAAAAAAEAnQAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAA== + tiles: AAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAAJ0AAAAAAAB5AAAAAAAAeQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAB5AAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAAHkAAAAAAAB5AAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAsAAAAAAAALAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAJAAAAAAAACQAAAAAAAAkAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACQAAAAAAAAkAAAAAAAAJAAAAAAAACwAAAAAAACUAAAAAAAAlAAAAAAAAJQAAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAkAAAAAAAAJAAAAAAAACQAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAACwAAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAAAsAAAAAAAALAAAAAAAACwAAAAAAAAsAAAAAAAAeQAAAAAAAHkAAAAAAACdAAAAAAAAnQAAAAAAAAQAAAAAAAAFAAAAAAAABAAAAAAAAA== version: 7 -1,-3: ind: -1,-3 - tiles: AAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAXQAAAAAAAF0AAAAAAABdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABeAAAAAAAAXgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXgAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAF4AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== version: 7 0,-3: ind: 0,-3 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAAAAF4AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAA== version: 7 -3,-2: ind: -3,-2 @@ -227,7 +235,7 @@ entities: version: 7 1,-3: ind: 1,-3 - tiles: AAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAegAAAAAAAHoAAAAAAAB6AAAAAAAAnQAAAAAAAHoAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJ0AAAAAAAB6AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAegAAAAAAAHoAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABCAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAQgAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAHoAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAA+AAAAAAIAPgAAAAACAD4AAAAAAwA+AAAAAAEAnQAAAAAAAJ0AAAAAAABCAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAPgAAAAADAD4AAAAAAAA+AAAAAAIAPgAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAnQAAAAAAAD4AAAAAAgA+AAAAAAAAPgAAAAAAAD4AAAAAAgCdAAAAAAAAnQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAwA+AAAAAAMAPgAAAAADAD4AAAAAAQA+AAAAAAAAnQAAAAAAAJ0AAAAAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAACdAAAAAAAAPgAAAAADAD4AAAAAAwA+AAAAAAIAPgAAAAACAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== + tiles: AAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAegAAAAAAAHoAAAAAAAB6AAAAAAAAnQAAAAAAAHoAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJ0AAAAAAAB6AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAegAAAAAAAHoAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABCAAAAAAAAAAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAQgAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAAAAAAAAAAAAnQAAAAAAAHoAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAA+AAAAAAIAPgAAAAACAD4AAAAAAwA+AAAAAAEAnQAAAAAAAJ0AAAAAAABCAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAPgAAAAADAD4AAAAAAAA+AAAAAAIAPgAAAAABAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAnQAAAAAAAD4AAAAAAgA+AAAAAAAAPgAAAAAAAD4AAAAAAgCdAAAAAAAAnQAAAAAAAEIAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAwA+AAAAAAMAPgAAAAADAD4AAAAAAQA+AAAAAAAAnQAAAAAAAJ0AAAAAAABCAAAAAAAAawAAAAAAAAYAAAAAAAAGAAAAAAAABgAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAHgAAAAAAACdAAAAAAAAPgAAAAADAD4AAAAAAwA+AAAAAAIAPgAAAAACAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== version: 7 2,-3: ind: 2,-3 @@ -271,7 +279,7 @@ entities: version: 7 -4,1: ind: -4,1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAADAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAgB4AAAAAAAAawAAAAADAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAeAAAAAAAAGsAAAAAAgCdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAgAzAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAzAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAADAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAAzAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAawAAAAABADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAwBrAAAAAAMAMwAAAAAAADMAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABxAAAAAAAAnQAAAAAAAGsAAAAAAQCdAAAAAAAAnQAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAADAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAAB4AAAAAAAAawAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABrAAAAAAAAeAAAAAAAAGsAAAAAAACdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAawAAAAAAAHgAAAAAAABrAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnQAAAAAAAGsAAAAAAgB4AAAAAAAAawAAAAADAJ0AAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAEAeAAAAAAAAGsAAAAAAgCdAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAMwAAAAAAAHgAAAAAAAAzAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAMwAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAgAzAAAAAAAAnQAAAAAAADMAAAAAAAB4AAAAAAAAMwAAAAAAAJ0AAAAAAAAzAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAADAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdAAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAB3AAAAAAAAdwAAAAAAAHcAAAAAAAAzAAAAAAAAawAAAAACAGsAAAAAAABrAAAAAAAAawAAAAABADMAAAAAAAAzAAAAAAAAMwAAAAAAAGsAAAAAAAAzAAAAAAAAMwAAAAAAADMAAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAwBrAAAAAAMAMwAAAAAAADMAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABxAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAA== version: 7 -4,0: ind: -4,0 @@ -279,7 +287,7 @@ entities: version: 7 -4,2: ind: -4,2 - tiles: MwAAAAAAADMAAAAAAAB9AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAGsAAAAAAABrAAAAAAAAawAAAAABAGsAAAAAAACdAAAAAAAAnQAAAAAAADMAAAAAAAAzAAAAAAAAfQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAABrAAAAAAAAawAAAAAAAGsAAAAAAABrAAAAAAEAnQAAAAAAAJ0AAAAAAABiAAAAAAAAMwAAAAAAAH0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACdAAAAAAAAawAAAAAAAGsAAAAAAwBrAAAAAAMAawAAAAAAAJ0AAAAAAACdAAAAAAAAYgAAAAAAADMAAAAAAAB9AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnQAAAAAAAGsAAAAAAgBrAAAAAAIAawAAAAAAAGsAAAAAAACdAAAAAAAAnQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAABiAAAAAAAAYgAAAAAAAH0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAA== + tiles: MwAAAAAAADMAAAAAAAB9AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAABeAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAADMAAAAAAAAzAAAAAAAAfQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAXgAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAABiAAAAAAAAMwAAAAAAAH0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAABeAAAAAAAAQQAAAAAAAF4AAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAYgAAAAAAADMAAAAAAAB9AAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAXgAAAAAAAEEAAAAAAABeAAAAAAAAnQAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAnQAAAAAAAF4AAAAAAABBAAAAAAAAXgAAAAAAAJ0AAAAAAAAAAAAAAAAAnQAAAAAAAJ0AAAAAAABiAAAAAAAAYgAAAAAAAH0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAGsAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnQAAAAAAAJ0AAAAAAACdAAAAAAAAnQAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJ0AAAAAAACdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAACdAAAAAAAAnQAAAAAAAA== version: 7 -2,4: ind: -2,4 @@ -339,11 +347,7 @@ entities: version: 7 -2,-4: ind: -2,-4 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAA== - version: 7 - -1,-4: - ind: -1,-4 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAJwAAAAAAACcAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAnAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAJwAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 2,2: ind: 2,2 @@ -375,18 +379,11 @@ entities: id: Basalt7 decals: 7878: 52.71875,-6.109375 - - node: - color: '#FFFFFFFF' - id: Bot - decals: - 3152: -19,-43 - 3154: -21,-43 - 3156: -20,-45 - node: color: '#FFFFFFFF' id: Box decals: - 3155: -20,-45 + 11263: -15,-41 - node: color: '#16212EFF' id: BrickBoxOverlay @@ -539,9 +536,6 @@ entities: color: '#FF9821FF' id: BrickCornerOverlayNE decals: - 9277: -22,-35 - 9288: -24,-41 - 9353: -21,-20 9508: -34,-22 9509: -36,-21 - node: @@ -585,11 +579,6 @@ entities: decals: 9014: -19,-14 11169: -19,2 - - node: - color: '#FF9821FF' - id: BrickCornerOverlayNW - decals: - 9297: -26,-37 - node: color: '#334E6DFF' id: BrickCornerOverlaySE @@ -625,12 +614,6 @@ entities: decals: 8713: 29,-2 8730: 34,4 - - node: - color: '#FF9821FF' - id: BrickCornerOverlaySE - decals: - 9361: -21,-24 - 9381: -17,-16 - node: color: '#639137FF' id: BrickCornerOverlaySW @@ -655,12 +638,6 @@ entities: id: BrickCornerOverlaySW decals: 10934: 30,4 - - node: - color: '#FF9821FF' - id: BrickCornerOverlaySW - decals: - 9340: -27,-34 - 9354: -27,-24 - node: color: '#334E6DFF' id: BrickEndOverlayN @@ -820,7 +797,6 @@ entities: color: '#AE6716FF' id: BrickLineOverlayE decals: - 4096: -17,-17 4256: -1,-18 - node: color: '#BC863FFF' @@ -867,48 +843,7 @@ entities: color: '#FF9821FF' id: BrickLineOverlayE decals: - 9269: -22,-36 - 9270: -22,-37 - 9271: -22,-38 - 9272: -22,-39 - 9282: -25,-36 - 9283: -25,-37 - 9284: -25,-38 - 9285: -25,-39 - 9286: -25,-40 9287: -24,-42 - 9313: -25,-26 - 9314: -25,-25 - 9315: -25,-27 - 9316: -25,-28 - 9317: -25,-29 - 9318: -27,-27 - 9319: -27,-28 - 9320: -27,-29 - 9321: -27,-29 - 9322: -27,-30 - 9323: -27,-31 - 9331: -27,-29 - 9332: -27,-30 - 9333: -27,-31 - 9334: -27,-32 - 9342: -25,-34 - 9343: -25,-33 - 9344: -25,-32 - 9345: -25,-31 - 9346: -25,-30 - 9347: -25,-29 - 9358: -21,-21 - 9359: -21,-22 - 9360: -21,-23 - 9373: -17,-17 - 9374: -17,-18 - 9375: -17,-19 - 9376: -17,-21 - 9377: -17,-22 - 9378: -17,-23 - 9379: -17,-24 - 9380: -17,-15 - node: color: '#334E6DFF' id: BrickLineOverlayN @@ -1045,7 +980,6 @@ entities: 8985: -15,-9 8986: -14,-9 8989: -17,-4 - 9010: -17,-14 9011: -18,-14 9012: -18,-14 9021: -18,2 @@ -1061,13 +995,6 @@ entities: color: '#FF9821FF' id: BrickLineOverlayN decals: - 9278: -23,-35 - 9279: -24,-35 - 9339: -26,-33 - 9349: -26,-26 - 9350: -23,-20 - 9351: -24,-20 - 9352: -22,-20 9364: -20,-22 9365: -20,-24 9488: -33,-23 @@ -1209,16 +1136,8 @@ entities: color: '#FF9821FF' id: BrickLineOverlayS decals: - 9280: -24,-35 - 9281: -23,-35 - 9341: -26,-34 - 9348: -26,-26 - 9356: -26,-24 - 9357: -25,-24 9362: -20,-24 9363: -20,-22 - 9382: -18,-16 - 9383: -19,-16 9494: -33,-23 9495: -32,-23 9496: -30,-23 @@ -1317,7 +1236,6 @@ entities: color: '#AE6716FF' id: BrickLineOverlayW decals: - 4093: -19,-17 4257: -1,-18 - node: color: '#BC863FFF' @@ -1368,45 +1286,8 @@ entities: color: '#FF9821FF' id: BrickLineOverlayW decals: - 9273: -22,-36 - 9274: -22,-37 - 9275: -22,-38 - 9276: -22,-39 - 9289: -26,-41 9290: -26,-42 9291: -26,-42 - 9292: -26,-41 - 9293: -26,-40 - 9294: -26,-39 - 9295: -26,-39 - 9296: -26,-38 - 9298: -25,-36 - 9299: -25,-35 - 9300: -27,-33 - 9301: -27,-32 - 9302: -27,-31 - 9303: -27,-30 - 9304: -27,-29 - 9305: -27,-28 - 9306: -27,-27 - 9307: -27,-26 - 9308: -27,-25 - 9309: -25,-27 - 9310: -25,-28 - 9311: -25,-29 - 9312: -25,-25 - 9335: -25,-29 - 9336: -25,-30 - 9337: -25,-31 - 9338: -25,-32 - 9355: -27,-23 - 9366: -19,-21 - 9367: -19,-22 - 9368: -19,-23 - 9369: -19,-24 - 9370: -19,-19 - 9371: -19,-18 - 9372: -19,-17 - node: color: '#FFFFFFFF' id: BrickTileDarkBox @@ -1525,9 +1406,6 @@ entities: color: '#FFFFFFFF' id: BrickTileSteelBox decals: - 9207: -21,-31 - 9208: -21,-33 - 9268: -21,-35 9839: 10,-8 9840: 10,-7 9841: 8,-7 @@ -1550,10 +1428,6 @@ entities: 8538: -4,28 8694: 34,7 9147: -21,-19 - 9148: -21,-20 - 9205: -21,-26 - 9230: -24,-41 - 9246: -22,-35 9412: -36,-21 9413: -34,-22 9515: 15,7 @@ -1564,7 +1438,6 @@ entities: 9543: -10,3 9544: -10,-3 9545: -10,-9 - 9546: -4,-17 9547: 9,-17 9548: 15,-17 9568: -10,8 @@ -1591,7 +1464,6 @@ entities: 10470: -4,31 10547: -30,39 10550: -30,44 - 10688: -51,34 11196: 3,17 - node: color: '#FFFFFFFF' @@ -1599,8 +1471,6 @@ entities: decals: 8542: -2,28 8543: -2,20 - 9204: -22,-26 - 9237: -26,-37 9517: -8,3 9518: -8,-3 9519: -8,-9 @@ -1608,7 +1478,6 @@ entities: 9542: -4,7 9549: 13,-17 9550: 7,-17 - 9551: -6,-17 9555: 21,-11 9558: 21,3 9559: 21,-3 @@ -1637,6 +1506,7 @@ entities: 10932: 30,7 10935: 29,4 11190: 0,17 + 11424: -9,-17 - node: color: '#FFFFFFFF' id: BrickTileSteelCornerSe @@ -1645,10 +1515,6 @@ entities: 8539: -4,26 8656: 29,-2 8698: 34,4 - 9126: -17,-16 - 9187: -21,-24 - 9191: -21,-28 - 9194: -22,-31 9512: 19,-5 9514: 19,1 9520: -4,-15 @@ -1691,8 +1557,6 @@ entities: decals: 8540: -2,18 8541: -2,26 - 9184: -27,-24 - 9220: -27,-34 9524: 21,-5 9525: 21,-13 9526: 21,1 @@ -1729,18 +1593,22 @@ entities: 10472: -2,33 10931: 30,4 11191: 0,15 + 11430: -8,-15 - node: color: '#FFFFFFFF' id: BrickTileSteelEndE decals: 8535: -4,12 8536: -4,14 + 11426: -11,-19 + 11429: -11,-17 - node: color: '#FFFFFFFF' id: BrickTileSteelEndN decals: 9210: -26,-30 10929: 38,-5 + 11420: -4,-17 - node: color: '#FFFFFFFF' id: BrickTileSteelEndS @@ -1763,9 +1631,6 @@ entities: color: '#FFFFFFFF' id: BrickTileSteelInnerNe decals: - 9174: -27,-26 - 9236: -25,-41 - 9266: -25,-35 9415: -36,-22 9416: -34,-23 9757: 8,-3 @@ -1774,9 +1639,6 @@ entities: id: BrickTileSteelInnerNw decals: 8663: 29,-2 - 9175: -25,-26 - 9199: -22,-31 - 9238: -25,-37 9765: 4,-2 10179: 38,-6 10183: 39,-5 @@ -1786,10 +1648,6 @@ entities: decals: 8583: 30,3 8584: 34,3 - 9128: -17,-14 - 9176: -27,-26 - 9206: -22,-28 - 9267: -25,-35 9689: 2,-3 9706: 8,-3 10363: -10,9 @@ -1800,21 +1658,15 @@ entities: color: '#FFFFFFFF' id: BrickTileSteelInnerSw decals: - 9177: -25,-26 - 9239: -25,-34 - 9245: -22,-35 9707: 8,-3 9756: 12,-3 10181: 38,-6 10416: 21,9 - 10690: -53,34 11199: 2,15 - node: color: '#FFFFFFFF' id: BrickTileSteelLineE decals: - 8084: -19,-17 - 8085: -17,-17 8546: -4,27 8547: -4,19 8548: 30,-2 @@ -1835,51 +1687,8 @@ entities: 8674: 29,3 8695: 34,6 8696: 34,5 - 9127: -17,-15 - 9136: -17,-17 - 9137: -17,-18 - 9138: -17,-19 - 9139: -17,-21 - 9140: -17,-22 - 9141: -17,-23 - 9142: -17,-24 - 9149: -21,-21 - 9150: -21,-22 - 9151: -21,-23 - 9164: -27,-27 - 9165: -27,-28 - 9166: -27,-29 - 9167: -25,-25 - 9168: -25,-26 - 9169: -25,-27 - 9170: -25,-28 - 9171: -25,-29 - 9178: -25,-25 - 9179: -27,-25 - 9190: -21,-27 - 9192: -22,-29 - 9193: -22,-30 9211: -26,-31 9229: -24,-42 - 9231: -25,-40 - 9232: -25,-39 - 9233: -25,-38 - 9234: -25,-37 - 9235: -25,-36 - 9247: -22,-36 - 9248: -22,-37 - 9249: -22,-38 - 9250: -22,-38 - 9251: -22,-39 - 9260: -25,-34 - 9261: -25,-33 - 9262: -25,-32 - 9263: -25,-31 - 9264: -25,-30 - 9265: -25,-29 - 9324: -27,-30 - 9325: -27,-31 - 9326: -27,-32 9560: 19,-4 9561: 19,2 9569: -10,2 @@ -2015,12 +1824,6 @@ entities: 10668: -39,31 10669: -39,30 10671: -53,31 - 10672: -53,32 - 10673: -53,33 - 10681: -51,33 - 10682: -51,32 - 10683: -51,32 - 10684: -51,31 10691: -57,24 10692: -57,23 10693: -57,25 @@ -2106,15 +1909,6 @@ entities: 8692: 32,7 8693: 33,7 9143: -20,-22 - 9144: -22,-20 - 9145: -24,-20 - 9146: -23,-20 - 9173: -26,-26 - 9197: -24,-31 - 9198: -23,-31 - 9258: -23,-35 - 9259: -24,-35 - 9330: -26,-33 9391: -28,-23 9392: -29,-23 9393: -30,-23 @@ -2125,7 +1919,6 @@ entities: 9409: -37,-21 9410: -38,-21 9414: -35,-22 - 9552: -5,-17 9553: 8,-17 9554: 14,-17 9564: 14,7 @@ -2173,10 +1966,6 @@ entities: 10312: 20,-17 10313: 21,-17 10314: 21,-17 - 10336: -11,-17 - 10337: -10,-17 - 10338: -9,-17 - 10339: -9,-17 10340: -8,-17 10341: -7,-17 10388: -8,7 @@ -2250,10 +2039,6 @@ entities: 10642: -68,28 10643: -69,28 10644: -70,28 - 10674: -52,33 - 10685: -54,34 - 10686: -53,34 - 10687: -52,34 10946: 31,4 10947: 32,4 10948: 33,4 @@ -2265,6 +2050,8 @@ entities: 11187: 4,19 11188: 2,17 11189: 1,17 + 11421: -5,-17 + 11422: -6,-17 - node: zIndex: -1 color: '#FFFFFFFF' @@ -2293,16 +2080,6 @@ entities: 8679: 33,4 8684: 29,6 8753: 32,4 - 9124: -18,-16 - 9125: -19,-16 - 9172: -26,-26 - 9182: -26,-24 - 9183: -25,-24 - 9195: -24,-31 - 9196: -23,-31 - 9221: -26,-34 - 9240: -24,-35 - 9244: -23,-35 9398: -34,-23 9399: -35,-23 9400: -36,-23 @@ -2364,7 +2141,6 @@ entities: 10332: -2,-15 10333: -3,-15 10334: -7,-15 - 10335: -8,-15 10364: -7,9 10365: -6,9 10366: -5,9 @@ -2435,7 +2211,6 @@ entities: 10663: -41,30 10664: -40,30 10665: -39,30 - 10689: -54,34 10941: 31,7 10942: 32,7 10943: 33,7 @@ -2451,8 +2226,6 @@ entities: id: BrickTileSteelLineW decals: 3571: 9,14 - 8082: -19,-17 - 8083: -17,-17 8544: -2,19 8545: -2,27 8565: 34,-2 @@ -2470,53 +2243,8 @@ entities: 8667: 29,2 8668: 29,3 8682: 30,5 - 9129: -19,-17 - 9130: -19,-18 - 9131: -19,-19 - 9132: -19,-21 - 9133: -19,-22 - 9134: -19,-23 - 9135: -19,-24 - 9155: -27,-25 - 9156: -27,-26 - 9157: -27,-27 - 9158: -27,-28 - 9159: -27,-29 - 9160: -25,-25 - 9161: -25,-29 - 9162: -25,-28 - 9163: -25,-27 - 9180: -27,-25 - 9181: -25,-25 - 9185: -27,-23 - 9200: -22,-30 - 9201: -22,-29 - 9202: -22,-28 - 9203: -22,-27 9212: -26,-31 - 9213: -27,-29 - 9214: -27,-30 - 9215: -27,-30 - 9216: -27,-31 - 9217: -27,-31 - 9218: -27,-32 - 9219: -27,-33 - 9222: -25,-35 - 9223: -25,-36 - 9224: -26,-38 - 9225: -26,-39 - 9226: -26,-40 - 9227: -26,-41 9228: -26,-42 - 9252: -22,-36 - 9253: -22,-37 - 9254: -22,-38 - 9255: -22,-38 - 9256: -22,-39 - 9257: -22,-39 - 9327: -25,-30 - 9328: -25,-31 - 9329: -25,-32 9556: 21,-12 9557: 21,-4 9572: -8,-10 @@ -2645,12 +2373,7 @@ entities: 10586: -37,11 10587: -37,10 10588: -37,10 - 10675: -51,33 - 10676: -51,32 - 10677: -51,31 10678: -53,31 - 10679: -53,32 - 10680: -53,33 10709: -55,13 10710: -55,12 10711: -55,11 @@ -2722,6 +2445,12 @@ entities: 11182: 3,19 11183: 3,18 11197: 0,16 + 11425: -9,-18 + 11427: -9,-19 + 11428: -9,-20 + 11431: -8,-14 + 11432: -8,-13 + 11433: -8,-12 - node: color: '#FFFFFFFF' id: BrickTileWhiteBox @@ -2816,7 +2545,6 @@ entities: 8250: -18,17 8262: -20,13 8386: -9,17 - 8862: -19,-16 8944: -13,-11 9046: -24,-14 10810: -62,36 @@ -2967,7 +2695,6 @@ entities: 8433: -17,11 8434: -14,11 8859: -18,-14 - 8860: -17,-14 8903: -18,2 8904: -17,2 8905: -16,2 @@ -3132,16 +2859,35 @@ entities: 10841: -21,9 10852: -20,11 10924: -27,11 + - node: + color: '#FFFFFFFF' + id: Busha3 + decals: + 11434: -6,-19 + 11435: -7,-18 + - node: + color: '#A88661FF' + id: CheckerNESW + decals: + 11312: -21,-26 + 11313: -21,-27 + 11314: -21,-28 + 11315: -21,-29 + 11316: -21,-30 + - node: + color: '#55391AFF' + id: CheckerNWSE + decals: + 11408: -6,-22 + 11409: -5,-22 + 11410: -5,-23 + 11411: -6,-23 + 11412: -6,-24 + 11413: -5,-24 - node: color: '#FFFFFFFF' id: Delivery decals: - 3146: -21,-44 - 3147: -21,-45 - 3148: -21,-46 - 3149: -19,-44 - 3150: -19,-45 - 3151: -19,-46 8175: -36,-29 8177: -36,-32 8624: 31,2 @@ -3200,12 +2946,6 @@ entities: id: Dirt decals: 672: -8,17 - 1015: -18,-29 - 1016: -14,-28 - 1017: -10,-27 - 1018: -9,-29 - 1019: -6,-29 - 1020: 0,-28 1021: 3,-29 1022: 3,-30 1023: 1,-32 @@ -3476,6 +3216,9 @@ entities: 11126: 34,-11 11127: 34,-12 11128: 34,-13 + 11353: 16,-31 + 11354: 16,-29 + 11355: 16,-33 - node: color: '#FFFFFFFF' id: MiniTileDarkLineN @@ -3501,6 +3244,13 @@ entities: 11060: 48,-14 11061: 49,-14 11062: 50,-14 + 11336: 19,-30 + 11337: 18,-30 + 11338: 17,-30 + 11339: 17,-32 + 11340: 18,-32 + 11341: 19,-32 + 11352: 15,-33 - node: color: '#FFFFFFFF' id: MiniTileDarkLineS @@ -3526,6 +3276,13 @@ entities: 11081: 32,-14 11082: 33,-14 11083: 34,-14 + 11345: 15,-29 + 11346: 17,-30 + 11347: 18,-30 + 11348: 19,-30 + 11349: 19,-32 + 11350: 18,-32 + 11351: 17,-32 - node: color: '#FFFFFFFF' id: MiniTileDarkLineW @@ -3533,6 +3290,9 @@ entities: 11123: 36,-13 11124: 36,-12 11125: 36,-11 + 11342: 16,-32 + 11343: 16,-31 + 11344: 16,-30 - node: color: '#43999EFF' id: MiniTileEndOverlayN @@ -3577,9 +3337,6 @@ entities: color: '#FF9821FF' id: MiniTileInnerOverlayNE decals: - 9389: -25,-35 - 9390: -25,-41 - 9484: -27,-26 9510: -36,-22 9511: -34,-23 - node: @@ -3630,14 +3387,7 @@ entities: id: MiniTileInnerOverlayNW decals: 9031: -13,-11 - 9032: -19,-16 9062: -24,-14 - - node: - color: '#FF9821FF' - id: MiniTileInnerOverlayNW - decals: - 9388: -25,-37 - 9485: -25,-26 - node: color: '#FFA647FF' id: MiniTileInnerOverlayNW @@ -3685,13 +3435,6 @@ entities: decals: 9029: -19,-4 11170: -19,2 - - node: - color: '#FF9821FF' - id: MiniTileInnerOverlaySE - decals: - 9384: -17,-14 - 9385: -25,-35 - 9487: -27,-26 - node: color: '#FFA647FF' id: MiniTileInnerOverlaySE @@ -3732,13 +3475,6 @@ entities: decals: 9030: -13,-9 9655: -13,-11 - - node: - color: '#FF9821FF' - id: MiniTileInnerOverlaySW - decals: - 9386: -25,-34 - 9387: -22,-35 - 9486: -25,-26 - node: color: '#FFA647FF' id: MiniTileInnerOverlaySW @@ -4159,6 +3895,19 @@ entities: 10229: 9,16 10230: 9,15 10231: 9,14 + - node: + color: '#3EB38896' + id: MiniTileWhiteBox + decals: + 11327: -21,-25 + 11328: -20,-26 + 11329: -20,-28 + - node: + color: '#EFB34196' + id: MiniTileWhiteBox + decals: + 11394: -11,-22 + 11397: -11,-24 - node: color: '#FFFFFFFF' id: MiniTileWhiteBox @@ -4169,11 +3918,59 @@ entities: 9110: -25,16 9113: -30,18 9121: -30,15 + - node: + color: '#EFB34196' + id: MiniTileWhiteCornerNe + decals: + 11215: -24,-33 + 11246: -21,-35 + 11277: -21,-20 + 11376: -13,-18 + 11388: -13,-22 + 11401: -7,-22 + - node: + color: '#EFB34196' + id: MiniTileWhiteCornerNw + decals: + 11212: -27,-33 + 11377: -19,-18 + - node: + color: '#EFB34196' + id: MiniTileWhiteCornerSe + decals: + 11233: -20,-42 + 11378: -13,-20 + 11389: -13,-24 + 11399: -7,-24 - node: color: '#FFFFFFFF' id: MiniTileWhiteCornerSe decals: 9096: -24,17 + - node: + color: '#EFB34196' + id: MiniTileWhiteCornerSw + decals: + 11225: -27,-41 + 11232: -21,-42 + 11282: -27,-24 + 11365: -19,-24 + - node: + color: '#3EB38896' + id: MiniTileWhiteEndE + decals: + 11326: -21,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteEndE + decals: + 11252: -18,-38 + 11253: -18,-36 + - node: + color: '#3EB38896' + id: MiniTileWhiteEndN + decals: + 11321: -22,-26 - node: color: '#FFFFFFFF' id: MiniTileWhiteEndN @@ -4188,6 +3985,84 @@ entities: 9116: -34,15 10868: -20,8 10872: -22,9 + - node: + color: '#3EB38896' + id: MiniTileWhiteEndW + decals: + 11332: -24,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteEndW + decals: + 11395: -9,-22 + 11396: -9,-24 + - node: + color: '#3EB38896' + id: MiniTileWhiteInnerNe + decals: + 11330: -22,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteInnerNe + decals: + 11217: -24,-35 + 11243: -21,-38 + 11245: -21,-36 + 11384: -17,-22 + - node: + color: '#3EB38896' + id: MiniTileWhiteInnerNw + decals: + 11331: -22,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteInnerNw + decals: + 11406: -7,-24 + - node: + color: '#EFB34196' + id: MiniTileWhiteInnerSe + decals: + 11237: -20,-38 + 11244: -21,-36 + 11383: -17,-20 + - node: + color: '#EFB34196' + id: MiniTileWhiteInnerSw + decals: + 11231: -21,-41 + 11405: -7,-22 + - node: + color: '#3EB38896' + id: MiniTileWhiteLineE + decals: + 11322: -22,-27 + 11323: -22,-28 + 11324: -22,-29 + 11325: -22,-30 + - node: + color: '#EFB34196' + id: MiniTileWhiteLineE + decals: + 11216: -24,-34 + 11234: -20,-41 + 11235: -20,-40 + 11236: -20,-39 + 11251: -21,-37 + 11264: -25,-28 + 11265: -25,-29 + 11266: -25,-27 + 11267: -25,-26 + 11268: -25,-25 + 11278: -21,-21 + 11279: -21,-22 + 11280: -21,-23 + 11281: -21,-24 + 11357: -17,-17 + 11379: -13,-19 + 11386: -17,-21 + 11387: -13,-23 + 11400: -7,-23 - node: color: '#FFFFFFFF' id: MiniTileWhiteLineE @@ -4195,6 +4070,70 @@ entities: 9097: -24,18 9119: -34,17 9120: -34,16 + - node: + color: '#3EB38896' + id: MiniTileWhiteLineN + decals: + 11334: -23,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteLineN + decals: + 11213: -26,-33 + 11214: -25,-33 + 11241: -19,-38 + 11242: -20,-38 + 11247: -22,-35 + 11248: -23,-35 + 11249: -20,-36 + 11250: -19,-36 + 11274: -24,-20 + 11275: -23,-20 + 11276: -22,-20 + 11358: -12,-18 + 11359: -12,-20 + 11371: -18,-18 + 11372: -17,-18 + 11373: -16,-18 + 11374: -15,-18 + 11375: -14,-18 + 11385: -16,-22 + 11391: -14,-22 + 11392: -12,-23 + 11402: -8,-22 + 11403: -8,-24 + - node: + color: '#3EB38896' + id: MiniTileWhiteLineS + decals: + 11333: -23,-31 + 11335: -22,-31 + - node: + color: '#EFB34196' + id: MiniTileWhiteLineS + decals: + 11226: -26,-41 + 11227: -25,-41 + 11228: -24,-41 + 11229: -23,-41 + 11230: -22,-41 + 11238: -19,-38 + 11239: -19,-36 + 11240: -20,-36 + 11284: -26,-24 + 11285: -25,-24 + 11360: -12,-20 + 11361: -12,-18 + 11362: -16,-24 + 11363: -17,-24 + 11364: -18,-24 + 11380: -14,-20 + 11381: -15,-20 + 11382: -16,-20 + 11390: -14,-24 + 11393: -12,-23 + 11398: -8,-24 + 11404: -8,-22 - node: color: '#FFFFFFFF' id: MiniTileWhiteLineS @@ -4203,17 +4142,71 @@ entities: 9099: -27,17 9100: -26,17 9101: -25,17 + - node: + color: '#3EB38896' + id: MiniTileWhiteLineW + decals: + 11317: -22,-30 + 11318: -22,-29 + 11319: -22,-28 + 11320: -22,-27 + - node: + color: '#EFB34196' + id: MiniTileWhiteLineW + decals: + 11218: -27,-34 + 11219: -27,-35 + 11220: -27,-36 + 11221: -27,-37 + 11222: -27,-38 + 11223: -27,-39 + 11224: -27,-40 + 11269: -27,-29 + 11270: -27,-28 + 11271: -27,-27 + 11272: -27,-26 + 11273: -27,-25 + 11283: -27,-23 + 11356: -18,-17 + 11366: -19,-23 + 11367: -19,-22 + 11368: -19,-21 + 11369: -19,-20 + 11370: -19,-19 + 11407: -7,-23 - node: color: '#FFFFFFFF' id: MiniTileWhiteLineW decals: 9117: -34,16 9118: -34,17 + - node: + color: '#4B362BFF' + id: QuarterTileOverlayGreyscale + decals: + 11303: -18,-33 + - node: + color: '#EFB34196' + id: QuarterTileOverlayGreyscale + decals: + 11288: -26,-24 + 11289: -25,-24 + - node: + color: '#4B362BFF' + id: QuarterTileOverlayGreyscale180 + decals: + 11302: -18,-33 - node: color: '#4B709CFF' id: QuarterTileOverlayGreyscale180 decals: 10293: 2,-17 + - node: + color: '#EFB34196' + id: QuarterTileOverlayGreyscale180 + decals: + 11286: -27,-23 + 11287: -26,-23 - node: color: '#FFFFFFFF' id: WarnCornerNE @@ -4267,17 +4260,17 @@ entities: color: '#FFFFFFFF' id: WarnCornerSmallSE decals: - 110: -11,-29 - 111: -14,-29 - 112: -17,-29 8855: -25,-21 + 11296: -10,-37 + 11297: -7,-37 + 11298: -4,-37 - node: color: '#FFFFFFFF' id: WarnCornerSmallSW decals: - 113: -13,-29 - 114: -10,-29 - 115: -16,-29 + 11299: -3,-37 + 11300: -6,-37 + 11301: -9,-37 - node: color: '#FFFFFFFF' id: WarnEndS @@ -4289,9 +4282,6 @@ entities: decals: 52: -26,-43 64: -25,-43 - 107: -17,-30 - 108: -14,-30 - 109: -11,-30 173: -26,-46 174: -26,-45 175: -26,-44 @@ -4301,6 +4291,12 @@ entities: 8193: -35,-32 8842: -22,-24 8843: -22,-23 + 11258: -17,-40 + 11259: -17,-41 + 11260: -17,-42 + 11293: -7,-38 + 11294: -10,-38 + 11295: -4,-38 - node: color: '#DE3A3AFF' id: WarnLineGreyscaleN @@ -4321,6 +4317,7 @@ entities: 8191: -36,-30 8850: -27,-22 8851: -26,-22 + 11262: -19,-41 - node: color: '#FFFFFFFF' id: WarnLineS @@ -4329,9 +4326,6 @@ entities: 57: -24,-43 58: -24,-44 61: -25,-43 - 104: -13,-30 - 105: -10,-30 - 106: -16,-30 168: -25,-44 169: -25,-45 171: -24,-45 @@ -4340,6 +4334,9 @@ entities: 8189: -37,-32 8840: -24,-24 8841: -24,-23 + 11290: -9,-38 + 11291: -6,-38 + 11292: -3,-38 - node: color: '#FFFFFFFF' id: WarnLineW @@ -4350,6 +4347,7 @@ entities: 8195: -36,-31 8848: -26,-20 8849: -27,-20 + 11261: -19,-41 - node: color: '#612620FF' id: WoodTrimThinBoxWhite @@ -4374,6 +4372,80 @@ entities: 5: -3,-5 6: -4,-6 15: -4,-4 + - node: + color: '#55391AFF' + id: WoodTrimThinCornerNeWhite + decals: + 11419: -5,-22 + - node: + color: '#55391AFF' + id: WoodTrimThinCornerNwWhite + decals: + 11418: -6,-22 + - node: + color: '#55391AFF' + id: WoodTrimThinCornerSeWhite + decals: + 11414: -5,-24 + - node: + color: '#55391AFF' + id: WoodTrimThinCornerSwWhite + decals: + 11415: -6,-24 + - node: + color: '#A88661FF' + id: WoodTrimThinEndEWhite + decals: + 11255: -18,-37 + - node: + color: '#A88661FF' + id: WoodTrimThinEndWWhite + decals: + 11254: -20,-37 + - node: + color: '#4B362BFF' + id: WoodTrimThinInnerNwWhite + decals: + 11311: -16,-35 + - node: + color: '#4B362BFF' + id: WoodTrimThinInnerSwWhite + decals: + 11310: -16,-31 + - node: + color: '#55391AFF' + id: WoodTrimThinLineEWhite + decals: + 11416: -5,-23 + - node: + color: '#A88661FF' + id: WoodTrimThinLineNWhite + decals: + 11256: -19,-37 + - node: + color: '#4B362BFF' + id: WoodTrimThinLineSWhite + decals: + 11304: -19,-31 + 11305: -17,-31 + 11306: -18,-31 + - node: + color: '#A88661FF' + id: WoodTrimThinLineSWhite + decals: + 11257: -19,-37 + - node: + color: '#4B362BFF' + id: WoodTrimThinLineWWhite + decals: + 11307: -16,-34 + 11308: -16,-33 + 11309: -16,-32 + - node: + color: '#55391AFF' + id: WoodTrimThinLineWWhite + decals: + 11417: -6,-23 - node: color: '#FFFFFFFF' id: bushsnowa2 @@ -4617,8 +4689,6 @@ entities: 1: 53709 -3,3: 1: 56829 - -4,4: - 1: 65528 -3,4: 1: 56825 -3,-1: @@ -4641,7 +4711,7 @@ entities: -1,4: 1: 30583 -4,-4: - 1: 3855 + 1: 4095 -5,-4: 1: 28654 -4,-3: @@ -4655,13 +4725,13 @@ entities: -5,-1: 1: 26223 -3,-4: - 1: 53231 + 1: 53247 -3,-3: 1: 56799 -3,-2: 1: 56783 -3,-5: - 1: 57480 + 1: 61423 -2,-4: 1: 4607 0: 32768 @@ -4672,66 +4742,53 @@ entities: 1: 4369 0: 4 -2,-5: - 1: 61491 + 1: 65535 -1,-5: 1: 63662 -4,-8: - 3: 3 - 1: 65280 - 4: 8 + 1: 65535 -4,-9: - 3: 12288 - 4: 32768 - 0: 79 + 1: 65528 -5,-8: - 1: 60928 - 3: 6 + 1: 61166 -4,-7: 1: 4095 -5,-7: 1: 4079 -4,-6: - 1: 61422 + 1: 4095 -5,-6: 1: 61423 -4,-5: - 1: 3822 + 1: 4095 -5,-5: - 1: 44782 + 1: 52974 -3,-8: - 4: 1 - 1: 65280 - 5: 12 + 1: 65527 -3,-7: 1: 4095 - -3,-9: - 4: 4096 - 5: 49152 - 0: 47 -3,-6: - 1: 3822 + 1: 3838 + -3,-9: + 1: 63487 -2,-8: - 1: 65382 + 1: 65520 -2,-7: 1: 4095 -2,-6: 1: 4095 -2,-9: - 1: 24576 - 0: 159 + 1: 28927 -1,-8: - 3: 7 - 1: 60992 + 1: 32631 -1,-7: - 1: 3823 + 1: 1919 -1,-9: - 3: 28672 - 0: 159 + 1: 65535 -1,-6: 1: 43246 0,-8: - 3: 3 - 1: 65416 + 1: 65534 0,-7: 1: 4095 0,-6: @@ -4784,9 +4841,8 @@ entities: 8,-1: 1: 65520 0,-9: - 3: 12288 - 1: 32768 - 0: 207 + 1: 57361 + 0: 204 1,-8: 1: 65523 1,-7: @@ -4800,34 +4856,42 @@ entities: 1: 29431 2,-9: 1: 4096 - 0: 26127 + 0: 60943 2,-8: - 0: 61030 + 0: 61166 2,-7: 0: 3822 3,-8: - 0: 13056 + 0: 13107 + 1: 34952 3,-7: 0: 4595 1: 49152 3,-6: 1: 64796 + 3,-9: + 0: 13071 + 1: 32768 + 4,-8: + 1: 65535 4,-7: 0: 240 1: 28672 4,-6: 1: 30487 + 4,-9: + 1: 61440 + 0: 15 + 5,-8: + 1: 61423 5,-7: - 0: 17 + 0: 16 1: 47308 5,-6: 1: 48043 - 5,-8: - 0: 4369 - 1: 52428 5,-9: - 0: 4369 - 1: 52424 + 1: 60616 + 0: 17 6,-7: 1: 64797 6,-6: @@ -4861,7 +4925,7 @@ entities: -8,2: 1: 30576 -9,2: - 1: 56768 + 1: 56704 0: 1 -8,3: 1: 29431 @@ -4958,8 +5022,7 @@ entities: -6,-7: 1: 36590 -5,-9: - 3: 24576 - 0: 142 + 1: 60935 -8,-11: 0: 60394 -9,-11: @@ -4982,42 +5045,65 @@ entities: -7,-10: 1: 61166 -6,-12: - 0: 15 - 1: 47872 - -6,-11: - 1: 63675 - -6,-10: - 1: 65523 - -6,-13: - 0: 24320 - -5,-12: 0: 34959 1: 13056 + -6,-11: + 1: 63539 + 0: 8 + -6,-10: + 1: 65535 + -6,-13: + 0: 22272 + -5,-12: + 0: 44835 -5,-11: - 1: 13107 - 0: 34952 - -5,-13: - 0: 24320 + 0: 15 + 1: 64768 -5,-10: - 0: 26344 + 1: 30493 + -5,-13: + 0: 8192 -4,-12: - 0: 12850 + 0: 44800 -4,-11: - 0: 12850 + 0: 15 + 1: 30464 -4,-10: - 0: 44594 - -4,-13: - 0: 8960 + 1: 34823 + 3: 13056 + -3,-12: + 0: 58112 + -3,-11: + 0: 245 + 4: 12288 + 5: 32768 -3,-10: - 0: 44800 + 4: 3 + 1: 65280 + 5: 8 + -2,-12: + 0: 61440 + -2,-11: + 0: 245 + 5: 4096 + 6: 49152 -2,-10: - 0: 44800 + 5: 1 + 1: 65280 + 6: 12 + -1,-12: + 0: 61440 + -1,-11: + 0: 245 + 6: 24576 -1,-10: - 0: 44800 - 0,-10: - 0: 44936 + 1: 65280 + 6: 6 0,-12: - 0: 34952 + 0: 63624 + 0,-10: + 1: 4352 + 0: 52360 0,-13: 0: 34816 0,-11: @@ -5036,16 +5122,12 @@ entities: 0: 3822 3,-11: 0: 58612 - 3,-9: - 0: 15 3,-12: 0: 61166 3,-10: 0: 3822 4,-11: 0: 58612 - 4,-9: - 0: 15 -12,-7: 0: 49152 -12,-6: @@ -5234,6 +5316,8 @@ entities: 8,7: 0: 112 1: 30464 + -4,4: + 1: 65520 -4,5: 1: 60943 -5,5: @@ -5241,7 +5325,7 @@ entities: -4,6: 1: 61166 -5,6: - 1: 4095 + 1: 4087 -4,7: 1: 61684 -5,7: @@ -5646,7 +5730,7 @@ entities: 0: 40863 -4,16: 0: 15 - 3: 30464 + 6: 30464 -3,13: 0: 30076 -3,15: @@ -5660,7 +5744,7 @@ entities: -12,7: 1: 4095 -13,7: - 1: 45055 + 1: 36863 -12,8: 1: 56797 -11,7: @@ -5707,12 +5791,13 @@ entities: -9,1: 0: 1911 -13,8: - 1: 48059 + 1: 34952 + 0: 1 -12,9: 1: 40413 -13,9: 1: 34952 - 0: 272 + 0: 256 -12,10: 1: 61695 -13,10: @@ -5761,7 +5846,7 @@ entities: 0: 4881 -8,16: 1: 51215 - 3: 13056 + 6: 13056 -7,14: 1: 61440 0: 234 @@ -5771,7 +5856,7 @@ entities: 0: 43748 -7,16: 1: 29467 - 3: 34816 + 6: 34816 -6,13: 0: 8949 -6,14: @@ -5781,10 +5866,10 @@ entities: 0: 14 -6,16: 1: 7 - 3: 65280 + 6: 65280 -5,16: 0: 15 - 3: 65280 + 6: 65280 -16,7: 1: 16383 -17,7: @@ -5818,13 +5903,13 @@ entities: 1: 13107 0: 2184 -14,7: - 1: 36859 + 1: 4091 -14,3: 1: 13299 0: 32768 -14,8: - 1: 52428 - 0: 16 + 0: 4 + 1: 60936 -13,5: 1: 1 -15,2: @@ -5865,27 +5950,28 @@ entities: 0: 61440 -15,11: 0: 245 - -14,9: - 0: 545 -14,10: 1: 239 0: 28672 -14,11: 0: 23925 + -14,9: + 1: 14 + 0: 512 -14,12: 0: 15 -13,12: 0: 3759 -9,16: 1: 8 - 3: 65280 + 6: 65280 0: 3 -8,17: - 3: 3 + 6: 3 0: 12032 1: 8 -9,17: - 3: 15 + 6: 15 0: 20224 -8,18: 0: 47 @@ -5894,21 +5980,21 @@ entities: -7,17: 1: 3 0: 12032 - 3: 8 + 6: 8 -7,18: 0: 47 -6,17: - 3: 15 + 6: 15 0: 7936 -6,18: 0: 31 -5,17: - 3: 15 + 6: 15 0: 40704 -5,18: 0: 159 -4,17: - 3: 7 + 6: 7 0: 20224 -4,18: 0: 79 @@ -5916,19 +6002,19 @@ entities: 0: 20288 -11,16: 0: 4383 - 3: 52224 + 6: 52224 -10,15: 0: 12064 -10,16: 0: 15 - 3: 65280 + 6: 65280 -11,17: 0: 12049 - 3: 12 + 6: 12 -11,18: 0: 47 -10,17: - 3: 15 + 6: 15 0: 40704 -10,18: 0: 159 @@ -6021,94 +6107,32 @@ entities: uniqueMixes: - volume: 2500 immutable: True - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + moles: {} - volume: 2500 temperature: 293.15 moles: - - 21.824879 - - 82.10312 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 21.824879 + Nitrogen: 82.10312 - volume: 2500 temperature: 235 moles: - - 21.824879 - - 82.10312 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 27.225372 + Nitrogen: 102.419266 - volume: 2500 temperature: 293.15 moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Plasma: 6666.982 - volume: 2500 temperature: 293.15 moles: - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 6666.982 - volume: 2500 temperature: 293.15 moles: - - 0 - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Nitrogen: 6666.982 + - volume: 2500 + temperature: 293.15 + moles: {} chunkSize: 4 - type: GasTileOverlay - type: RadiationGridResistance @@ -6116,6 +6140,48 @@ entities: - type: BecomesStation id: Awesome - type: ImplicitRoof + - type: ExplosionAirtightGrid + - uid: 14229 + components: + - type: MetaData + name: grid + - type: Transform + pos: -2.786892,1.3025341 + parent: 1 + - type: MapGrid + chunks: + -2,-4: + ind: -2,-4 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + version: 7 + - type: Broadphase + - type: Physics + bodyStatus: InAir + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + dampingModifier: 0.25 + - type: ImplicitRoof + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - type: GridAtmosphere + version: 2 + data: + chunkSize: 4 + - type: GasTileOverlay + - type: IFF + flags: HideLabel + - type: NavMap - uid: 16832 components: - type: MetaData @@ -6167,6 +6233,7 @@ entities: - type: IFF flags: Hide - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 16838 components: - type: MetaData @@ -6222,6 +6289,7 @@ entities: - type: IFF flags: Hide - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 16911 components: - type: MetaData @@ -7097,6 +7165,7 @@ entities: - type: GasTileOverlay - type: RadiationGridResistance - type: ImplicitRoof + - type: ExplosionAirtightGrid - uid: 19351 components: - type: MetaData @@ -7166,19 +7235,6 @@ entities: - 7700 - type: Fixtures fixtures: {} - - uid: 5 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-30.5 - parent: 2 - - type: DeviceList - devices: - - 9832 - - 9850 - - 9849 - - type: Fixtures - fixtures: {} - uid: 6 components: - type: Transform @@ -7234,8 +7290,8 @@ entities: - 7514 - 7513 - 7512 - - 7767 - - 7768 + - 3550 + - 3437 - 7593 - 7747 - 7749 @@ -7243,37 +7299,16 @@ entities: - 7750 - 7503 - 7679 - - type: Fixtures - fixtures: {} - - uid: 10 - components: - - type: Transform - pos: -8.5,-20.5 - parent: 2 - - type: DeviceList - devices: - - 9699 - - 425 - - 9833 - - 7606 + - 3441 + - 3292 - type: Fixtures fixtures: {} - uid: 11 components: - type: Transform - pos: -21.5,-33.5 + rot: 1.5707963267948966 rad + pos: -27.5,-32.5 parent: 2 - - type: DeviceList - devices: - - 9844 - - 437 - - 9710 - - 9843 - - 436 - - 9709 - - 7596 - - 7595 - - 7594 - type: Fixtures fixtures: {} - uid: 12 @@ -7309,46 +7344,6 @@ entities: - 9688 - type: Fixtures fixtures: {} - - uid: 14 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-19.5 - parent: 2 - - type: DeviceList - devices: - - 7765 - - 7766 - - 9701 - - 426 - - 9835 - - 7604 - - 7600 - - 7601 - - 7754 - - 7753 - - type: Fixtures - fixtures: {} - - uid: 15 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-14.5 - parent: 2 - - type: DeviceList - devices: - - 7686 - - 7685 - - 7605 - - 7766 - - 7765 - - 7768 - - 7767 - - 9845 - - 438 - - 9711 - - type: Fixtures - fixtures: {} - uid: 16 components: - type: Transform @@ -7363,19 +7358,6 @@ entities: - 7506 - type: Fixtures fixtures: {} - - uid: 17 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-20.5 - parent: 2 - - type: DeviceList - devices: - - 9712 - - 440 - - 7604 - - type: Fixtures - fixtures: {} - uid: 18 components: - type: Transform @@ -8491,7 +8473,6 @@ entities: - 7546 - 7545 - 7728 - - 7727 - 7726 - type: Fixtures fixtures: {} @@ -8661,39 +8642,6 @@ entities: - 7733 - type: Fixtures fixtures: {} - - uid: 98 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 25.5,-25.5 - parent: 2 - - type: DeviceList - devices: - - 7519 - - 7520 - - 9733 - - 456 - - 9865 - - 7521 - - 7493 - - type: Fixtures - fixtures: {} - - uid: 99 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 24.5,-32.5 - parent: 2 - - type: DeviceList - devices: - - 9734 - - 419 - - 7521 - - 7493 - - 7676 - - 9866 - - type: Fixtures - fixtures: {} - uid: 100 components: - type: Transform @@ -8786,6 +8734,62 @@ entities: - 7611 - type: Fixtures fixtures: {} + - uid: 170 + components: + - type: Transform + pos: -6.5,-20.5 + parent: 2 + - type: DeviceList + devices: + - 3454 + - 4810 + - 169 + - type: Fixtures + fixtures: {} + - uid: 993 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.5,-19.5 + parent: 2 + - type: DeviceList + devices: + - 3438 + - 3443 + - 3454 + - 3550 + - 3437 + - 3441 + - 3292 + - 3293 + - 3291 + - type: Fixtures + fixtures: {} + - uid: 1509 + components: + - type: Transform + pos: 17.5,-27.5 + parent: 2 + - type: DeviceList + devices: + - 13567 + - 13564 + - 1507 + - 9922 + - type: Fixtures + fixtures: {} + - uid: 19372 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -51.5,34.5 + parent: 2 + - type: DeviceList + devices: + - 19370 + - 19371 + - type: Fixtures + fixtures: {} - proto: AirCanister entities: - uid: 105 @@ -8838,11 +8842,6 @@ entities: - type: Transform pos: -70.5,33.5 parent: 2 - - uid: 115 - components: - - type: Transform - pos: -14.5,-31.5 - parent: 2 - uid: 116 components: - type: Transform @@ -8889,18 +8888,8 @@ entities: immutable: False temperature: 0 moles: - - 393.0592 - - 1478.6512 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 393.0592 + Nitrogen: 1478.6512 - uid: 16918 components: - type: Transform @@ -9012,13 +9001,20 @@ entities: rot: -1.5707963267948966 rad pos: 34.5,-32.5 parent: 2 -- proto: AirlockAtmosphericsLocked +- proto: AirlockAtmosphericsGlassLocked entities: - - uid: 135 + - uid: 12159 components: - type: Transform - pos: -3.5,-27.5 + pos: -0.5,-29.5 parent: 2 + - uid: 12160 + components: + - type: Transform + pos: -0.5,-27.5 + parent: 2 +- proto: AirlockAtmosphericsLocked + entities: - uid: 136 components: - type: Transform @@ -9148,11 +9144,10 @@ entities: parent: 2 - proto: AirlockChiefEngineerLocked entities: - - uid: 158 + - uid: 11276 components: - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-21.5 + pos: -11.5,-22.5 parent: 2 - proto: AirlockChiefMedicalOfficerLocked entities: @@ -9216,18 +9211,6 @@ entities: rot: 1.5707963267948966 rad pos: 24.5,-33.5 parent: 2 -- proto: AirlockEngineeringGlassLocked - entities: - - uid: 169 - components: - - type: Transform - pos: -18.5,-16.5 - parent: 2 - - uid: 170 - components: - - type: Transform - pos: -16.5,-16.5 - parent: 2 - proto: AirlockEngineeringLocked entities: - uid: 171 @@ -9260,11 +9243,6 @@ entities: - type: Transform pos: -35.5,-23.5 parent: 2 - - uid: 177 - components: - - type: Transform - pos: -15.5,-15.5 - parent: 2 - uid: 178 components: - type: Transform @@ -9290,11 +9268,6 @@ entities: - type: Transform pos: -22.5,-32.5 parent: 2 - - uid: 183 - components: - - type: Transform - pos: -11.5,-15.5 - parent: 2 - uid: 184 components: - type: Transform @@ -9310,11 +9283,6 @@ entities: - type: Transform pos: 31.5,-17.5 parent: 2 - - uid: 187 - components: - - type: Transform - pos: -21.5,-40.5 - parent: 2 - uid: 188 components: - type: Transform @@ -9342,18 +9310,32 @@ entities: rot: 1.5707963267948966 rad pos: 25.5,29.5 parent: 2 -- proto: AirlockEVALocked - entities: - - uid: 193 + - uid: 10322 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -52.5,31.5 + pos: -11.5,-17.5 parent: 2 - - uid: 194 + - uid: 11175 components: - type: Transform - pos: -50.5,31.5 + pos: -11.5,-19.5 + parent: 2 + - uid: 12158 + components: + - type: Transform + pos: -18.5,-40.5 + parent: 2 +- proto: AirlockEVAGlassLocked + entities: + - uid: 19365 + components: + - type: Transform + pos: 20.5,-31.5 + parent: 2 + - uid: 19366 + components: + - type: Transform + pos: 20.5,-29.5 parent: 2 - proto: AirlockExternal entities: @@ -9638,10 +9620,10 @@ entities: parent: 2 - proto: AirlockExternalGlassAtmosphericsLocked entities: - - uid: 234 + - uid: 12547 components: - type: Transform - pos: -1.5,-30.5 + pos: -8.5,-32.5 parent: 2 - proto: AirlockExternalGlassEngineeringLocked entities: @@ -10651,16 +10633,6 @@ entities: - type: Transform pos: -19.5,-9.5 parent: 2 - - uid: 396 - components: - - type: Transform - pos: -15.5,-13.5 - parent: 2 - - uid: 397 - components: - - type: Transform - pos: -11.5,-13.5 - parent: 2 - uid: 398 components: - type: Transform @@ -10784,14 +10756,6 @@ entities: - type: DeviceNetwork deviceLists: - 69 - - uid: 419 - components: - - type: Transform - pos: 23.5,-32.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 99 - uid: 420 components: - type: Transform @@ -10829,22 +10793,6 @@ entities: - type: DeviceNetwork deviceLists: - 18 - - uid: 425 - components: - - type: Transform - pos: -7.5,-22.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 10 - - uid: 426 - components: - - type: Transform - pos: -17.5,-19.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - uid: 427 components: - type: Transform @@ -10913,25 +10861,6 @@ entities: - type: Transform pos: -25.5,-37.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - uid: 437 - components: - - type: Transform - pos: -21.5,-36.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - uid: 438 - components: - - type: Transform - pos: -17.5,-14.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - uid: 439 components: - type: Transform @@ -10940,14 +10869,6 @@ entities: - type: DeviceNetwork deviceLists: - 77 - - uid: 440 - components: - - type: Transform - pos: -13.5,-21.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 17 - uid: 441 components: - type: Transform @@ -11065,14 +10986,6 @@ entities: - type: DeviceNetwork deviceLists: - 97 - - uid: 456 - components: - - type: Transform - pos: 24.5,-26.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - uid: 457 components: - type: Transform @@ -11672,6 +11585,11 @@ entities: - type: Transform pos: -20.5,7.5 parent: 2 + - uid: 648 + components: + - type: Transform + pos: -6.5,-22.5 + parent: 2 - proto: AltarNanotrasen entities: - uid: 536 @@ -11770,13 +11688,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 548 - components: - - type: Transform - pos: -6.5,-20.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 549 components: - type: Transform @@ -12028,14 +11939,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 581 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-21.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 582 components: - type: Transform @@ -12089,14 +11992,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 589 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -19.5,-20.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 590 components: - type: Transform @@ -12156,13 +12051,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 598 - components: - - type: Transform - pos: -19.5,27.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 599 components: - type: Transform @@ -12201,14 +12089,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 604 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -49.5,33.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 605 components: - type: Transform @@ -12271,6 +12151,37 @@ entities: parent: 2 - type: Fixtures fixtures: {} + - uid: 1505 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -55.5,34.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 2098 + components: + - type: Transform + pos: -17.5,27.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 4073 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-24.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 9835 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-20.5 + parent: 2 + - type: Fixtures + fixtures: {} - uid: 16942 components: - type: Transform @@ -12317,6 +12228,20 @@ entities: parent: 16911 - type: Fixtures fixtures: {} + - uid: 19487 + components: + - type: Transform + pos: 1.5,-24.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 19494 + components: + - type: Transform + pos: 15.5,-27.5 + parent: 2 + - type: Fixtures + fixtures: {} - proto: APCElectronics entities: - uid: 613 @@ -12428,6 +12353,11 @@ entities: - type: Transform pos: -32.477085,63.38263 parent: 2 + - uid: 15519 + components: + - type: Transform + pos: -11.492144,-26.475725 + parent: 2 - proto: Ashtray entities: - uid: 634 @@ -12450,6 +12380,11 @@ entities: - type: Transform pos: -37.501637,39.603874 parent: 2 + - uid: 14547 + components: + - type: Transform + pos: -11.695269,-26.24135 + parent: 2 - proto: AsimovCircuitBoard entities: - uid: 639 @@ -12473,35 +12408,6 @@ entities: rot: 1.5707963267948966 rad pos: 54.5,-7.5 parent: 2 - - uid: 647 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -6.5,-18.5 - parent: 2 - - uid: 648 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -6.5,-19.5 - parent: 2 - - uid: 649 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -8.5,-18.5 - parent: 2 - - uid: 650 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -8.5,-19.5 - parent: 2 - - uid: 651 - components: - - type: Transform - pos: -1.5,-30.5 - parent: 2 - uid: 652 components: - type: Transform @@ -12637,63 +12543,28 @@ entities: rot: 1.5707963267948966 rad pos: 54.5,-8.5 parent: 2 + - uid: 14425 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -53.5,32.5 + parent: 2 + - uid: 15318 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -51.5,32.5 + parent: 2 +- proto: AtmosDeviceFanTiny + entities: + - uid: 8097 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-32.5 + parent: 2 - proto: AtmosFixBlockerMarker entities: - - uid: 675 - components: - - type: Transform - pos: -15.5,-31.5 - parent: 2 - - uid: 676 - components: - - type: Transform - pos: -14.5,-31.5 - parent: 2 - - uid: 677 - components: - - type: Transform - pos: -14.5,-32.5 - parent: 2 - - uid: 678 - components: - - type: Transform - pos: -15.5,-32.5 - parent: 2 - - uid: 679 - components: - - type: Transform - pos: -18.5,-31.5 - parent: 2 - - uid: 680 - components: - - type: Transform - pos: -17.5,-31.5 - parent: 2 - - uid: 681 - components: - - type: Transform - pos: -17.5,-32.5 - parent: 2 - - uid: 682 - components: - - type: Transform - pos: -18.5,-32.5 - parent: 2 - - uid: 683 - components: - - type: Transform - pos: -3.5,-31.5 - parent: 2 - - uid: 684 - components: - - type: Transform - pos: -2.5,-31.5 - parent: 2 - - uid: 685 - components: - - type: Transform - pos: -2.5,-32.5 - parent: 2 - uid: 686 components: - type: Transform @@ -13054,40 +12925,45 @@ entities: - type: Transform pos: -13.5,67.5 parent: 2 - - uid: 758 + - uid: 12087 components: - type: Transform - pos: -3.5,-32.5 + pos: -4.5,-39.5 parent: 2 - - uid: 759 + - uid: 12088 components: - type: Transform - pos: -1.5,-32.5 + pos: -5.5,-39.5 parent: 2 - - uid: 760 + - uid: 12089 components: - type: Transform - pos: -1.5,-31.5 + pos: -5.5,-40.5 parent: 2 - - uid: 761 + - uid: 12090 components: - type: Transform - pos: 0.5,-31.5 + pos: -4.5,-40.5 parent: 2 - - uid: 762 + - uid: 12095 components: - type: Transform - pos: 0.5,-32.5 + pos: -2.5,-39.5 parent: 2 - - uid: 763 + - uid: 12096 components: - type: Transform - pos: 1.5,-31.5 + pos: -2.5,-40.5 parent: 2 - - uid: 764 + - uid: 12155 components: - type: Transform - pos: 1.5,-32.5 + pos: -1.5,-40.5 + parent: 2 + - uid: 12156 + components: + - type: Transform + pos: -1.5,-39.5 parent: 2 - proto: AtmosFixFreezerMarker entities: @@ -13538,47 +13414,69 @@ entities: parent: 2 - proto: AtmosFixNitrogenMarker entities: - - uid: 854 + - uid: 12035 components: - type: Transform - pos: -9.5,-31.5 + pos: -7.5,-40.5 parent: 2 - - uid: 855 + - uid: 12036 components: - type: Transform - pos: -8.5,-31.5 + pos: -7.5,-39.5 parent: 2 - - uid: 856 + - uid: 12085 components: - type: Transform - pos: -8.5,-32.5 + pos: -8.5,-39.5 parent: 2 - - uid: 857 + - uid: 12086 components: - type: Transform - pos: -9.5,-32.5 + pos: -8.5,-40.5 parent: 2 - proto: AtmosFixOxygenMarker entities: - - uid: 858 + - uid: 11930 components: - type: Transform - pos: -12.5,-31.5 + pos: -10.5,-40.5 parent: 2 - - uid: 859 + - uid: 11932 components: - type: Transform - pos: -11.5,-31.5 + pos: -10.5,-39.5 parent: 2 - - uid: 860 + - uid: 12003 components: - type: Transform - pos: -11.5,-32.5 + pos: -11.5,-39.5 parent: 2 - - uid: 861 + - uid: 12004 components: - type: Transform - pos: -12.5,-32.5 + pos: -11.5,-40.5 + parent: 2 +- proto: AtmosFixPlasmaMarker + entities: + - uid: 10691 + components: + - type: Transform + pos: -14.5,-37.5 + parent: 2 + - uid: 11792 + components: + - type: Transform + pos: -14.5,-36.5 + parent: 2 + - uid: 11813 + components: + - type: Transform + pos: -15.5,-36.5 + parent: 2 + - uid: 11929 + components: + - type: Transform + pos: -15.5,-37.5 parent: 2 - proto: Autolathe entities: @@ -13604,10 +13502,20 @@ entities: parent: 2 - proto: BannerEngineering entities: - - uid: 866 + - uid: 1283 components: - type: Transform - pos: -12.5,-20.5 + pos: -14.5,-21.5 + parent: 2 + - uid: 3442 + components: + - type: Transform + pos: -8.5,-21.5 + parent: 2 + - uid: 3621 + components: + - type: Transform + pos: -6.5,-23.5 parent: 2 - proto: BannerGreen entities: @@ -13633,6 +13541,16 @@ entities: - type: Transform pos: 5.5,-3.5 parent: 2 + - uid: 19359 + components: + - type: Transform + pos: 15.5,-32.5 + parent: 2 + - uid: 19360 + components: + - type: Transform + pos: 15.5,-28.5 + parent: 2 - proto: BannerRevolution entities: - uid: 871 @@ -13888,11 +13806,6 @@ entities: - type: Transform pos: 40.5,-15.5 parent: 2 - - uid: 916 - components: - - type: Transform - pos: -13.5,-17.5 - parent: 2 - uid: 917 components: - type: Transform @@ -13983,6 +13896,11 @@ entities: - type: Transform pos: 42.5,-4.5 parent: 2 + - uid: 3451 + components: + - type: Transform + pos: -4.5,-22.5 + parent: 2 - uid: 16951 components: - type: Transform @@ -14002,10 +13920,10 @@ entities: parent: 2 - proto: BedsheetCE entities: - - uid: 936 + - uid: 3439 components: - type: Transform - pos: -13.5,-17.5 + pos: -4.5,-22.5 parent: 2 - proto: BedsheetClown entities: @@ -14197,30 +14115,15 @@ entities: parent: 2 - proto: BlastDoor entities: - - uid: 966 + - uid: 683 components: - type: Transform - pos: -1.5,-33.5 + pos: -15.5,-35.5 parent: 2 - - uid: 967 + - uid: 685 components: - type: Transform - pos: -9.5,-30.5 - parent: 2 - - uid: 968 - components: - - type: Transform - pos: -12.5,-30.5 - parent: 2 - - uid: 969 - components: - - type: Transform - pos: -17.5,-33.5 - parent: 2 - - uid: 970 - components: - - type: Transform - pos: -18.5,-33.5 + pos: -2.5,-38.5 parent: 2 - uid: 971 components: @@ -14232,11 +14135,6 @@ entities: - type: Transform pos: -27.5,-16.5 parent: 2 - - uid: 973 - components: - - type: Transform - pos: -2.5,-33.5 - parent: 2 - uid: 974 components: - type: Transform @@ -14257,26 +14155,6 @@ entities: - type: Transform pos: -27.5,-17.5 parent: 2 - - uid: 978 - components: - - type: Transform - pos: -15.5,-33.5 - parent: 2 - - uid: 979 - components: - - type: Transform - pos: -14.5,-33.5 - parent: 2 - - uid: 980 - components: - - type: Transform - pos: -15.5,-30.5 - parent: 2 - - uid: 981 - components: - - type: Transform - pos: -18.5,-30.5 - parent: 2 - uid: 982 components: - type: Transform @@ -14307,10 +14185,20 @@ entities: - type: Transform pos: -23.5,-16.5 parent: 2 - - uid: 988 + - uid: 2836 components: - type: Transform - pos: 0.5,-30.5 + pos: -5.5,-38.5 + parent: 2 + - uid: 2851 + components: + - type: Transform + pos: -11.5,-38.5 + parent: 2 + - uid: 2862 + components: + - type: Transform + pos: -8.5,-38.5 parent: 2 - uid: 16953 components: @@ -14366,31 +14254,11 @@ entities: parent: 2 - proto: BlastDoorOpen entities: - - uid: 993 - components: - - type: Transform - pos: -6.5,-19.5 - parent: 2 - - uid: 994 - components: - - type: Transform - pos: -6.5,-18.5 - parent: 2 - uid: 995 components: - type: Transform pos: 6.5,-18.5 parent: 2 - - uid: 996 - components: - - type: Transform - pos: -8.5,-19.5 - parent: 2 - - uid: 997 - components: - - type: Transform - pos: -8.5,-18.5 - parent: 2 - uid: 998 components: - type: Transform @@ -14406,21 +14274,6 @@ entities: - type: Transform pos: -4.5,-5.5 parent: 2 - - uid: 1001 - components: - - type: Transform - pos: -8.5,-32.5 - parent: 2 - - uid: 1002 - components: - - type: Transform - pos: -11.5,-32.5 - parent: 2 - - uid: 1003 - components: - - type: Transform - pos: 1.5,-32.5 - parent: 2 - uid: 1004 components: - type: Transform @@ -14436,6 +14289,47 @@ entities: - type: Transform pos: 59.5,-7.5 parent: 2 + - uid: 19388 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-33.5 + parent: 2 + - uid: 19389 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,-33.5 + parent: 2 + - uid: 19390 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-33.5 + parent: 2 + - uid: 19391 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-31.5 + parent: 2 + - uid: 19392 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-31.5 + parent: 2 + - uid: 19393 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,-31.5 + parent: 2 + - uid: 19394 + components: + - type: Transform + pos: -4.5,-32.5 + parent: 2 - proto: BlockGameArcade entities: - uid: 1007 @@ -14800,6 +14694,11 @@ entities: - type: Transform pos: 42.5,-6.5 parent: 2 + - uid: 10016 + components: + - type: Transform + pos: -12.5,-23.5 + parent: 2 - proto: BookSpaceLaw entities: - uid: 1025 @@ -14850,11 +14749,6 @@ entities: - type: Transform pos: -41.5,39.5 parent: 2 - - uid: 1082 - components: - - type: Transform - pos: -17.5,-23.5 - parent: 2 - proto: BoxBeaker entities: - uid: 1084 @@ -14871,10 +14765,10 @@ entities: parent: 2 - proto: BoxBeanbag entities: - - uid: 1086 + - uid: 19368 components: - type: Transform - pos: -10.510994,-23.445034 + pos: -54.328426,34.476143 parent: 2 - proto: BoxBodyBag entities: @@ -15370,11 +15264,13 @@ entities: rot: 1.5707963267948966 rad pos: 28.532946,2.5569396 parent: 2 - - uid: 1145 +- proto: BoxFolderYellowThreePapers + entities: + - uid: 7478 components: - type: Transform rot: 3.141592653589793 rad - pos: -13.605877,-23.453194 + pos: -9.848166,-23.38774 parent: 2 - proto: BoxForensicPad entities: @@ -15390,6 +15286,24 @@ entities: - type: Transform pos: 30.51166,-9.325979 parent: 2 +- proto: BoxInflatable + entities: + - uid: 12473 + components: + - type: Transform + parent: 12333 + - type: Physics + canCollide: False + - type: InsideEntityStorage + storage: 12333 + - uid: 12474 + components: + - type: Transform + parent: 12333 + - type: Physics + canCollide: False + - type: InsideEntityStorage + storage: 12333 - proto: BoxLatexGloves entities: - uid: 1148 @@ -15866,11 +15780,10 @@ entities: rot: 3.141592653589793 rad pos: -27.5,4.5 parent: 2 - - uid: 1226 + - uid: 2846 components: - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-24.5 + pos: -8.5,-33.5 parent: 2 - proto: ButtonFrameCautionSecurity entities: @@ -15958,11 +15871,6 @@ entities: - type: Transform pos: -74.5,17.5 parent: 2 - - uid: 1242 - components: - - type: Transform - pos: -10.5,-14.5 - parent: 2 - uid: 1243 components: - type: Transform @@ -16108,71 +16016,6 @@ entities: - type: Transform pos: -33.5,-27.5 parent: 2 - - uid: 1272 - components: - - type: Transform - pos: -8.5,-22.5 - parent: 2 - - uid: 1273 - components: - - type: Transform - pos: -7.5,-22.5 - parent: 2 - - uid: 1274 - components: - - type: Transform - pos: -6.5,-22.5 - parent: 2 - - uid: 1275 - components: - - type: Transform - pos: -6.5,-20.5 - parent: 2 - - uid: 1276 - components: - - type: Transform - pos: -9.5,-22.5 - parent: 2 - - uid: 1277 - components: - - type: Transform - pos: -5.5,-22.5 - parent: 2 - - uid: 1278 - components: - - type: Transform - pos: -7.5,-21.5 - parent: 2 - - uid: 1279 - components: - - type: Transform - pos: -7.5,-20.5 - parent: 2 - - uid: 1280 - components: - - type: Transform - pos: -7.5,-19.5 - parent: 2 - - uid: 1281 - components: - - type: Transform - pos: -7.5,-18.5 - parent: 2 - - uid: 1282 - components: - - type: Transform - pos: -9.5,-23.5 - parent: 2 - - uid: 1283 - components: - - type: Transform - pos: -5.5,-23.5 - parent: 2 - - uid: 1284 - components: - - type: Transform - pos: -7.5,-17.5 - parent: 2 - uid: 1285 components: - type: Transform @@ -17273,36 +17116,6 @@ entities: - type: Transform pos: -28.5,31.5 parent: 2 - - uid: 1505 - components: - - type: Transform - pos: -49.5,33.5 - parent: 2 - - uid: 1506 - components: - - type: Transform - pos: -51.5,33.5 - parent: 2 - - uid: 1507 - components: - - type: Transform - pos: -52.5,33.5 - parent: 2 - - uid: 1508 - components: - - type: Transform - pos: -50.5,33.5 - parent: 2 - - uid: 1509 - components: - - type: Transform - pos: -52.5,34.5 - parent: 2 - - uid: 1510 - components: - - type: Transform - pos: -50.5,34.5 - parent: 2 - uid: 1511 components: - type: Transform @@ -20238,11 +20051,6 @@ entities: - type: Transform pos: -25.5,17.5 parent: 2 - - uid: 2098 - components: - - type: Transform - pos: -19.5,27.5 - parent: 2 - uid: 2099 components: - type: Transform @@ -20251,7 +20059,7 @@ entities: - uid: 2100 components: - type: Transform - pos: -19.5,26.5 + pos: -17.5,26.5 parent: 2 - uid: 2101 components: @@ -23218,156 +23026,6 @@ entities: - type: Transform pos: -18.5,-12.5 parent: 2 - - uid: 2694 - components: - - type: Transform - pos: -18.5,-13.5 - parent: 2 - - uid: 2695 - components: - - type: Transform - pos: -17.5,-13.5 - parent: 2 - - uid: 2696 - components: - - type: Transform - pos: -16.5,-13.5 - parent: 2 - - uid: 2697 - components: - - type: Transform - pos: -15.5,-13.5 - parent: 2 - - uid: 2698 - components: - - type: Transform - pos: -13.5,-13.5 - parent: 2 - - uid: 2699 - components: - - type: Transform - pos: -14.5,-13.5 - parent: 2 - - uid: 2700 - components: - - type: Transform - pos: -18.5,-15.5 - parent: 2 - - uid: 2701 - components: - - type: Transform - pos: -16.5,-15.5 - parent: 2 - - uid: 2702 - components: - - type: Transform - pos: -15.5,-15.5 - parent: 2 - - uid: 2703 - components: - - type: Transform - pos: -17.5,-15.5 - parent: 2 - - uid: 2704 - components: - - type: Transform - pos: -14.5,-15.5 - parent: 2 - - uid: 2705 - components: - - type: Transform - pos: -13.5,-15.5 - parent: 2 - - uid: 2706 - components: - - type: Transform - pos: -18.5,-16.5 - parent: 2 - - uid: 2707 - components: - - type: Transform - pos: -18.5,-18.5 - parent: 2 - - uid: 2708 - components: - - type: Transform - pos: -18.5,-19.5 - parent: 2 - - uid: 2709 - components: - - type: Transform - pos: -18.5,-20.5 - parent: 2 - - uid: 2710 - components: - - type: Transform - pos: -18.5,-17.5 - parent: 2 - - uid: 2711 - components: - - type: Transform - pos: -19.5,-20.5 - parent: 2 - - uid: 2712 - components: - - type: Transform - pos: -17.5,-17.5 - parent: 2 - - uid: 2713 - components: - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - uid: 2714 - components: - - type: Transform - pos: -16.5,-18.5 - parent: 2 - - uid: 2715 - components: - - type: Transform - pos: -16.5,-20.5 - parent: 2 - - uid: 2716 - components: - - type: Transform - pos: -16.5,-21.5 - parent: 2 - - uid: 2717 - components: - - type: Transform - pos: -16.5,-22.5 - parent: 2 - - uid: 2718 - components: - - type: Transform - pos: -16.5,-23.5 - parent: 2 - - uid: 2719 - components: - - type: Transform - pos: -16.5,-19.5 - parent: 2 - - uid: 2720 - components: - - type: Transform - pos: -17.5,-23.5 - parent: 2 - - uid: 2721 - components: - - type: Transform - pos: -18.5,-23.5 - parent: 2 - - uid: 2722 - components: - - type: Transform - pos: -18.5,-22.5 - parent: 2 - - uid: 2723 - components: - - type: Transform - pos: -18.5,-21.5 - parent: 2 - uid: 2724 components: - type: Transform @@ -23598,26 +23256,6 @@ entities: - type: Transform pos: -21.5,-34.5 parent: 2 - - uid: 2770 - components: - - type: Transform - pos: -21.5,-35.5 - parent: 2 - - uid: 2771 - components: - - type: Transform - pos: -21.5,-37.5 - parent: 2 - - uid: 2772 - components: - - type: Transform - pos: -21.5,-38.5 - parent: 2 - - uid: 2773 - components: - - type: Transform - pos: -21.5,-36.5 - parent: 2 - uid: 2774 components: - type: Transform @@ -23783,256 +23421,26 @@ entities: - type: Transform pos: -15.5,-24.5 parent: 2 - - uid: 2807 - components: - - type: Transform - pos: -15.5,-26.5 - parent: 2 - uid: 2808 components: - type: Transform pos: -15.5,-25.5 parent: 2 - - uid: 2809 - components: - - type: Transform - pos: -18.5,-28.5 - parent: 2 - - uid: 2810 - components: - - type: Transform - pos: -5.5,-27.5 - parent: 2 - - uid: 2811 - components: - - type: Transform - pos: -5.5,-29.5 - parent: 2 - - uid: 2812 - components: - - type: Transform - pos: -5.5,-30.5 - parent: 2 - - uid: 2813 - components: - - type: Transform - pos: -5.5,-28.5 - parent: 2 - - uid: 2814 - components: - - type: Transform - pos: -18.5,-27.5 - parent: 2 - - uid: 2815 - components: - - type: Transform - pos: -18.5,-29.5 - parent: 2 - - uid: 2816 - components: - - type: Transform - pos: -18.5,-30.5 - parent: 2 - - uid: 2817 - components: - - type: Transform - pos: -18.5,-31.5 - parent: 2 - - uid: 2818 - components: - - type: Transform - pos: -18.5,-32.5 - parent: 2 - - uid: 2819 - components: - - type: Transform - pos: -17.5,-32.5 - parent: 2 - - uid: 2820 - components: - - type: Transform - pos: -15.5,-27.5 - parent: 2 - - uid: 2821 - components: - - type: Transform - pos: -15.5,-29.5 - parent: 2 - - uid: 2822 - components: - - type: Transform - pos: -15.5,-30.5 - parent: 2 - - uid: 2823 - components: - - type: Transform - pos: -15.5,-31.5 - parent: 2 - - uid: 2824 - components: - - type: Transform - pos: -15.5,-28.5 - parent: 2 - - uid: 2825 - components: - - type: Transform - pos: -15.5,-32.5 - parent: 2 - - uid: 2826 - components: - - type: Transform - pos: -14.5,-32.5 - parent: 2 - - uid: 2827 - components: - - type: Transform - pos: -12.5,-27.5 - parent: 2 - - uid: 2828 - components: - - type: Transform - pos: -12.5,-29.5 - parent: 2 - - uid: 2829 - components: - - type: Transform - pos: -12.5,-30.5 - parent: 2 - - uid: 2830 - components: - - type: Transform - pos: -12.5,-31.5 - parent: 2 - - uid: 2831 - components: - - type: Transform - pos: -12.5,-32.5 - parent: 2 - - uid: 2832 - components: - - type: Transform - pos: -12.5,-28.5 - parent: 2 - - uid: 2833 - components: - - type: Transform - pos: -9.5,-27.5 - parent: 2 - - uid: 2834 - components: - - type: Transform - pos: -9.5,-29.5 - parent: 2 - - uid: 2835 - components: - - type: Transform - pos: -9.5,-30.5 - parent: 2 - - uid: 2836 - components: - - type: Transform - pos: -9.5,-31.5 - parent: 2 - - uid: 2837 - components: - - type: Transform - pos: -9.5,-32.5 - parent: 2 - - uid: 2838 - components: - - type: Transform - pos: -9.5,-28.5 - parent: 2 - - uid: 2839 - components: - - type: Transform - pos: -5.5,-31.5 - parent: 2 - - uid: 2840 - components: - - type: Transform - pos: -4.5,-27.5 - parent: 2 - - uid: 2841 - components: - - type: Transform - pos: -2.5,-27.5 - parent: 2 - - uid: 2842 - components: - - type: Transform - pos: -1.5,-27.5 - parent: 2 - - uid: 2843 - components: - - type: Transform - pos: -3.5,-27.5 - parent: 2 - - uid: 2844 - components: - - type: Transform - pos: -1.5,-26.5 - parent: 2 - - uid: 2845 - components: - - type: Transform - pos: 0.5,-26.5 - parent: 2 - - uid: 2846 - components: - - type: Transform - pos: 1.5,-26.5 - parent: 2 - uid: 2847 components: - type: Transform pos: 2.5,-26.5 parent: 2 - - uid: 2848 - components: - - type: Transform - pos: -0.5,-26.5 - parent: 2 - uid: 2849 components: - type: Transform pos: 3.5,-26.5 parent: 2 - - uid: 2850 - components: - - type: Transform - pos: -1.5,-28.5 - parent: 2 - - uid: 2851 - components: - - type: Transform - pos: -1.5,-29.5 - parent: 2 - - uid: 2852 - components: - - type: Transform - pos: -0.5,-29.5 - parent: 2 - - uid: 2853 - components: - - type: Transform - pos: 1.5,-29.5 - parent: 2 - - uid: 2854 - components: - - type: Transform - pos: 2.5,-29.5 - parent: 2 - uid: 2855 components: - type: Transform pos: 3.5,-29.5 parent: 2 - - uid: 2856 - components: - - type: Transform - pos: 0.5,-29.5 - parent: 2 - uid: 2857 components: - type: Transform @@ -24053,21 +23461,6 @@ entities: - type: Transform pos: 54.5,-8.5 parent: 2 - - uid: 2861 - components: - - type: Transform - pos: -1.5,-31.5 - parent: 2 - - uid: 2862 - components: - - type: Transform - pos: -1.5,-32.5 - parent: 2 - - uid: 2863 - components: - - type: Transform - pos: -1.5,-30.5 - parent: 2 - uid: 2864 components: - type: Transform @@ -25173,11 +24566,6 @@ entities: - type: Transform pos: 7.5,-29.5 parent: 2 - - uid: 3085 - components: - - type: Transform - pos: -11.5,-27.5 - parent: 2 - uid: 3086 components: - type: Transform @@ -25188,21 +24576,6 @@ entities: - type: Transform pos: 7.5,-26.5 parent: 2 - - uid: 3088 - components: - - type: Transform - pos: -13.5,-27.5 - parent: 2 - - uid: 3089 - components: - - type: Transform - pos: -8.5,-27.5 - parent: 2 - - uid: 3090 - components: - - type: Transform - pos: -11.5,-21.5 - parent: 2 - uid: 3091 components: - type: Transform @@ -25228,71 +24601,6 @@ entities: - type: Transform pos: -46.5,50.5 parent: 2 - - uid: 3096 - components: - - type: Transform - pos: -7.5,-27.5 - parent: 2 - - uid: 3097 - components: - - type: Transform - pos: -14.5,-27.5 - parent: 2 - - uid: 3098 - components: - - type: Transform - pos: -10.5,-27.5 - parent: 2 - - uid: 3099 - components: - - type: Transform - pos: -17.5,-27.5 - parent: 2 - - uid: 3100 - components: - - type: Transform - pos: -16.5,-27.5 - parent: 2 - - uid: 3101 - components: - - type: Transform - pos: -13.5,-21.5 - parent: 2 - - uid: 3102 - components: - - type: Transform - pos: -12.5,-21.5 - parent: 2 - - uid: 3103 - components: - - type: Transform - pos: -13.5,-20.5 - parent: 2 - - uid: 3104 - components: - - type: Transform - pos: -13.5,-18.5 - parent: 2 - - uid: 3105 - components: - - type: Transform - pos: -13.5,-17.5 - parent: 2 - - uid: 3106 - components: - - type: Transform - pos: -13.5,-19.5 - parent: 2 - - uid: 3107 - components: - - type: Transform - pos: -13.5,-22.5 - parent: 2 - - uid: 3108 - components: - - type: Transform - pos: -6.5,-27.5 - parent: 2 - uid: 3109 components: - type: Transform @@ -26043,6 +25351,176 @@ entities: - type: Transform pos: -26.5,9.5 parent: 2 + - uid: 4403 + components: + - type: Transform + pos: -17.5,27.5 + parent: 2 + - uid: 7032 + components: + - type: Transform + pos: -52.5,34.5 + parent: 2 + - uid: 7033 + components: + - type: Transform + pos: -52.5,33.5 + parent: 2 + - uid: 8209 + components: + - type: Transform + pos: -54.5,34.5 + parent: 2 + - uid: 8210 + components: + - type: Transform + pos: -52.5,32.5 + parent: 2 + - uid: 8211 + components: + - type: Transform + pos: -55.5,34.5 + parent: 2 + - uid: 8212 + components: + - type: Transform + pos: -53.5,35.5 + parent: 2 + - uid: 8253 + components: + - type: Transform + pos: -53.5,34.5 + parent: 2 + - uid: 14297 + components: + - type: Transform + pos: -8.5,-24.5 + parent: 2 + - uid: 14302 + components: + - type: Transform + pos: -8.5,-23.5 + parent: 2 + - uid: 14303 + components: + - type: Transform + pos: -8.5,-22.5 + parent: 2 + - uid: 14304 + components: + - type: Transform + pos: -9.5,-22.5 + parent: 2 + - uid: 14305 + components: + - type: Transform + pos: -10.5,-22.5 + parent: 2 + - uid: 14325 + components: + - type: Transform + pos: -7.5,-22.5 + parent: 2 + - uid: 14326 + components: + - type: Transform + pos: -6.5,-22.5 + parent: 2 + - uid: 14768 + components: + - type: Transform + pos: -15.5,-20.5 + parent: 2 + - uid: 15436 + components: + - type: Transform + pos: -16.5,-25.5 + parent: 2 + - uid: 15623 + components: + - type: Transform + pos: -16.5,-20.5 + parent: 2 + - uid: 15624 + components: + - type: Transform + pos: -17.5,-20.5 + parent: 2 + - uid: 15625 + components: + - type: Transform + pos: -17.5,-21.5 + parent: 2 + - uid: 15626 + components: + - type: Transform + pos: -17.5,-22.5 + parent: 2 + - uid: 15627 + components: + - type: Transform + pos: -16.5,-22.5 + parent: 2 + - uid: 15680 + components: + - type: Transform + pos: -15.5,-22.5 + parent: 2 + - uid: 15941 + components: + - type: Transform + pos: -14.5,-22.5 + parent: 2 + - uid: 15942 + components: + - type: Transform + pos: -13.5,-22.5 + parent: 2 + - uid: 16306 + components: + - type: Transform + pos: -17.5,-19.5 + parent: 2 + - uid: 16357 + components: + - type: Transform + pos: -17.5,-18.5 + parent: 2 + - uid: 16358 + components: + - type: Transform + pos: -16.5,-18.5 + parent: 2 + - uid: 16466 + components: + - type: Transform + pos: -15.5,-18.5 + parent: 2 + - uid: 16516 + components: + - type: Transform + pos: -17.5,-25.5 + parent: 2 + - uid: 16544 + components: + - type: Transform + pos: -14.5,-18.5 + parent: 2 + - uid: 16569 + components: + - type: Transform + pos: -13.5,-18.5 + parent: 2 + - uid: 16570 + components: + - type: Transform + pos: -12.5,-18.5 + parent: 2 + - uid: 16571 + components: + - type: Transform + pos: -12.5,-17.5 + parent: 2 - uid: 16980 components: - type: Transform @@ -26933,6 +26411,526 @@ entities: - type: Transform pos: 11.5,20.5 parent: 16911 + - uid: 19378 + components: + - type: Transform + pos: -11.5,-17.5 + parent: 2 + - uid: 19379 + components: + - type: Transform + pos: -10.5,-17.5 + parent: 2 + - uid: 19380 + components: + - type: Transform + pos: -5.5,-15.5 + parent: 2 + - uid: 19381 + components: + - type: Transform + pos: -5.5,-16.5 + parent: 2 + - uid: 19382 + components: + - type: Transform + pos: -5.5,-17.5 + parent: 2 + - uid: 19395 + components: + - type: Transform + pos: -17.5,-26.5 + parent: 2 + - uid: 19396 + components: + - type: Transform + pos: -17.5,-27.5 + parent: 2 + - uid: 19397 + components: + - type: Transform + pos: -17.5,-28.5 + parent: 2 + - uid: 19398 + components: + - type: Transform + pos: -17.5,-29.5 + parent: 2 + - uid: 19399 + components: + - type: Transform + pos: -17.5,-30.5 + parent: 2 + - uid: 19400 + components: + - type: Transform + pos: -17.5,-31.5 + parent: 2 + - uid: 19401 + components: + - type: Transform + pos: -17.5,-32.5 + parent: 2 + - uid: 19402 + components: + - type: Transform + pos: -16.5,-30.5 + parent: 2 + - uid: 19403 + components: + - type: Transform + pos: -15.5,-30.5 + parent: 2 + - uid: 19404 + components: + - type: Transform + pos: -14.5,-30.5 + parent: 2 + - uid: 19405 + components: + - type: Transform + pos: -14.5,-31.5 + parent: 2 + - uid: 19406 + components: + - type: Transform + pos: -14.5,-32.5 + parent: 2 + - uid: 19407 + components: + - type: Transform + pos: -14.5,-33.5 + parent: 2 + - uid: 19408 + components: + - type: Transform + pos: -13.5,-33.5 + parent: 2 + - uid: 19409 + components: + - type: Transform + pos: -12.5,-33.5 + parent: 2 + - uid: 19410 + components: + - type: Transform + pos: -12.5,-34.5 + parent: 2 + - uid: 19411 + components: + - type: Transform + pos: -12.5,-35.5 + parent: 2 + - uid: 19412 + components: + - type: Transform + pos: -12.5,-36.5 + parent: 2 + - uid: 19413 + components: + - type: Transform + pos: -11.5,-36.5 + parent: 2 + - uid: 19414 + components: + - type: Transform + pos: -10.5,-36.5 + parent: 2 + - uid: 19415 + components: + - type: Transform + pos: -9.5,-36.5 + parent: 2 + - uid: 19416 + components: + - type: Transform + pos: -8.5,-36.5 + parent: 2 + - uid: 19417 + components: + - type: Transform + pos: -7.5,-36.5 + parent: 2 + - uid: 19418 + components: + - type: Transform + pos: -4.5,-36.5 + parent: 2 + - uid: 19419 + components: + - type: Transform + pos: -5.5,-36.5 + parent: 2 + - uid: 19420 + components: + - type: Transform + pos: -3.5,-36.5 + parent: 2 + - uid: 19421 + components: + - type: Transform + pos: -2.5,-36.5 + parent: 2 + - uid: 19422 + components: + - type: Transform + pos: -6.5,-36.5 + parent: 2 + - uid: 19423 + components: + - type: Transform + pos: -1.5,-36.5 + parent: 2 + - uid: 19424 + components: + - type: Transform + pos: -0.5,-36.5 + parent: 2 + - uid: 19425 + components: + - type: Transform + pos: 0.5,-36.5 + parent: 2 + - uid: 19426 + components: + - type: Transform + pos: -14.5,-34.5 + parent: 2 + - uid: 19427 + components: + - type: Transform + pos: -14.5,-35.5 + parent: 2 + - uid: 19428 + components: + - type: Transform + pos: -14.5,-36.5 + parent: 2 + - uid: 19429 + components: + - type: Transform + pos: -10.5,-37.5 + parent: 2 + - uid: 19430 + components: + - type: Transform + pos: -10.5,-38.5 + parent: 2 + - uid: 19431 + components: + - type: Transform + pos: -10.5,-39.5 + parent: 2 + - uid: 19432 + components: + - type: Transform + pos: -7.5,-37.5 + parent: 2 + - uid: 19433 + components: + - type: Transform + pos: -7.5,-38.5 + parent: 2 + - uid: 19434 + components: + - type: Transform + pos: -7.5,-39.5 + parent: 2 + - uid: 19435 + components: + - type: Transform + pos: -4.5,-37.5 + parent: 2 + - uid: 19436 + components: + - type: Transform + pos: -4.5,-38.5 + parent: 2 + - uid: 19437 + components: + - type: Transform + pos: -4.5,-39.5 + parent: 2 + - uid: 19438 + components: + - type: Transform + pos: -1.5,-37.5 + parent: 2 + - uid: 19439 + components: + - type: Transform + pos: -1.5,-38.5 + parent: 2 + - uid: 19440 + components: + - type: Transform + pos: -1.5,-39.5 + parent: 2 + - uid: 19441 + components: + - type: Transform + pos: -11.5,-33.5 + parent: 2 + - uid: 19442 + components: + - type: Transform + pos: -10.5,-33.5 + parent: 2 + - uid: 19443 + components: + - type: Transform + pos: -9.5,-33.5 + parent: 2 + - uid: 19444 + components: + - type: Transform + pos: -9.5,-32.5 + parent: 2 + - uid: 19445 + components: + - type: Transform + pos: -8.5,-32.5 + parent: 2 + - uid: 19446 + components: + - type: Transform + pos: -14.5,-29.5 + parent: 2 + - uid: 19447 + components: + - type: Transform + pos: -14.5,-28.5 + parent: 2 + - uid: 19448 + components: + - type: Transform + pos: -13.5,-28.5 + parent: 2 + - uid: 19449 + components: + - type: Transform + pos: -12.5,-28.5 + parent: 2 + - uid: 19450 + components: + - type: Transform + pos: -12.5,-27.5 + parent: 2 + - uid: 19451 + components: + - type: Transform + pos: -11.5,-28.5 + parent: 2 + - uid: 19452 + components: + - type: Transform + pos: -10.5,-28.5 + parent: 2 + - uid: 19453 + components: + - type: Transform + pos: -9.5,-28.5 + parent: 2 + - uid: 19454 + components: + - type: Transform + pos: -8.5,-28.5 + parent: 2 + - uid: 19455 + components: + - type: Transform + pos: -7.5,-28.5 + parent: 2 + - uid: 19456 + components: + - type: Transform + pos: -6.5,-28.5 + parent: 2 + - uid: 19457 + components: + - type: Transform + pos: -5.5,-28.5 + parent: 2 + - uid: 19458 + components: + - type: Transform + pos: -4.5,-28.5 + parent: 2 + - uid: 19459 + components: + - type: Transform + pos: -3.5,-28.5 + parent: 2 + - uid: 19460 + components: + - type: Transform + pos: -2.5,-28.5 + parent: 2 + - uid: 19488 + components: + - type: Transform + pos: 1.5,-24.5 + parent: 2 + - uid: 19489 + components: + - type: Transform + pos: 1.5,-25.5 + parent: 2 + - uid: 19490 + components: + - type: Transform + pos: 1.5,-26.5 + parent: 2 + - uid: 19491 + components: + - type: Transform + pos: 2.5,-27.5 + parent: 2 + - uid: 19492 + components: + - type: Transform + pos: 2.5,-28.5 + parent: 2 + - uid: 19493 + components: + - type: Transform + pos: 2.5,-29.5 + parent: 2 + - uid: 19507 + components: + - type: Transform + pos: 15.5,-27.5 + parent: 2 + - uid: 19508 + components: + - type: Transform + pos: 15.5,-28.5 + parent: 2 + - uid: 19509 + components: + - type: Transform + pos: 16.5,-28.5 + parent: 2 + - uid: 19510 + components: + - type: Transform + pos: 16.5,-30.5 + parent: 2 + - uid: 19511 + components: + - type: Transform + pos: 16.5,-29.5 + parent: 2 + - uid: 19512 + components: + - type: Transform + pos: 16.5,-31.5 + parent: 2 + - uid: 19513 + components: + - type: Transform + pos: 17.5,-31.5 + parent: 2 + - uid: 19514 + components: + - type: Transform + pos: 18.5,-31.5 + parent: 2 + - uid: 19515 + components: + - type: Transform + pos: 19.5,-31.5 + parent: 2 + - uid: 19516 + components: + - type: Transform + pos: 17.5,-29.5 + parent: 2 + - uid: 19517 + components: + - type: Transform + pos: 18.5,-29.5 + parent: 2 + - uid: 19518 + components: + - type: Transform + pos: 19.5,-29.5 + parent: 2 + - uid: 19530 + components: + - type: Transform + pos: -21.5,-36.5 + parent: 2 + - uid: 19531 + components: + - type: Transform + pos: -21.5,-35.5 + parent: 2 + - uid: 19532 + components: + - type: Transform + pos: -20.5,-36.5 + parent: 2 + - uid: 19533 + components: + - type: Transform + pos: -19.5,-36.5 + parent: 2 + - uid: 19534 + components: + - type: Transform + pos: -18.5,-36.5 + parent: 2 + - uid: 19535 + components: + - type: Transform + pos: -20.5,-37.5 + parent: 2 + - uid: 19536 + components: + - type: Transform + pos: -20.5,-38.5 + parent: 2 + - uid: 19537 + components: + - type: Transform + pos: -20.5,-39.5 + parent: 2 + - uid: 19538 + components: + - type: Transform + pos: -20.5,-40.5 + parent: 2 + - uid: 19539 + components: + - type: Transform + pos: -19.5,-40.5 + parent: 2 + - uid: 19540 + components: + - type: Transform + pos: -18.5,-40.5 + parent: 2 + - uid: 19541 + components: + - type: Transform + pos: -17.5,-40.5 + parent: 2 + - uid: 19542 + components: + - type: Transform + pos: -16.5,-40.5 + parent: 2 + - uid: 19543 + components: + - type: Transform + pos: -15.5,-40.5 + parent: 2 + - uid: 19544 + components: + - type: Transform + pos: -14.5,-40.5 + parent: 2 - proto: CableApcStack entities: - uid: 3259 @@ -27003,6 +27001,76 @@ entities: parent: 2 - proto: CableHV entities: + - uid: 10 + components: + - type: Transform + pos: -16.5,-17.5 + parent: 2 + - uid: 177 + components: + - type: Transform + pos: -15.5,-28.5 + parent: 2 + - uid: 916 + components: + - type: Transform + pos: -9.5,-17.5 + parent: 2 + - uid: 936 + components: + - type: Transform + pos: -15.5,-17.5 + parent: 2 + - uid: 1086 + components: + - type: Transform + pos: -12.5,-28.5 + parent: 2 + - uid: 1145 + components: + - type: Transform + pos: -17.5,-28.5 + parent: 2 + - uid: 1272 + components: + - type: Transform + pos: -8.5,-28.5 + parent: 2 + - uid: 1278 + components: + - type: Transform + pos: -11.5,-28.5 + parent: 2 + - uid: 3090 + components: + - type: Transform + pos: -11.5,-17.5 + parent: 2 + - uid: 3101 + components: + - type: Transform + pos: -8.5,-17.5 + parent: 2 + - uid: 3102 + components: + - type: Transform + pos: -12.5,-17.5 + parent: 2 + - uid: 3105 + components: + - type: Transform + pos: -14.5,-28.5 + parent: 2 + - uid: 3106 + components: + - type: Transform + pos: -17.5,-19.5 + parent: 2 + - uid: 3107 + components: + - type: Transform + pos: -13.5,-28.5 + parent: 2 - uid: 3271 components: - type: Transform @@ -27053,16 +27121,6 @@ entities: - type: Transform pos: -26.5,-22.5 parent: 2 - - uid: 3281 - components: - - type: Transform - pos: -7.5,-18.5 - parent: 2 - - uid: 3282 - components: - - type: Transform - pos: -7.5,-16.5 - parent: 2 - uid: 3283 components: - type: Transform @@ -27103,21 +27161,6 @@ entities: - type: Transform pos: -35.5,-16.5 parent: 2 - - uid: 3291 - components: - - type: Transform - pos: -7.5,-17.5 - parent: 2 - - uid: 3292 - components: - - type: Transform - pos: -7.5,-19.5 - parent: 2 - - uid: 3293 - components: - - type: Transform - pos: -7.5,-20.5 - parent: 2 - uid: 3294 components: - type: Transform @@ -27573,26 +27616,11 @@ entities: - type: Transform pos: -26.5,-30.5 parent: 2 - - uid: 3385 - components: - - type: Transform - pos: -7.5,-23.5 - parent: 2 - - uid: 3386 - components: - - type: Transform - pos: -7.5,-21.5 - parent: 2 - uid: 3387 components: - type: Transform pos: -35.5,-26.5 parent: 2 - - uid: 3388 - components: - - type: Transform - pos: -7.5,-22.5 - parent: 2 - uid: 3389 components: - type: Transform @@ -27678,25 +27706,15 @@ entities: - type: Transform pos: -20.5,-23.5 parent: 2 - - uid: 3406 - components: - - type: Transform - pos: -19.5,-40.5 - parent: 2 - - uid: 3407 - components: - - type: Transform - pos: -7.5,-24.5 - parent: 2 - uid: 3408 components: - type: Transform - pos: -45.5,1.5 + pos: -17.5,-21.5 parent: 2 - uid: 3409 components: - type: Transform - pos: -44.5,1.5 + pos: -9.5,-28.5 parent: 2 - uid: 3410 components: @@ -27711,82 +27729,42 @@ entities: - uid: 3412 components: - type: Transform - pos: -39.5,1.5 + pos: -5.5,-28.5 parent: 2 - uid: 3413 components: - type: Transform - pos: -41.5,1.5 + pos: -17.5,-20.5 parent: 2 - uid: 3414 components: - type: Transform - pos: -8.5,-24.5 + pos: -17.5,-17.5 parent: 2 - uid: 3415 components: - type: Transform - pos: -9.5,-24.5 + pos: -17.5,-22.5 parent: 2 - uid: 3416 components: - type: Transform - pos: -6.5,-24.5 + pos: -17.5,-18.5 parent: 2 - uid: 3417 components: - type: Transform - pos: -5.5,-24.5 - parent: 2 - - uid: 3418 - components: - - type: Transform - pos: -8.5,-21.5 + pos: -7.5,-28.5 parent: 2 - uid: 3419 components: - type: Transform - pos: -5.5,-23.5 + pos: -6.5,-28.5 parent: 2 - uid: 3420 components: - type: Transform - pos: -4.5,-23.5 - parent: 2 - - uid: 3421 - components: - - type: Transform - pos: -9.5,-21.5 - parent: 2 - - uid: 3422 - components: - - type: Transform - pos: -9.5,-20.5 - parent: 2 - - uid: 3423 - components: - - type: Transform - pos: -10.5,-20.5 - parent: 2 - - uid: 3424 - components: - - type: Transform - pos: -6.5,-21.5 - parent: 2 - - uid: 3425 - components: - - type: Transform - pos: -5.5,-21.5 - parent: 2 - - uid: 3426 - components: - - type: Transform - pos: -5.5,-20.5 - parent: 2 - - uid: 3427 - components: - - type: Transform - pos: -4.5,-20.5 + pos: -8.5,-16.5 parent: 2 - uid: 3428 components: @@ -27798,87 +27776,7 @@ entities: - type: Transform pos: -8.5,-15.5 parent: 2 - - uid: 3430 - components: - - type: Transform - pos: -9.5,-15.5 - parent: 2 - - uid: 3431 - components: - - type: Transform - pos: -10.5,-15.5 - parent: 2 - - uid: 3432 - components: - - type: Transform - pos: -11.5,-15.5 - parent: 2 - - uid: 3433 - components: - - type: Transform - pos: -12.5,-15.5 - parent: 2 - - uid: 3434 - components: - - type: Transform - pos: -13.5,-15.5 - parent: 2 - uid: 3435 - components: - - type: Transform - pos: -14.5,-15.5 - parent: 2 - - uid: 3436 - components: - - type: Transform - pos: -15.5,-15.5 - parent: 2 - - uid: 3437 - components: - - type: Transform - pos: -16.5,-15.5 - parent: 2 - - uid: 3438 - components: - - type: Transform - pos: -16.5,-16.5 - parent: 2 - - uid: 3439 - components: - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - uid: 3440 - components: - - type: Transform - pos: -17.5,-17.5 - parent: 2 - - uid: 3441 - components: - - type: Transform - pos: -17.5,-18.5 - parent: 2 - - uid: 3442 - components: - - type: Transform - pos: -17.5,-19.5 - parent: 2 - - uid: 3443 - components: - - type: Transform - pos: -17.5,-20.5 - parent: 2 - - uid: 3444 - components: - - type: Transform - pos: -17.5,-21.5 - parent: 2 - - uid: 3445 - components: - - type: Transform - pos: -17.5,-22.5 - parent: 2 - - uid: 3446 components: - type: Transform pos: -17.5,-23.5 @@ -27898,31 +27796,11 @@ entities: - type: Transform pos: -20.5,-31.5 parent: 2 - - uid: 3450 - components: - - type: Transform - pos: -18.5,-24.5 - parent: 2 - - uid: 3451 - components: - - type: Transform - pos: -16.5,-23.5 - parent: 2 - uid: 3452 components: - type: Transform pos: -16.5,-24.5 parent: 2 - - uid: 3453 - components: - - type: Transform - pos: -37.5,1.5 - parent: 2 - - uid: 3454 - components: - - type: Transform - pos: -40.5,1.5 - parent: 2 - uid: 3455 components: - type: Transform @@ -28338,11 +28216,6 @@ entities: - type: Transform pos: -46.5,0.5 parent: 2 - - uid: 3538 - components: - - type: Transform - pos: -46.5,1.5 - parent: 2 - uid: 3539 components: - type: Transform @@ -28398,11 +28271,6 @@ entities: - type: Transform pos: -43.5,-1.5 parent: 2 - - uid: 3550 - components: - - type: Transform - pos: -42.5,1.5 - parent: 2 - uid: 3551 components: - type: Transform @@ -28433,11 +28301,6 @@ entities: - type: Transform pos: -38.5,0.5 parent: 2 - - uid: 3557 - components: - - type: Transform - pos: -38.5,1.5 - parent: 2 - uid: 3558 components: - type: Transform @@ -28508,11 +28371,6 @@ entities: - type: Transform pos: -34.5,-0.5 parent: 2 - - uid: 3572 - components: - - type: Transform - pos: -34.5,1.5 - parent: 2 - uid: 3573 components: - type: Transform @@ -28633,11 +28491,6 @@ entities: - type: Transform pos: -41.5,-2.5 parent: 2 - - uid: 3597 - components: - - type: Transform - pos: -43.5,1.5 - parent: 2 - uid: 3598 components: - type: Transform @@ -28753,11 +28606,6 @@ entities: - type: Transform pos: 14.5,-44.5 parent: 2 - - uid: 3621 - components: - - type: Transform - pos: 14.5,-42.5 - parent: 2 - uid: 3622 components: - type: Transform @@ -28863,16 +28711,6 @@ entities: - type: Transform pos: 10.5,-43.5 parent: 2 - - uid: 3643 - components: - - type: Transform - pos: 10.5,-42.5 - parent: 2 - - uid: 3644 - components: - - type: Transform - pos: 9.5,-42.5 - parent: 2 - uid: 3645 components: - type: Transform @@ -28913,21 +28751,6 @@ entities: - type: Transform pos: 18.5,-43.5 parent: 2 - - uid: 3653 - components: - - type: Transform - pos: 18.5,-42.5 - parent: 2 - - uid: 3654 - components: - - type: Transform - pos: 17.5,-42.5 - parent: 2 - - uid: 3655 - components: - - type: Transform - pos: 19.5,-42.5 - parent: 2 - uid: 3656 components: - type: Transform @@ -30528,31 +30351,6 @@ entities: - type: Transform pos: -28.5,2.5 parent: 2 - - uid: 3976 - components: - - type: Transform - pos: -19.5,-41.5 - parent: 2 - - uid: 3977 - components: - - type: Transform - pos: -18.5,-40.5 - parent: 2 - - uid: 3978 - components: - - type: Transform - pos: -35.5,1.5 - parent: 2 - - uid: 3979 - components: - - type: Transform - pos: -33.5,1.5 - parent: 2 - - uid: 3980 - components: - - type: Transform - pos: -36.5,1.5 - parent: 2 - uid: 3981 components: - type: Transform @@ -30641,17 +30439,17 @@ entities: - uid: 3998 components: - type: Transform - pos: 8.5,-42.5 + pos: -3.5,-28.5 parent: 2 - uid: 3999 components: - type: Transform - pos: 7.5,-42.5 + pos: -4.5,-28.5 parent: 2 - uid: 4000 components: - type: Transform - pos: 6.5,-42.5 + pos: -18.5,-27.5 parent: 2 - uid: 4001 components: @@ -30703,11 +30501,6 @@ entities: - type: Transform pos: -31.5,-35.5 parent: 2 - - uid: 4011 - components: - - type: Transform - pos: 20.5,-42.5 - parent: 2 - uid: 4012 components: - type: Transform @@ -30716,27 +30509,17 @@ entities: - uid: 4013 components: - type: Transform - pos: 16.5,-42.5 + pos: -17.5,-27.5 parent: 2 - uid: 4014 components: - type: Transform - pos: 15.5,-42.5 + pos: -10.5,-28.5 parent: 2 - uid: 4015 components: - type: Transform - pos: 13.5,-42.5 - parent: 2 - - uid: 4016 - components: - - type: Transform - pos: 12.5,-42.5 - parent: 2 - - uid: 4017 - components: - - type: Transform - pos: 11.5,-42.5 + pos: -16.5,-28.5 parent: 2 - uid: 4018 components: @@ -30748,11 +30531,6 @@ entities: - type: Transform pos: -30.5,1.5 parent: 2 - - uid: 4020 - components: - - type: Transform - pos: -32.5,1.5 - parent: 2 - uid: 4021 components: - type: Transform @@ -30778,76 +30556,6 @@ entities: - type: Transform pos: -22.5,-19.5 parent: 2 - - uid: 4026 - components: - - type: Transform - pos: -20.5,-43.5 - parent: 2 - - uid: 4027 - components: - - type: Transform - pos: -20.5,-44.5 - parent: 2 - - uid: 4028 - components: - - type: Transform - pos: -19.5,-39.5 - parent: 2 - - uid: 4029 - components: - - type: Transform - pos: -20.5,-45.5 - parent: 2 - - uid: 4030 - components: - - type: Transform - pos: -19.5,-44.5 - parent: 2 - - uid: 4031 - components: - - type: Transform - pos: -19.5,-45.5 - parent: 2 - - uid: 4032 - components: - - type: Transform - pos: -18.5,-43.5 - parent: 2 - - uid: 4033 - components: - - type: Transform - pos: -18.5,-44.5 - parent: 2 - - uid: 4034 - components: - - type: Transform - pos: -19.5,-43.5 - parent: 2 - - uid: 4035 - components: - - type: Transform - pos: -18.5,-45.5 - parent: 2 - - uid: 4036 - components: - - type: Transform - pos: -20.5,-39.5 - parent: 2 - - uid: 4037 - components: - - type: Transform - pos: -19.5,-42.5 - parent: 2 - - uid: 4038 - components: - - type: Transform - pos: -18.5,-39.5 - parent: 2 - - uid: 4039 - components: - - type: Transform - pos: -18.5,-41.5 - parent: 2 - uid: 4040 components: - type: Transform @@ -30868,21 +30576,11 @@ entities: - type: Transform pos: -23.5,-40.5 parent: 2 - - uid: 4044 - components: - - type: Transform - pos: -21.5,-40.5 - parent: 2 - uid: 4045 components: - type: Transform pos: -24.5,-38.5 parent: 2 - - uid: 4046 - components: - - type: Transform - pos: -20.5,-40.5 - parent: 2 - uid: 4047 components: - type: Transform @@ -30903,6 +30601,146 @@ entities: - type: Transform pos: -12.5,5.5 parent: 2 + - uid: 4404 + components: + - type: Transform + pos: 27.5,18.5 + parent: 2 + - uid: 4946 + components: + - type: Transform + pos: -19.5,-27.5 + parent: 2 + - uid: 7172 + components: + - type: Transform + pos: -26.5,-7.5 + parent: 2 + - uid: 7173 + components: + - type: Transform + pos: -20.5,28.5 + parent: 2 + - uid: 8169 + components: + - type: Transform + pos: -13.5,-17.5 + parent: 2 + - uid: 9502 + components: + - type: Transform + pos: -10.5,-17.5 + parent: 2 + - uid: 10695 + components: + - type: Transform + pos: -13.5,-39.5 + parent: 2 + - uid: 10696 + components: + - type: Transform + pos: -14.5,-39.5 + parent: 2 + - uid: 10748 + components: + - type: Transform + pos: -15.5,-39.5 + parent: 2 + - uid: 10750 + components: + - type: Transform + pos: -14.5,-40.5 + parent: 2 + - uid: 10951 + components: + - type: Transform + pos: -13.5,-41.5 + parent: 2 + - uid: 10967 + components: + - type: Transform + pos: -14.5,-41.5 + parent: 2 + - uid: 10968 + components: + - type: Transform + pos: -15.5,-41.5 + parent: 2 + - uid: 10969 + components: + - type: Transform + pos: -16.5,-41.5 + parent: 2 + - uid: 10970 + components: + - type: Transform + pos: -16.5,-40.5 + parent: 2 + - uid: 10971 + components: + - type: Transform + pos: -16.5,-39.5 + parent: 2 + - uid: 10972 + components: + - type: Transform + pos: -17.5,-40.5 + parent: 2 + - uid: 10973 + components: + - type: Transform + pos: -18.5,-40.5 + parent: 2 + - uid: 11309 + components: + - type: Transform + pos: -14.5,-17.5 + parent: 2 + - uid: 11569 + components: + - type: Transform + pos: 0.5,-27.5 + parent: 2 + - uid: 11614 + components: + - type: Transform + pos: -0.5,-27.5 + parent: 2 + - uid: 11615 + components: + - type: Transform + pos: -2.5,-28.5 + parent: 2 + - uid: 12419 + components: + - type: Transform + pos: -1.5,-27.5 + parent: 2 + - uid: 12420 + components: + - type: Transform + pos: -1.5,-28.5 + parent: 2 + - uid: 13087 + components: + - type: Transform + pos: 32.5,-15.5 + parent: 2 + - uid: 15609 + components: + - type: Transform + pos: -21.5,-40.5 + parent: 2 + - uid: 15610 + components: + - type: Transform + pos: -20.5,-40.5 + parent: 2 + - uid: 15611 + components: + - type: Transform + pos: -19.5,-40.5 + parent: 2 - uid: 17158 components: - type: Transform @@ -31328,6 +31166,11 @@ entities: - type: Transform pos: 9.5,11.5 parent: 16911 + - uid: 19519 + components: + - type: Transform + pos: -51.5,44.5 + parent: 2 - proto: CableHVStack entities: - uid: 17244 @@ -31356,6 +31199,56 @@ entities: parent: 2 - proto: CableMV entities: + - uid: 397 + components: + - type: Transform + pos: -12.5,-22.5 + parent: 2 + - uid: 604 + components: + - type: Transform + pos: -52.5,32.5 + parent: 2 + - uid: 2702 + components: + - type: Transform + pos: -17.5,-22.5 + parent: 2 + - uid: 2713 + components: + - type: Transform + pos: -19.5,-21.5 + parent: 2 + - uid: 2714 + components: + - type: Transform + pos: -20.5,-21.5 + parent: 2 + - uid: 2715 + components: + - type: Transform + pos: -21.5,-21.5 + parent: 2 + - uid: 2717 + components: + - type: Transform + pos: -8.5,-22.5 + parent: 2 + - uid: 2718 + components: + - type: Transform + pos: -15.5,-22.5 + parent: 2 + - uid: 2723 + components: + - type: Transform + pos: -8.5,-23.5 + parent: 2 + - uid: 3979 + components: + - type: Transform + pos: -17.5,-21.5 + parent: 2 - uid: 4054 components: - type: Transform @@ -31384,28 +31277,13 @@ entities: - uid: 4059 components: - type: Transform - pos: -50.5,44.5 + pos: -51.5,43.5 parent: 2 - uid: 4060 components: - type: Transform pos: -12.5,25.5 parent: 2 - - uid: 4061 - components: - - type: Transform - pos: -15.5,-15.5 - parent: 2 - - uid: 4062 - components: - - type: Transform - pos: -18.5,-21.5 - parent: 2 - - uid: 4063 - components: - - type: Transform - pos: -17.5,-15.5 - parent: 2 - uid: 4064 components: - type: Transform @@ -31449,37 +31327,17 @@ entities: - uid: 4072 components: - type: Transform - pos: -4.5,-23.5 - parent: 2 - - uid: 4073 - components: - - type: Transform - pos: -4.5,-23.5 + pos: -8.5,-24.5 parent: 2 - uid: 4074 components: - type: Transform - pos: -6.5,-23.5 - parent: 2 - - uid: 4075 - components: - - type: Transform - pos: -6.5,-20.5 - parent: 2 - - uid: 4076 - components: - - type: Transform - pos: -6.5,-21.5 - parent: 2 - - uid: 4077 - components: - - type: Transform - pos: -5.5,-23.5 + pos: -14.5,-22.5 parent: 2 - uid: 4078 components: - type: Transform - pos: -6.5,-22.5 + pos: -17.5,-20.5 parent: 2 - uid: 4079 components: @@ -31816,11 +31674,6 @@ entities: - type: Transform pos: 31.5,-16.5 parent: 2 - - uid: 4146 - components: - - type: Transform - pos: 31.5,-15.5 - parent: 2 - uid: 4147 components: - type: Transform @@ -32689,52 +32542,12 @@ entities: - uid: 4320 components: - type: Transform - pos: -21.5,-20.5 - parent: 2 - - uid: 4321 - components: - - type: Transform - pos: -19.5,-20.5 + pos: -15.5,-20.5 parent: 2 - uid: 4322 components: - type: Transform - pos: -20.5,-20.5 - parent: 2 - - uid: 4323 - components: - - type: Transform - pos: -17.5,-21.5 - parent: 2 - - uid: 4324 - components: - - type: Transform - pos: -16.5,-21.5 - parent: 2 - - uid: 4325 - components: - - type: Transform - pos: -15.5,-21.5 - parent: 2 - - uid: 4326 - components: - - type: Transform - pos: -14.5,-21.5 - parent: 2 - - uid: 4327 - components: - - type: Transform - pos: -13.5,-21.5 - parent: 2 - - uid: 4328 - components: - - type: Transform - pos: -12.5,-21.5 - parent: 2 - - uid: 4329 - components: - - type: Transform - pos: -11.5,-21.5 + pos: -16.5,-22.5 parent: 2 - uid: 4330 components: @@ -32804,7 +32617,7 @@ entities: - uid: 4343 components: - type: Transform - pos: -20.5,29.5 + pos: -17.5,27.5 parent: 2 - uid: 4344 components: @@ -33101,21 +32914,6 @@ entities: - type: Transform pos: -17.5,26.5 parent: 2 - - uid: 4403 - components: - - type: Transform - pos: -18.5,26.5 - parent: 2 - - uid: 4404 - components: - - type: Transform - pos: -19.5,26.5 - parent: 2 - - uid: 4405 - components: - - type: Transform - pos: -19.5,27.5 - parent: 2 - uid: 4406 components: - type: Transform @@ -33206,11 +33004,6 @@ entities: - type: Transform pos: -28.5,12.5 parent: 2 - - uid: 4424 - components: - - type: Transform - pos: 26.5,18.5 - parent: 2 - uid: 4425 components: - type: Transform @@ -34391,25 +34184,10 @@ entities: - type: Transform pos: -50.5,30.5 parent: 2 - - uid: 4661 - components: - - type: Transform - pos: -50.5,31.5 - parent: 2 - - uid: 4662 - components: - - type: Transform - pos: -50.5,33.5 - parent: 2 - - uid: 4663 - components: - - type: Transform - pos: -50.5,32.5 - parent: 2 - uid: 4664 components: - type: Transform - pos: -49.5,33.5 + pos: -52.5,31.5 parent: 2 - uid: 4665 components: @@ -34829,7 +34607,7 @@ entities: - uid: 4748 components: - type: Transform - pos: -25.5,-7.5 + pos: 27.5,17.5 parent: 2 - uid: 4749 components: @@ -35136,21 +34914,11 @@ entities: - type: Transform pos: -17.5,-11.5 parent: 2 - - uid: 4810 - components: - - type: Transform - pos: -17.5,-13.5 - parent: 2 - uid: 4811 components: - type: Transform pos: -17.5,-12.5 parent: 2 - - uid: 4812 - components: - - type: Transform - pos: -18.5,-13.5 - parent: 2 - uid: 4813 components: - type: Transform @@ -35491,11 +35259,6 @@ entities: - type: Transform pos: 54.5,-16.5 parent: 2 - - uid: 4881 - components: - - type: Transform - pos: -18.5,-20.5 - parent: 2 - uid: 4882 components: - type: Transform @@ -35816,61 +35579,6 @@ entities: - type: Transform pos: 3.5,26.5 parent: 2 - - uid: 4946 - components: - - type: Transform - pos: -18.5,-17.5 - parent: 2 - - uid: 4947 - components: - - type: Transform - pos: -18.5,-19.5 - parent: 2 - - uid: 4948 - components: - - type: Transform - pos: -18.5,-16.5 - parent: 2 - - uid: 4949 - components: - - type: Transform - pos: -18.5,-15.5 - parent: 2 - - uid: 4950 - components: - - type: Transform - pos: -18.5,-18.5 - parent: 2 - - uid: 4951 - components: - - type: Transform - pos: -14.5,-15.5 - parent: 2 - - uid: 4952 - components: - - type: Transform - pos: -13.5,-15.5 - parent: 2 - - uid: 4953 - components: - - type: Transform - pos: -16.5,-15.5 - parent: 2 - - uid: 4954 - components: - - type: Transform - pos: -11.5,-15.5 - parent: 2 - - uid: 4955 - components: - - type: Transform - pos: -10.5,-15.5 - parent: 2 - - uid: 4956 - components: - - type: Transform - pos: -12.5,-15.5 - parent: 2 - uid: 4957 components: - type: Transform @@ -36021,6 +35729,66 @@ entities: - type: Transform pos: -30.5,40.5 parent: 2 + - uid: 6539 + components: + - type: Transform + pos: -11.5,-22.5 + parent: 2 + - uid: 6592 + components: + - type: Transform + pos: -10.5,-22.5 + parent: 2 + - uid: 6596 + components: + - type: Transform + pos: -13.5,-22.5 + parent: 2 + - uid: 8208 + components: + - type: Transform + pos: -9.5,-22.5 + parent: 2 + - uid: 9823 + components: + - type: Transform + pos: -16.5,-20.5 + parent: 2 + - uid: 9833 + components: + - type: Transform + pos: -18.5,-21.5 + parent: 2 + - uid: 13080 + components: + - type: Transform + pos: 32.5,-16.5 + parent: 2 + - uid: 13546 + components: + - type: Transform + pos: -26.5,-7.5 + parent: 2 + - uid: 13551 + components: + - type: Transform + pos: -26.5,-6.5 + parent: 2 + - uid: 13552 + components: + - type: Transform + pos: 27.5,18.5 + parent: 2 + - uid: 14231 + components: + - type: Transform + pos: -18.5,-13.5 + parent: 2 + - uid: 14236 + components: + - type: Transform + pos: -17.5,-13.5 + parent: 2 - uid: 17247 components: - type: Transform @@ -36676,8 +36444,248 @@ entities: - type: Transform pos: 13.5,4.5 parent: 16911 + - uid: 19373 + components: + - type: Transform + pos: -52.5,33.5 + parent: 2 + - uid: 19374 + components: + - type: Transform + pos: -52.5,34.5 + parent: 2 + - uid: 19375 + components: + - type: Transform + pos: -53.5,34.5 + parent: 2 + - uid: 19376 + components: + - type: Transform + pos: -54.5,34.5 + parent: 2 + - uid: 19377 + components: + - type: Transform + pos: -55.5,34.5 + parent: 2 + - uid: 19461 + components: + - type: Transform + pos: -17.5,-26.5 + parent: 2 + - uid: 19462 + components: + - type: Transform + pos: -17.5,-27.5 + parent: 2 + - uid: 19463 + components: + - type: Transform + pos: -17.5,-28.5 + parent: 2 + - uid: 19464 + components: + - type: Transform + pos: -16.5,-28.5 + parent: 2 + - uid: 19465 + components: + - type: Transform + pos: -15.5,-28.5 + parent: 2 + - uid: 19466 + components: + - type: Transform + pos: -14.5,-28.5 + parent: 2 + - uid: 19467 + components: + - type: Transform + pos: -13.5,-28.5 + parent: 2 + - uid: 19468 + components: + - type: Transform + pos: -12.5,-28.5 + parent: 2 + - uid: 19469 + components: + - type: Transform + pos: -11.5,-28.5 + parent: 2 + - uid: 19470 + components: + - type: Transform + pos: -10.5,-28.5 + parent: 2 + - uid: 19471 + components: + - type: Transform + pos: -9.5,-28.5 + parent: 2 + - uid: 19472 + components: + - type: Transform + pos: -8.5,-28.5 + parent: 2 + - uid: 19473 + components: + - type: Transform + pos: -7.5,-28.5 + parent: 2 + - uid: 19474 + components: + - type: Transform + pos: -6.5,-28.5 + parent: 2 + - uid: 19475 + components: + - type: Transform + pos: -5.5,-28.5 + parent: 2 + - uid: 19476 + components: + - type: Transform + pos: -4.5,-28.5 + parent: 2 + - uid: 19477 + components: + - type: Transform + pos: -3.5,-28.5 + parent: 2 + - uid: 19478 + components: + - type: Transform + pos: -2.5,-28.5 + parent: 2 + - uid: 19479 + components: + - type: Transform + pos: -1.5,-28.5 + parent: 2 + - uid: 19480 + components: + - type: Transform + pos: -1.5,-27.5 + parent: 2 + - uid: 19481 + components: + - type: Transform + pos: -0.5,-27.5 + parent: 2 + - uid: 19482 + components: + - type: Transform + pos: 0.5,-27.5 + parent: 2 + - uid: 19483 + components: + - type: Transform + pos: 1.5,-27.5 + parent: 2 + - uid: 19484 + components: + - type: Transform + pos: 1.5,-26.5 + parent: 2 + - uid: 19485 + components: + - type: Transform + pos: 1.5,-25.5 + parent: 2 + - uid: 19486 + components: + - type: Transform + pos: 1.5,-24.5 + parent: 2 + - uid: 19495 + components: + - type: Transform + pos: 22.5,-31.5 + parent: 2 + - uid: 19496 + components: + - type: Transform + pos: 21.5,-31.5 + parent: 2 + - uid: 19497 + components: + - type: Transform + pos: 20.5,-31.5 + parent: 2 + - uid: 19498 + components: + - type: Transform + pos: 19.5,-31.5 + parent: 2 + - uid: 19499 + components: + - type: Transform + pos: 18.5,-31.5 + parent: 2 + - uid: 19500 + components: + - type: Transform + pos: 17.5,-31.5 + parent: 2 + - uid: 19501 + components: + - type: Transform + pos: 16.5,-31.5 + parent: 2 + - uid: 19502 + components: + - type: Transform + pos: 16.5,-30.5 + parent: 2 + - uid: 19503 + components: + - type: Transform + pos: 16.5,-29.5 + parent: 2 + - uid: 19504 + components: + - type: Transform + pos: 16.5,-28.5 + parent: 2 + - uid: 19505 + components: + - type: Transform + pos: 15.5,-28.5 + parent: 2 + - uid: 19506 + components: + - type: Transform + pos: 15.5,-27.5 + parent: 2 + - uid: 19520 + components: + - type: Transform + pos: -51.5,44.5 + parent: 2 + - uid: 19522 + components: + - type: Transform + pos: 32.5,-15.5 + parent: 2 + - uid: 19528 + components: + - type: Transform + pos: -19.5,28.5 + parent: 2 + - uid: 19529 + components: + - type: Transform + pos: -20.5,28.5 + parent: 2 - proto: CableTerminal entities: + - uid: 4405 + components: + - type: Transform + pos: -25.5,-6.5 + parent: 2 - uid: 4987 components: - type: Transform @@ -36725,17 +36733,11 @@ entities: rot: 3.141592653589793 rad pos: 23.5,-42.5 parent: 2 - - uid: 4995 + - uid: 13550 components: - type: Transform rot: 3.141592653589793 rad - pos: -19.5,-41.5 - parent: 2 - - uid: 4996 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-41.5 + pos: 26.5,17.5 parent: 2 - uid: 17378 components: @@ -36797,6 +36799,18 @@ entities: rot: -1.5707963267948966 rad pos: 24.5,-10.5 parent: 16911 + - uid: 19521 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -50.5,43.5 + parent: 2 + - uid: 19524 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 31.5,-16.5 + parent: 2 - proto: CandleGreenInfinite entities: - uid: 4997 @@ -36887,6 +36901,23 @@ entities: - type: Transform pos: -3.4659548,-3.9046545 parent: 2 +- proto: CarbonDioxideCanister + entities: + - uid: 3088 + components: + - type: Transform + pos: -8.5,-25.5 + parent: 2 + - uid: 5740 + components: + - type: Transform + pos: -4.5,-39.5 + parent: 2 + - uid: 8070 + components: + - type: Transform + pos: -8.5,-26.5 + parent: 2 - proto: CargoMailTeleporter entities: - uid: 5014 @@ -37426,6 +37457,76 @@ entities: spent: True - proto: Catwalk entities: + - uid: 679 + components: + - type: Transform + pos: -8.5,-27.5 + parent: 2 + - uid: 681 + components: + - type: Transform + pos: -9.5,-27.5 + parent: 2 + - uid: 684 + components: + - type: Transform + pos: -9.5,-26.5 + parent: 2 + - uid: 761 + components: + - type: Transform + pos: -8.5,-26.5 + parent: 2 + - uid: 764 + components: + - type: Transform + pos: -8.5,-25.5 + parent: 2 + - uid: 854 + components: + - type: Transform + pos: -7.5,-25.5 + parent: 2 + - uid: 980 + components: + - type: Transform + pos: -6.5,-25.5 + parent: 2 + - uid: 981 + components: + - type: Transform + pos: -7.5,-26.5 + parent: 2 + - uid: 2770 + components: + - type: Transform + pos: -7.5,-27.5 + parent: 2 + - uid: 2837 + components: + - type: Transform + pos: -5.5,-26.5 + parent: 2 + - uid: 2840 + components: + - type: Transform + pos: -6.5,-26.5 + parent: 2 + - uid: 2843 + components: + - type: Transform + pos: -5.5,-25.5 + parent: 2 + - uid: 2844 + components: + - type: Transform + pos: -5.5,-27.5 + parent: 2 + - uid: 3098 + components: + - type: Transform + pos: -6.5,-27.5 + parent: 2 - uid: 5102 components: - type: Transform @@ -37497,11 +37598,6 @@ entities: - type: Transform pos: 36.5,1.5 parent: 2 - - uid: 5116 - components: - - type: Transform - pos: 1.5,-29.5 - parent: 2 - uid: 5117 components: - type: Transform @@ -38613,16 +38709,6 @@ entities: - type: Transform pos: -76.5,17.5 parent: 2 - - uid: 5336 - components: - - type: Transform - pos: -17.5,-29.5 - parent: 2 - - uid: 5337 - components: - - type: Transform - pos: -14.5,-29.5 - parent: 2 - uid: 5338 components: - type: Transform @@ -40659,36 +40745,6 @@ entities: - type: Transform pos: -35.5,-35.5 parent: 2 - - uid: 5740 - components: - - type: Transform - pos: -6.5,-30.5 - parent: 2 - - uid: 5741 - components: - - type: Transform - pos: -6.5,-31.5 - parent: 2 - - uid: 5742 - components: - - type: Transform - pos: -5.5,-30.5 - parent: 2 - - uid: 5743 - components: - - type: Transform - pos: -5.5,-31.5 - parent: 2 - - uid: 5744 - components: - - type: Transform - pos: -5.5,-32.5 - parent: 2 - - uid: 5745 - components: - - type: Transform - pos: -6.5,-32.5 - parent: 2 - uid: 5746 components: - type: Transform @@ -40827,6 +40883,96 @@ entities: rot: 1.5707963267948966 rad pos: 38.5,22.5 parent: 2 + - uid: 7836 + components: + - type: Transform + pos: -9.5,-25.5 + parent: 2 + - uid: 7838 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-39.5 + parent: 2 + - uid: 7841 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-39.5 + parent: 2 + - uid: 7842 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-39.5 + parent: 2 + - uid: 7843 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-39.5 + parent: 2 + - uid: 7845 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-36.5 + parent: 2 + - uid: 10058 + components: + - type: Transform + pos: -24.5,-29.5 + parent: 2 + - uid: 11185 + components: + - type: Transform + pos: -24.5,-30.5 + parent: 2 + - uid: 11298 + components: + - type: Transform + pos: -24.5,-31.5 + parent: 2 + - uid: 11299 + components: + - type: Transform + pos: -25.5,-29.5 + parent: 2 + - uid: 11300 + components: + - type: Transform + pos: -25.5,-30.5 + parent: 2 + - uid: 11301 + components: + - type: Transform + pos: -25.5,-31.5 + parent: 2 + - uid: 11304 + components: + - type: Transform + pos: -26.5,-29.5 + parent: 2 + - uid: 11305 + components: + - type: Transform + pos: -26.5,-30.5 + parent: 2 + - uid: 11350 + components: + - type: Transform + pos: -26.5,-31.5 + parent: 2 + - uid: 12157 + components: + - type: Transform + pos: -20.5,-32.5 + parent: 2 + - uid: 14314 + components: + - type: Transform + pos: -17.5,-29.5 + parent: 2 - uid: 17390 components: - type: Transform @@ -42563,18 +42709,6 @@ entities: rot: 1.5707963267948966 rad pos: 22.5,-33.5 parent: 2 - - uid: 5795 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-32.5 - parent: 2 - - uid: 5796 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-31.5 - parent: 2 - uid: 5797 components: - type: Transform @@ -42849,6 +42983,42 @@ entities: rot: -1.5707963267948966 rad pos: -47.53233,37.54135 parent: 2 + - uid: 10977 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-38.5 + parent: 2 + - uid: 10978 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-37.5 + parent: 2 + - uid: 10979 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-36.5 + parent: 2 + - uid: 10980 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-36.5 + parent: 2 + - uid: 10981 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-37.5 + parent: 2 + - uid: 10982 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-38.5 + parent: 2 - proto: ChairGreyscale entities: - uid: 5844 @@ -42977,6 +43147,16 @@ entities: parent: 16911 - proto: ChairOfficeLight entities: + - uid: 2828 + components: + - type: Transform + pos: -12.5,-25.5 + parent: 2 + - uid: 3655 + components: + - type: Transform + pos: -9.5,-22.5 + parent: 2 - uid: 5863 components: - type: Transform @@ -43069,6 +43249,22 @@ entities: rot: -1.5707963267948966 rad pos: -20.542828,21.603903 parent: 2 + - uid: 10983 + components: + - type: Transform + pos: -22.5,-35.5 + parent: 2 + - uid: 12410 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-17.5 + parent: 2 + - uid: 12478 + components: + - type: Transform + pos: -1.4284668,-33.681366 + parent: 2 - proto: ChairPilotSeat entities: - uid: 5879 @@ -43242,6 +43438,31 @@ entities: - type: Transform pos: -37.626637,39.635124 parent: 2 + - uid: 15428 + components: + - type: Transform + pos: -11.820269,-26.3976 + parent: 2 + - uid: 15433 + components: + - type: Transform + pos: -11.867144,-26.2726 + parent: 2 + - uid: 15438 + components: + - type: Transform + pos: -11.632769,-26.36635 + parent: 2 + - uid: 15439 + components: + - type: Transform + pos: -12.226519,-26.444475 + parent: 2 + - uid: 15453 + components: + - type: Transform + pos: -12.335894,-26.381975 + parent: 2 - proto: CigCartonGreen entities: - uid: 5905 @@ -43286,18 +43507,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -43384,18 +43595,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - uid: 5922 components: - type: Transform @@ -43621,20 +43822,15 @@ entities: immutable: False temperature: 293.147 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: ClosetFireFilled entities: + - uid: 5795 + components: + - type: Transform + pos: 22.5,-26.5 + parent: 2 - uid: 5965 components: - type: Transform @@ -43676,18 +43872,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - uid: 5972 components: - type: Transform @@ -43738,11 +43924,6 @@ entities: - type: Transform pos: 11.5,-14.5 parent: 2 - - uid: 5982 - components: - - type: Transform - pos: 22.5,-28.5 - parent: 2 - uid: 5983 components: - type: Transform @@ -43841,18 +44022,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -43875,18 +44046,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8977377 - - 7.139109 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8977377 + Nitrogen: 7.139109 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44028,18 +44189,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44062,18 +44213,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44096,18 +44237,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44171,15 +44302,15 @@ entities: parent: 2 - proto: ClosetRadiationSuitFilled entities: - - uid: 6037 + - uid: 11153 components: - type: Transform - pos: -22.5,-39.5 + pos: -19.5,-41.5 parent: 2 - - uid: 6038 + - uid: 11154 components: - type: Transform - pos: -23.5,-39.5 + pos: -20.5,-41.5 parent: 2 - proto: ClosetTool entities: @@ -44244,18 +44375,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44279,18 +44400,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44316,18 +44427,8 @@ entities: immutable: False temperature: 293.14697 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -44394,6 +44495,13 @@ entities: pos: -45.52265,33.840355 parent: 2 - type: EyeProtection +- proto: ClothingEyesGlassesMeson + entities: + - uid: 8395 + components: + - type: Transform + pos: -15.597261,-17.287827 + parent: 2 - proto: ClothingEyesGlassesSunglasses entities: - uid: 6065 @@ -44401,6 +44509,18 @@ entities: - type: Transform pos: 15.503975,-19.02182 parent: 2 +- proto: ClothingEyesGlassesThermal + entities: + - uid: 10015 + components: + - type: Transform + pos: -15.581636,-17.600327 + parent: 2 + - uid: 14315 + components: + - type: Transform + pos: 0.5,-34.5 + parent: 2 - proto: ClothingHandsGlovesColorBlack entities: - uid: 6055 @@ -44442,6 +44562,12 @@ entities: - type: Transform pos: -32.285084,64.626755 parent: 2 + - uid: 8396 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.269136,-17.475327 + parent: 2 - proto: ClothingHandsGlovesFingerlessInsulated entities: - uid: 6071 @@ -44790,10 +44916,10 @@ entities: - type: InsideEntityStorage - proto: ClothingShoesBling entities: - - uid: 6119 + - uid: 11496 components: - type: Transform - pos: -4.46716,-21.245726 + pos: -53.5,34.5 parent: 2 - proto: ClothingShoesBootsLaceup entities: @@ -44809,6 +44935,16 @@ entities: - type: Transform pos: -20.337545,-12.3313 parent: 2 + - uid: 16754 + components: + - type: Transform + pos: 15.498966,-29.263462 + parent: 2 + - uid: 19356 + components: + - type: Transform + pos: 15.483341,-29.700962 + parent: 2 - proto: ClothingShoesBootsWinterSec entities: - uid: 6059 @@ -44827,10 +44963,10 @@ entities: parent: 2 - proto: ClothingShoesSlippers entities: - - uid: 6123 + - uid: 3282 components: - type: Transform - pos: -13.53664,-18.218166 + pos: -5.2967405,-22.787128 parent: 2 - proto: ClothingUniformJumpskirtDetective entities: @@ -44995,11 +45131,6 @@ entities: rot: -1.5707963267948966 rad pos: 10.5,-6.5 parent: 2 - - uid: 6141 - components: - - type: Transform - pos: -13.5,-22.5 - parent: 2 - uid: 6142 components: - type: Transform @@ -45035,6 +45166,18 @@ entities: rot: 1.5707963267948966 rad pos: 29.5,-11.5 parent: 2 + - uid: 8264 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-23.5 + parent: 2 + - uid: 11789 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-23.5 + parent: 2 - uid: 17684 components: - type: Transform @@ -45070,6 +45213,12 @@ entities: rot: 3.141592653589793 rad pos: -33.5,-39.5 parent: 2 + - uid: 7878 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-25.5 + parent: 2 - proto: ComputerAnalysisConsole entities: - uid: 6150 @@ -45089,6 +45238,14 @@ entities: rot: 1.5707963267948966 rad pos: -26.5,-14.5 parent: 2 +- proto: ComputerAtmosMonitoring + entities: + - uid: 7852 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-25.5 + parent: 2 - proto: computerBodyScanner entities: - uid: 6152 @@ -45169,10 +45326,11 @@ entities: parent: 2 - proto: ComputerCargoOrdersEngineering entities: - - uid: 6156 + - uid: 581 components: - type: Transform - pos: -17.5,-17.5 + rot: 1.5707963267948966 rad + pos: -18.5,-18.5 parent: 2 - proto: ComputerCargoOrdersMedical entities: @@ -45302,6 +45460,12 @@ entities: parent: 2 - proto: ComputerPowerMonitoring entities: + - uid: 589 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-17.5 + parent: 2 - uid: 6176 components: - type: Transform @@ -45462,6 +45626,12 @@ entities: rot: -1.5707963267948966 rad pos: 15.5,20.5 parent: 2 + - uid: 14316 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -18.5,-31.5 + parent: 2 - uid: 17694 components: - type: Transform @@ -45926,18 +46096,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 6276 components: - type: Transform @@ -45959,18 +46119,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46000,18 +46150,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46060,6 +46200,25 @@ entities: - type: Transform pos: 31.5,-1.5 parent: 2 +- proto: CrateEngineering + entities: + - uid: 12333 + components: + - type: Transform + pos: -0.5,-32.5 + parent: 2 + - type: ContainerContainer + containers: + entity_storage: !type:Container + showEnts: False + occludes: True + ents: + - 12473 + - 12474 + paper_label: !type:ContainerSlot + showEnts: False + occludes: True + ent: null - proto: CrateEngineeringAMEControl entities: - uid: 6283 @@ -46098,6 +46257,13 @@ entities: - type: Transform pos: -30.5,0.5 parent: 2 +- proto: CrateEngineeringSingularityCollector + entities: + - uid: 11791 + components: + - type: Transform + pos: -16.5,-39.5 + parent: 2 - proto: CrateEngineeringSolar entities: - uid: 6287 @@ -46111,18 +46277,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - proto: CrateFreezer entities: - uid: 6288 @@ -46130,6 +46286,23 @@ entities: - type: Transform pos: -19.5,7.5 parent: 2 + - uid: 15588 + components: + - type: Transform + pos: -16.5,-33.5 + parent: 2 + - type: ContainerContainer + containers: + entity_storage: !type:Container + showEnts: False + occludes: True + ents: + - 15589 + - 15590 + paper_label: !type:ContainerSlot + showEnts: False + occludes: True + ent: null - proto: CrateFunInstrumentsVariety entities: - uid: 6289 @@ -46152,18 +46325,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateFunToyBox entities: - uid: 6291 @@ -46187,18 +46350,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46226,18 +46379,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46360,18 +46503,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46422,18 +46555,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46460,18 +46583,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46498,18 +46611,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46536,18 +46639,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46576,18 +46669,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -46724,18 +46807,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 6352 components: - type: Transform @@ -47701,6 +47774,95 @@ entities: - type: Transform pos: -22.345272,-8.254836 parent: 2 +- proto: DecalSpawnerDirtWide + entities: + - uid: 396 + components: + - type: Transform + pos: -24.5,-33.5 + parent: 2 + - uid: 3421 + components: + - type: Transform + pos: -14.5,-14.5 + parent: 2 + - uid: 3422 + components: + - type: Transform + pos: -14.5,-30.5 + parent: 2 + - uid: 3978 + components: + - type: Transform + pos: -7.5,-35.5 + parent: 2 + - uid: 4323 + components: + - type: Transform + pos: -20.5,-40.5 + parent: 2 + - uid: 4325 + components: + - type: Transform + pos: -5.5,-29.5 + parent: 2 + - uid: 4948 + components: + - type: Transform + pos: -9.5,-18.5 + parent: 2 + - uid: 4953 + components: + - type: Transform + pos: -15.5,-40.5 + parent: 2 + - uid: 4954 + components: + - type: Transform + pos: 4.5,-31.5 + parent: 2 + - uid: 6478 + components: + - type: Transform + pos: -14.5,-22.5 + parent: 2 + - uid: 7405 + components: + - type: Transform + pos: -24.5,-38.5 + parent: 2 + - uid: 8222 + components: + - type: Transform + pos: -24.5,-22.5 + parent: 2 + - uid: 8256 + components: + - type: Transform + pos: -17.5,-19.5 + parent: 2 +- proto: DecalSpawnerFlowers + entities: + - uid: 2697 + components: + - type: Transform + pos: -5.5,-17.5 + parent: 2 + - uid: 3423 + components: + - type: Transform + pos: -7.5,-19.5 + parent: 2 + - uid: 3980 + components: + - type: Transform + pos: -4.5,-19.5 + parent: 2 + - uid: 4326 + components: + - type: Transform + pos: -6.5,-18.5 + parent: 2 - proto: DefaultStationBeaconAI entities: - uid: 6422 @@ -47748,13 +47910,6 @@ entities: - type: Transform pos: -23.5,-13.5 parent: 2 -- proto: DefaultStationBeaconAtmospherics - entities: - - uid: 6429 - components: - - type: Transform - pos: -9.5,-26.5 - parent: 2 - proto: DefaultStationBeaconBar entities: - uid: 6430 @@ -47804,13 +47959,6 @@ entities: - type: Transform pos: 25.5,4.5 parent: 2 -- proto: DefaultStationBeaconCERoom - entities: - - uid: 6437 - components: - - type: Transform - pos: -13.5,-19.5 - parent: 2 - proto: DefaultStationBeaconChapel entities: - uid: 6438 @@ -47860,13 +48008,6 @@ entities: - type: Transform pos: -28.5,27.5 parent: 2 -- proto: DefaultStationBeaconEngineering - entities: - - uid: 6445 - components: - - type: Transform - pos: -17.5,-21.5 - parent: 2 - proto: DefaultStationBeaconEvac entities: - uid: 6446 @@ -47876,10 +48017,10 @@ entities: parent: 2 - proto: DefaultStationBeaconEVAStorage entities: - - uid: 6447 + - uid: 19364 components: - type: Transform - pos: -51.5,33.5 + pos: 17.5,-30.5 parent: 2 - proto: DefaultStationBeaconGravGen entities: @@ -48091,10 +48232,10 @@ entities: parent: 2 - proto: DefaultStationBeaconVault entities: - - uid: 6478 + - uid: 12544 components: - type: Transform - pos: -7.5,-21.5 + pos: -53.5,35.5 parent: 2 - proto: DefaultStationBeaconWardensOffice entities: @@ -48170,6 +48311,11 @@ entities: parent: 2 - proto: DeskBell entities: + - uid: 1281 + components: + - type: Transform + pos: -16.425982,-16.430346 + parent: 2 - uid: 6491 components: - type: Transform @@ -48189,6 +48335,12 @@ entities: parent: 2 - proto: DisposalBend entities: + - uid: 4329 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-23.5 + parent: 2 - uid: 6494 components: - type: Transform @@ -48223,12 +48375,6 @@ entities: rot: 1.5707963267948966 rad pos: 2.5,-1.5 parent: 2 - - uid: 6500 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 29.5,36.5 - parent: 2 - uid: 6501 components: - type: Transform @@ -48436,23 +48582,6 @@ entities: rot: 1.5707963267948966 rad pos: -24.5,-23.5 parent: 2 - - uid: 6537 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-23.5 - parent: 2 - - uid: 6538 - components: - - type: Transform - pos: -16.5,-14.5 - parent: 2 - - uid: 6539 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-14.5 - parent: 2 - uid: 6540 components: - type: Transform @@ -48620,6 +48749,24 @@ entities: - type: Transform pos: -23.5,6.5 parent: 2 + - uid: 11601 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-22.5 + parent: 2 + - uid: 12361 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-17.5 + parent: 2 + - uid: 12657 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-17.5 + parent: 2 - uid: 17811 components: - type: Transform @@ -48676,6 +48823,12 @@ entities: parent: 16911 - proto: DisposalJunction entities: + - uid: 2704 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-15.5 + parent: 2 - uid: 6569 components: - type: Transform @@ -48808,12 +48961,6 @@ entities: rot: 3.141592653589793 rad pos: 20.5,-7.5 parent: 2 - - uid: 6592 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -16.5,-21.5 - parent: 2 - uid: 6593 components: - type: Transform @@ -48832,13 +48979,25 @@ entities: rot: 3.141592653589793 rad pos: 7.5,37.5 parent: 2 -- proto: DisposalJunctionFlipped - entities: - - uid: 6596 + - uid: 7001 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-22.5 + parent: 2 + - uid: 12448 components: - type: Transform rot: 1.5707963267948966 rad - pos: -10.5,-15.5 + pos: -14.5,-17.5 + parent: 2 +- proto: DisposalJunctionFlipped + entities: + - uid: 3426 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-14.5 parent: 2 - uid: 6597 components: @@ -48935,6 +49094,57 @@ entities: parent: 2 - proto: DisposalPipe entities: + - uid: 17 + components: + - type: Transform + pos: -14.5,-18.5 + parent: 2 + - uid: 426 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-17.5 + parent: 2 + - uid: 2712 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-17.5 + parent: 2 + - uid: 2719 + components: + - type: Transform + pos: -18.5,-13.5 + parent: 2 + - uid: 2721 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-16.5 + parent: 2 + - uid: 3425 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-14.5 + parent: 2 + - uid: 4881 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-17.5 + parent: 2 + - uid: 4952 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -16.5,-14.5 + parent: 2 + - uid: 6156 + components: + - type: Transform + pos: -8.5,-13.5 + parent: 2 - uid: 6613 components: - type: Transform @@ -49267,8 +49477,7 @@ entities: - uid: 6668 components: - type: Transform - rot: 3.141592653589793 rad - pos: 29.5,35.5 + pos: 30.5,35.5 parent: 2 - uid: 6669 components: @@ -50221,15 +50430,11 @@ entities: - type: Transform pos: -8.5,-11.5 parent: 2 - - uid: 6841 - components: - - type: Transform - pos: -8.5,-13.5 - parent: 2 - uid: 6842 components: - type: Transform - pos: -8.5,-14.5 + rot: -1.5707963267948966 rad + pos: -15.5,-14.5 parent: 2 - uid: 6843 components: @@ -51112,77 +51317,11 @@ entities: rot: 3.141592653589793 rad pos: 23.5,-29.5 parent: 2 - - uid: 6998 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -9.5,-15.5 - parent: 2 - - uid: 6999 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-15.5 - parent: 2 - - uid: 7000 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-15.5 - parent: 2 - - uid: 7001 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -14.5,-15.5 - parent: 2 - - uid: 7002 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-15.5 - parent: 2 - - uid: 7003 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -12.5,-15.5 - parent: 2 - - uid: 7004 - components: - - type: Transform - pos: -16.5,-16.5 - parent: 2 - - uid: 7005 - components: - - type: Transform - pos: -16.5,-18.5 - parent: 2 - uid: 7006 - components: - - type: Transform - pos: -16.5,-19.5 - parent: 2 - - uid: 7007 - components: - - type: Transform - pos: -16.5,-20.5 - parent: 2 - - uid: 7008 - components: - - type: Transform - pos: -16.5,-22.5 - parent: 2 - - uid: 7009 - components: - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - uid: 7010 components: - type: Transform rot: -1.5707963267948966 rad - pos: -17.5,-23.5 + pos: -12.5,-14.5 parent: 2 - uid: 7011 components: @@ -51190,12 +51329,6 @@ entities: rot: -1.5707963267948966 rad pos: -19.5,-23.5 parent: 2 - - uid: 7012 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-23.5 - parent: 2 - uid: 7013 components: - type: Transform @@ -51304,17 +51437,6 @@ entities: rot: 1.5707963267948966 rad pos: -22.5,-34.5 parent: 2 - - uid: 7032 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-14.5 - parent: 2 - - uid: 7033 - components: - - type: Transform - pos: -18.5,-13.5 - parent: 2 - uid: 7034 components: - type: Transform @@ -51767,24 +51889,6 @@ entities: rot: 1.5707963267948966 rad pos: 1.5,-9.5 parent: 2 - - uid: 7116 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-21.5 - parent: 2 - - uid: 7117 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-21.5 - parent: 2 - - uid: 7118 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -14.5,-21.5 - parent: 2 - uid: 7119 components: - type: Transform @@ -52029,6 +52133,132 @@ entities: rot: 1.5707963267948966 rad pos: -26.5,6.5 parent: 2 + - uid: 7164 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 29.5,34.5 + parent: 2 + - uid: 7167 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-14.5 + parent: 2 + - uid: 7217 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-14.5 + parent: 2 + - uid: 7276 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-14.5 + parent: 2 + - uid: 7280 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-14.5 + parent: 2 + - uid: 8255 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-14.5 + parent: 2 + - uid: 9491 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-17.5 + parent: 2 + - uid: 9711 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-23.5 + parent: 2 + - uid: 12034 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-22.5 + parent: 2 + - uid: 12043 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-22.5 + parent: 2 + - uid: 12357 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-22.5 + parent: 2 + - uid: 12360 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-22.5 + parent: 2 + - uid: 12422 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-18.5 + parent: 2 + - uid: 12423 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-22.5 + parent: 2 + - uid: 12424 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-19.5 + parent: 2 + - uid: 12445 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -9.5,-17.5 + parent: 2 + - uid: 12446 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-17.5 + parent: 2 + - uid: 12447 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-22.5 + parent: 2 + - uid: 13057 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -16.5,-17.5 + parent: 2 + - uid: 13322 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-20.5 + parent: 2 + - uid: 13323 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-21.5 + parent: 2 - uid: 17820 components: - type: Transform @@ -52606,12 +52836,6 @@ entities: parent: 16911 - proto: DisposalSignalRouterFlipped entities: - - uid: 7164 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 29.5,34.5 - parent: 2 - uid: 7165 components: - type: Transform @@ -52620,17 +52844,23 @@ entities: parent: 2 - proto: DisposalTrunk entities: + - uid: 2722 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-15.5 + parent: 2 + - uid: 6500 + components: + - type: Transform + pos: 30.5,36.5 + parent: 2 - uid: 7166 components: - type: Transform rot: 3.141592653589793 rad pos: 9.5,-3.5 parent: 2 - - uid: 7167 - components: - - type: Transform - pos: -10.5,-14.5 - parent: 2 - uid: 7168 components: - type: Transform @@ -52655,17 +52885,6 @@ entities: rot: 3.141592653589793 rad pos: 12.5,25.5 parent: 2 - - uid: 7172 - components: - - type: Transform - pos: 30.5,35.5 - parent: 2 - - uid: 7173 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 30.5,36.5 - parent: 2 - uid: 7174 components: - type: Transform @@ -52916,12 +53135,6 @@ entities: rot: 3.141592653589793 rad pos: 5.5,-9.5 parent: 2 - - uid: 7217 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -12.5,-21.5 - parent: 2 - uid: 7218 components: - type: Transform @@ -52957,8 +53170,29 @@ entities: rot: 3.141592653589793 rad pos: -23.5,5.5 parent: 2 + - uid: 9845 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-19.5 + parent: 2 + - uid: 9973 + components: + - type: Transform + pos: -10.5,-21.5 + parent: 2 - proto: DisposalUnit entities: + - uid: 2720 + components: + - type: Transform + pos: -18.5,-15.5 + parent: 2 + - uid: 3644 + components: + - type: Transform + pos: -10.5,-21.5 + parent: 2 - uid: 7224 components: - type: Transform @@ -52999,11 +53233,6 @@ entities: - type: Transform pos: 33.5,13.5 parent: 2 - - uid: 7232 - components: - - type: Transform - pos: -10.5,-14.5 - parent: 2 - uid: 7233 components: - type: Transform @@ -53204,11 +53433,6 @@ entities: - type: Transform pos: 5.5,-9.5 parent: 2 - - uid: 7273 - components: - - type: Transform - pos: -12.5,-21.5 - parent: 2 - uid: 7274 components: - type: Transform @@ -53219,13 +53443,23 @@ entities: - type: Transform pos: 36.5,17.5 parent: 2 -- proto: DisposalYJunction - entities: - - uid: 7276 + - uid: 8394 components: - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-15.5 + pos: -14.5,-19.5 + parent: 2 + - uid: 16393 + components: + - type: Transform + pos: -18.5,-26.5 + parent: 2 +- proto: DisposalYJunction + entities: + - uid: 2705 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-14.5 parent: 2 - uid: 7277 components: @@ -53243,12 +53477,6 @@ entities: - type: Transform pos: 14.5,26.5 parent: 2 - - uid: 7280 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-15.5 - parent: 2 - proto: DogBed entities: - uid: 7281 @@ -53295,6 +53523,13 @@ entities: - type: Transform pos: 1.571956,-18.370005 parent: 2 +- proto: DoubleEmergencyOxygenTankFilled + entities: + - uid: 19383 + components: + - type: Transform + pos: -13.377129,-27.273674 + parent: 2 - proto: DresserCaptainFilled entities: - uid: 7289 @@ -53304,10 +53539,10 @@ entities: parent: 2 - proto: DresserChiefEngineerFilled entities: - - uid: 7290 + - uid: 3597 components: - type: Transform - pos: -14.5,-17.5 + pos: -6.5,-21.5 parent: 2 - proto: DresserChiefMedicalOfficerFilled entities: @@ -53388,6 +53623,18 @@ entities: - type: Transform pos: -44.868347,44.457825 parent: 2 +- proto: DrinkBeerglass + entities: + - uid: 12481 + components: + - type: Transform + pos: -0.7253418,-33.993866 + parent: 2 + - uid: 12545 + components: + - type: Transform + pos: -0.3972168,-34.22824 + parent: 2 - proto: DrinkBottleOfNothingFull entities: - uid: 7301 @@ -53955,6 +54202,18 @@ entities: rot: 1.5707963267948966 rad pos: -21.5,9.5 parent: 2 + - uid: 11417 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -26.5,-39.5 + parent: 2 + - uid: 11610 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -19.5,-38.5 + parent: 2 - proto: EmergencyOxygenTank entities: - uid: 901 @@ -54028,30 +54287,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 7405 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-18.5 - parent: 2 - - type: Fixtures - fixtures: {} - - uid: 7406 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-25.5 - parent: 2 - - type: Fixtures - fixtures: {} - - uid: 7407 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -19.5,-29.5 - parent: 2 - - type: Fixtures - fixtures: {} - uid: 7408 components: - type: Transform @@ -54162,6 +54397,14 @@ entities: fixtures: {} - proto: FaxMachineBase entities: + - uid: 3444 + components: + - type: Transform + pos: -10.5,-23.5 + parent: 2 + - type: FaxMachine + name: Старший инженер + destinationAddress: Старший инженер - uid: 7422 components: - type: Transform @@ -54170,14 +54413,6 @@ entities: - type: FaxMachine name: Научный руководитель destinationAddress: Научный руководитель - - uid: 7423 - components: - - type: Transform - pos: -14.5,-23.5 - parent: 2 - - type: FaxMachine - name: Старший инженер - destinationAddress: Старший инженер - uid: 7424 components: - type: Transform @@ -54510,6 +54745,11 @@ entities: parent: 2 - proto: filingCabinetRandom entities: + - uid: 3643 + components: + - type: Transform + pos: -7.5,-23.5 + parent: 2 - uid: 7476 components: - type: Transform @@ -54520,11 +54760,6 @@ entities: - type: Transform pos: -33.5,16.5 parent: 2 - - uid: 7478 - components: - - type: Transform - pos: -12.5,-22.5 - parent: 2 - uid: 7479 components: - type: Transform @@ -54545,6 +54780,35 @@ entities: - type: Transform pos: 12.5,-1.5 parent: 2 +- proto: FireAlarm + entities: + - uid: 10044 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 24.5,-29.5 + parent: 2 + - type: DeviceList + devices: + - 13567 + - 13564 + - 7519 + - 7520 + - 98 + - type: Fixtures + fixtures: {} + - uid: 13565 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 17.5,-33.5 + parent: 2 + - type: DeviceList + devices: + - 13567 + - 13564 + - type: Fixtures + fixtures: {} - proto: FireAxe entities: - uid: 17923 @@ -54563,11 +54827,11 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 7484 + - uid: 8071 components: - type: Transform rot: -1.5707963267948966 rad - pos: -3.5,-26.5 + pos: -0.5,-30.5 parent: 2 - type: Fixtures fixtures: {} @@ -54636,15 +54900,6 @@ entities: rot: -1.5707963267948966 rad pos: -49.5,41.5 parent: 2 - - uid: 7493 - components: - - type: Transform - pos: 22.5,-29.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - - 99 - uid: 7494 components: - type: Transform @@ -54852,7 +55107,7 @@ entities: - type: DeviceNetwork deviceLists: - 97 - - 98 + - 10044 - uid: 7520 components: - type: Transform @@ -54861,16 +55116,7 @@ entities: - type: DeviceNetwork deviceLists: - 97 - - 98 - - uid: 7521 - components: - - type: Transform - pos: 23.5,-29.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - - 99 + - 10044 - uid: 7522 components: - type: Transform @@ -55386,17 +55632,11 @@ entities: - type: Transform pos: -26.5,-28.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - uid: 7595 components: - type: Transform pos: -24.5,-28.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - uid: 7596 components: - type: Transform @@ -55404,7 +55644,6 @@ entities: parent: 2 - type: DeviceNetwork deviceLists: - - 11 - 12 - uid: 7597 components: @@ -55439,7 +55678,6 @@ entities: - type: DeviceNetwork deviceLists: - 13 - - 14 - uid: 7601 components: - type: Transform @@ -55448,7 +55686,6 @@ entities: - type: DeviceNetwork deviceLists: - 13 - - 14 - uid: 7602 components: - type: Transform @@ -55465,16 +55702,6 @@ entities: - type: DeviceNetwork deviceLists: - 13 - - uid: 7604 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-21.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - 17 - uid: 7605 components: - type: Transform @@ -55483,16 +55710,7 @@ entities: parent: 2 - type: DeviceNetwork deviceLists: - - 15 - 6 - - uid: 7606 - components: - - type: Transform - pos: -7.5,-19.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 10 - uid: 7607 components: - type: Transform @@ -56110,7 +56328,6 @@ entities: - type: DeviceNetwork deviceLists: - 50 - - 99 - uid: 7677 components: - type: Transform @@ -56196,7 +56413,6 @@ entities: parent: 2 - type: DeviceNetwork deviceLists: - - 15 - 57 - uid: 7686 components: @@ -56206,7 +56422,6 @@ entities: parent: 2 - type: DeviceNetwork deviceLists: - - 15 - 57 - uid: 7687 components: @@ -56563,15 +56778,6 @@ entities: - type: DeviceNetwork deviceLists: - 88 - - uid: 7727 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -50.5,31.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 88 - uid: 7728 components: - type: Transform @@ -56772,17 +56978,11 @@ entities: - type: Transform pos: -18.5,-23.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - uid: 7754 components: - type: Transform pos: -16.5,-23.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - uid: 7755 components: - type: Transform @@ -56822,6 +57022,60 @@ entities: parent: 2 - proto: FirelockGlass entities: + - uid: 98 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 23.5,-35.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 10044 + - uid: 3292 + components: + - type: Transform + pos: -17.5,-16.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 + - uid: 3437 + components: + - type: Transform + pos: -11.5,-17.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 + - uid: 3441 + components: + - type: Transform + pos: -16.5,-16.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 + - uid: 3454 + components: + - type: Transform + pos: -11.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 170 + - uid: 3550 + components: + - type: Transform + pos: -11.5,-19.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - 9 - uid: 7760 components: - type: Transform @@ -56847,46 +57101,6 @@ entities: - type: Transform pos: 5.5,9.5 parent: 2 - - uid: 7765 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-16.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - 15 - - uid: 7766 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-16.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - 15 - - uid: 7767 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-13.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - - 9 - - uid: 7768 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-15.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - - 9 - uid: 7769 components: - type: Transform @@ -56923,6 +57137,28 @@ entities: deviceLists: - 91 - 92 + - uid: 13564 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-29.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 13565 + - 1509 + - 10044 + - uid: 13567 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-31.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 13565 + - 1509 + - 10044 - proto: Fireplace entities: - uid: 7773 @@ -57078,6 +57314,13 @@ entities: - type: Transform pos: 56.511456,-6.1529155 parent: 2 +- proto: FloraTree + entities: + - uid: 13589 + components: + - type: Transform + pos: -4.8649893,-18.019133 + parent: 2 - proto: FloraTreeConifer entities: - uid: 7786 @@ -57085,6 +57328,13 @@ entities: - type: Transform pos: 56.481674,-11.007644 parent: 2 +- proto: FloraTreeLarge + entities: + - uid: 8261 + components: + - type: Transform + pos: -7.5,-17.5 + parent: 2 - proto: FloraTreeSnow entities: - uid: 7787 @@ -58074,6 +58324,11 @@ entities: - type: Transform pos: -29.530487,10.600556 parent: 2 + - uid: 11174 + components: + - type: Transform + pos: -13.5,-21.5 + parent: 2 - uid: 16963 components: - type: Transform @@ -58168,6 +58423,16 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: FoodFrozenPopsicleOrange + entities: + - uid: 15589 + components: + - type: Transform + parent: 15588 + - type: Physics + canCollide: False + - type: InsideEntityStorage + storage: 15588 - proto: FoodFrozenSnowcone entities: - uid: 7814 @@ -58175,6 +58440,16 @@ entities: - type: Transform pos: 56.6848,-11.972504 parent: 2 +- proto: FoodFrozenSundae + entities: + - uid: 15590 + components: + - type: Transform + parent: 15588 + - type: Physics + canCollide: False + - type: InsideEntityStorage + storage: 15588 - proto: FoodMealMemoryleek entities: - uid: 7815 @@ -58261,6 +58536,25 @@ entities: - type: Transform pos: 0.4270475,20.289186 parent: 2 +- proto: FoodPieFrostySlice + entities: + - uid: 15598 + components: + - type: Transform + pos: -18.49255,-32.444473 + parent: 2 +- proto: FoodPlateSmall + entities: + - uid: 14506 + components: + - type: Transform + pos: -18.492144,-32.413223 + parent: 2 + - uid: 14546 + components: + - type: Transform + pos: -18.507769,-33.288223 + parent: 2 - proto: FoodRicePudding entities: - uid: 7822 @@ -58321,18 +58615,6 @@ entities: canCollide: False - proto: GasCanisterBrokenBase entities: - - uid: 7836 - components: - - type: Transform - pos: -6.5,-30.5 - parent: 2 - - uid: 7837 - components: - - type: MetaData - name: разбитая канистра плазмы - - type: Transform - pos: -20.5,-42.5 - parent: 2 - uid: 18102 components: - type: Transform @@ -58375,11 +58657,43 @@ entities: parent: 16911 - proto: GasFilter entities: - - uid: 7838 + - uid: 8922 components: - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-28.5 + rot: 1.5707963267948966 rad + pos: -0.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13420 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -9.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13522 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13524 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13525 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-33.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -58397,93 +58711,113 @@ entities: rot: -1.5707963267948966 rad pos: -24.5,16.5 parent: 2 + - uid: 13561 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' +- proto: GasMinerCarbonDioxide + entities: + - uid: 9685 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-40.5 + parent: 2 - proto: GasMinerNitrogenStation entities: - - uid: 7841 - components: - - type: Transform - pos: -8.5,-32.5 - parent: 2 -- proto: GasMinerOxygenStation - entities: - - uid: 7842 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-32.5 - parent: 2 -- proto: GasMinerPlasma - entities: - - uid: 7843 - components: - - type: Transform - pos: 1.5,-32.5 - parent: 2 - - type: GasMiner - spawnAmount: 25 -- proto: GasOutletInjector - entities: - - uid: 7844 + - uid: 3096 components: - type: Transform rot: 3.141592653589793 rad - pos: -1.5,-31.5 + pos: -7.5,-40.5 parent: 2 - - uid: 7845 +- proto: GasMinerOxygenStation + entities: + - uid: 651 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,-40.5 + parent: 2 +- proto: GasMinerPlasma + entities: + - uid: 2839 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-37.5 + parent: 2 +- proto: GasMinerWaterVapor + entities: + - uid: 9832 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-40.5 + parent: 2 +- proto: GasOutletInjector + entities: + - uid: 3097 components: - type: Transform rot: 1.5707963267948966 rad - pos: -14.5,-32.5 + pos: -11.5,-40.5 + parent: 2 + - uid: 4028 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-37.5 + parent: 2 + - uid: 4035 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,-40.5 + parent: 2 + - uid: 7847 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-32.5 + parent: 2 + - uid: 9535 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-40.5 + parent: 2 + - uid: 9667 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9844 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-40.5 + parent: 2 +- proto: GasPassiveVent + entities: + - uid: 4036 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-36.5 parent: 2 - uid: 7846 components: - type: Transform rot: 1.5707963267948966 rad - pos: -12.5,-32.5 - parent: 2 - - uid: 7847 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -9.5,-32.5 - parent: 2 - - uid: 7848 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-32.5 - parent: 2 - - uid: 7849 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 0.5,-32.5 - parent: 2 -- proto: GasPassiveVent - entities: - - uid: 7850 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-31.5 - parent: 2 - - uid: 7851 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-32.5 - parent: 2 - - uid: 7852 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-31.5 - parent: 2 - - uid: 7853 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-32.5 + pos: -5.5,-32.5 parent: 2 - uid: 7854 components: @@ -58514,20 +58848,82 @@ entities: - type: Transform pos: -13.5,1.5 parent: 2 - - uid: 7859 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-31.5 - parent: 2 - uid: 7860 components: - type: Transform rot: 1.5707963267948966 rad pos: 58.5,-8.5 parent: 2 + - uid: 9671 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.5,-39.5 + parent: 2 + - uid: 9673 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-39.5 + parent: 2 + - uid: 9850 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-39.5 + parent: 2 + - uid: 9959 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-39.5 + parent: 2 - proto: GasPipeBend entities: + - uid: 3407 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3431 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4031 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -0.5,-40.5 + parent: 2 + - uid: 4034 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-40.5 + parent: 2 + - uid: 7232 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7767 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 7861 components: - type: Transform @@ -58595,12 +58991,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 7870 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.5,-26.5 - parent: 2 - uid: 7871 components: - type: Transform @@ -58633,30 +59023,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 7875 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -7.5,-32.5 - parent: 2 - - uid: 7876 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -10.5,-32.5 - parent: 2 - - uid: 7877 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-32.5 - parent: 2 - - uid: 7878 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-32.5 - parent: 2 - uid: 7879 components: - type: Transform @@ -58789,11 +59155,11 @@ entities: - uid: 7898 components: - type: Transform - rot: 3.141592653589793 rad - pos: -17.5,-15.5 + rot: -1.5707963267948966 rad + pos: -8.5,-19.5 parent: 2 - type: AtmosPipeColor - color: '#0000FFFF' + color: '#FF0000FF' - uid: 7899 components: - type: Transform @@ -58802,14 +59168,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 7900 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 7901 components: - type: Transform @@ -59217,12 +59575,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 7953 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-32.5 - parent: 2 - uid: 7954 components: - type: Transform @@ -59520,13 +59872,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 7993 - components: - - type: Transform - pos: -52.5,34.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 7994 components: - type: Transform @@ -59866,6 +60211,185 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' + - uid: 8227 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8260 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8389 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -23.5,-37.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 10009 + components: + - type: Transform + pos: -5.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 12755 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-40.5 + parent: 2 + - uid: 12947 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-37.5 + parent: 2 + - uid: 13077 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-40.5 + parent: 2 + - uid: 13523 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-33.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13528 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13593 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14020 + components: + - type: Transform + pos: -14.5,-30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14054 + components: + - type: Transform + pos: -53.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14221 + components: + - type: Transform + pos: -51.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14224 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -52.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14228 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -54.5,34.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14271 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -12.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14272 + components: + - type: Transform + pos: -12.5,-33.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16384 + components: + - type: Transform + pos: -22.5,-35.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16385 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -21.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 16386 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -21.5,-35.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 16387 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -23.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 16390 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -22.5,-37.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16391 + components: + - type: Transform + pos: -21.5,-37.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16392 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -21.5,-38.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPipeFourway entities: - uid: 8038 @@ -59901,13 +60425,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8043 - components: - - type: Transform - pos: -18.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8044 components: - type: Transform @@ -60041,8 +60558,489 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 8206 + components: + - type: Transform + pos: -18.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13722 + components: + - type: Transform + pos: -51.5,30.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - proto: GasPipeStraight entities: + - uid: 14 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 135 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-37.5 + parent: 2 + - uid: 158 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 183 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 647 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 649 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 676 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-39.5 + parent: 2 + - uid: 677 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-38.5 + parent: 2 + - uid: 996 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 997 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 1002 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-36.5 + parent: 2 + - uid: 1003 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,-40.5 + parent: 2 + - uid: 2706 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 2707 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-20.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 2708 + components: + - type: Transform + pos: -17.5,-20.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 2709 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 2710 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 2835 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-40.5 + parent: 2 + - uid: 3099 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-38.5 + parent: 2 + - uid: 3100 + components: + - type: Transform + pos: -15.5,-35.5 + parent: 2 + - uid: 3281 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3385 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3386 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3388 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3406 + components: + - type: Transform + pos: -5.5,-38.5 + parent: 2 + - uid: 3427 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3430 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -12.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3432 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -9.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3433 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3434 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3436 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3976 + components: + - type: Transform + pos: -2.5,-38.5 + parent: 2 + - uid: 4030 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-39.5 + parent: 2 + - uid: 4046 + components: + - type: Transform + pos: -8.5,-38.5 + parent: 2 + - uid: 4061 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 4062 + components: + - type: Transform + pos: -17.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4063 + components: + - type: Transform + pos: -17.5,-18.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4947 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 4950 + components: + - type: Transform + pos: -17.5,-22.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4951 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-14.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 4955 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-14.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4956 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4995 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-39.5 + parent: 2 + - uid: 4996 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-40.5 + parent: 2 + - uid: 6119 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -16.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 6123 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-22.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 6141 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 6437 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -14.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 6445 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 6538 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -12.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 6841 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 6999 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7000 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7002 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7003 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7005 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7009 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-18.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7012 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7116 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7117 + components: + - type: Transform + pos: -17.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 7118 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7273 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 7766 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 8063 components: - type: Transform @@ -60072,12 +61070,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8067 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-32.5 - parent: 2 - uid: 8068 components: - type: Transform @@ -60093,18 +61085,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8070 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-30.5 - parent: 2 - - uid: 8071 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-30.5 - parent: 2 - uid: 8072 components: - type: Transform @@ -60143,13 +61123,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8077 - components: - - type: Transform - pos: 22.5,-29.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8078 components: - type: Transform @@ -60188,14 +61161,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8083 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-23.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8084 components: - type: Transform @@ -60204,35 +61169,17 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8085 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-30.5 - parent: 2 - - uid: 8086 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-31.5 - parent: 2 - - uid: 8087 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-30.5 - parent: 2 - uid: 8088 components: - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-31.5 + rot: 1.5707963267948966 rad + pos: -4.5,-32.5 parent: 2 - uid: 8089 components: - type: Transform rot: 1.5707963267948966 rad - pos: -8.5,-32.5 + pos: -0.5,-29.5 parent: 2 - uid: 8090 components: @@ -60250,35 +61197,11 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8092 - components: - - type: Transform - pos: -16.5,-30.5 - parent: 2 - - uid: 8093 - components: - - type: Transform - pos: -16.5,-31.5 - parent: 2 - - uid: 8094 - components: - - type: Transform - pos: -18.5,-31.5 - parent: 2 - uid: 8095 components: - type: Transform - pos: -15.5,-31.5 - parent: 2 - - uid: 8096 - components: - - type: Transform - pos: -18.5,-30.5 - parent: 2 - - uid: 8097 - components: - - type: Transform - pos: -15.5,-30.5 + rot: 1.5707963267948966 rad + pos: -1.5,-29.5 parent: 2 - uid: 8098 components: @@ -60472,12 +61395,8 @@ entities: - uid: 8123 components: - type: Transform - pos: -13.5,-30.5 - parent: 2 - - uid: 8124 - components: - - type: Transform - pos: -13.5,-31.5 + rot: 1.5707963267948966 rad + pos: -8.5,-32.5 parent: 2 - uid: 8125 components: @@ -60814,20 +61733,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8169 - components: - - type: Transform - pos: -52.5,29.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8170 - components: - - type: Transform - pos: -52.5,30.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8171 components: - type: Transform @@ -60957,13 +61862,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8189 - components: - - type: Transform - pos: 23.5,-31.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8190 components: - type: Transform @@ -61021,156 +61919,7 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8198 - components: - - type: Transform - pos: -16.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8199 - components: - - type: Transform - pos: -16.5,-21.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8200 - components: - - type: Transform - pos: -16.5,-19.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8201 - components: - - type: Transform - pos: -18.5,-18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8202 - components: - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8203 - components: - - type: Transform - pos: -16.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8204 - components: - - type: Transform - pos: -18.5,-19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8205 - components: - - type: Transform - pos: -18.5,-17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8206 - components: - - type: Transform - pos: -18.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8207 - components: - - type: Transform - pos: -18.5,-14.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8208 - components: - - type: Transform - pos: -18.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8209 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -17.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8210 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8211 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8212 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8213 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8214 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -12.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8215 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8216 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -14.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8217 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8218 components: - type: Transform rot: 1.5707963267948966 rad @@ -61178,35 +61927,59 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 8202 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8203 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8204 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-16.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8214 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8217 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8218 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 8219 components: - type: Transform rot: 1.5707963267948966 rad - pos: -14.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8220 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -12.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8221 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8222 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-15.5 + pos: -16.5,-15.5 parent: 2 - type: AtmosPipeColor color: '#0000FFFF' @@ -61238,14 +62011,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8227 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8228 components: - type: Transform @@ -61438,118 +62203,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8253 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8254 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-17.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8255 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8256 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-18.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8257 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-19.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8258 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8259 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-21.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8260 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-17.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8261 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-19.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8262 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8263 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-21.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8264 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-18.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8265 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-23.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8266 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-23.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8267 components: - type: Transform @@ -62472,10 +63125,8 @@ entities: components: - type: Transform rot: 1.5707963267948966 rad - pos: -22.5,-35.5 + pos: 0.5,-29.5 parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 8388 components: - type: Transform @@ -62484,70 +63135,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8389 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-37.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8390 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -22.5,-37.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8391 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8392 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8393 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8394 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8395 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8396 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8397 components: - type: Transform @@ -62597,18 +63184,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 8404 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-30.5 - parent: 2 - - uid: 8405 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-30.5 - parent: 2 - uid: 8406 components: - type: Transform @@ -65017,38 +65592,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8724 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -53.5,28.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 8725 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,31.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8726 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,32.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 8727 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,33.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 8728 components: - type: Transform @@ -66496,17 +67039,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 8922 - components: - - type: Transform - pos: 2.5,-31.5 - parent: 2 - - uid: 8923 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 1.5,-32.5 - parent: 2 - uid: 8924 components: - type: Transform @@ -68078,24 +68610,11 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9124 - components: - - type: Transform - pos: -52.5,31.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9125 - components: - - type: Transform - pos: -52.5,32.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9126 components: - type: Transform - pos: -52.5,33.5 + rot: -1.5707963267948966 rad + pos: 22.5,-31.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -69411,11 +69930,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9297 - components: - - type: Transform - pos: 2.5,-30.5 - parent: 2 - uid: 9298 components: - type: Transform @@ -70156,12 +70670,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9398 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-30.5 - parent: 2 - uid: 9399 components: - type: Transform @@ -70340,8 +70848,541 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' + - uid: 9444 + components: + - type: Transform + pos: 22.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9501 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -12.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9503 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 9508 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -12.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9583 + components: + - type: Transform + pos: 23.5,-32.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 9661 + components: + - type: Transform + pos: -11.5,-38.5 + parent: 2 + - uid: 10010 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10011 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10012 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10026 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10541 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 11829 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 12571 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-40.5 + parent: 2 + - uid: 12572 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-35.5 + parent: 2 + - uid: 12733 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 21.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 12734 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 18.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 12872 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-38.5 + parent: 2 + - uid: 12873 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-39.5 + parent: 2 + - uid: 12927 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-38.5 + parent: 2 + - uid: 13559 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -16.5,-21.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13562 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-30.5 + parent: 2 + - uid: 13563 + components: + - type: Transform + pos: -18.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13566 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 18.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13581 + components: + - type: Transform + pos: -18.5,-28.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13594 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13623 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 21.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13655 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -51.5,31.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13714 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -51.5,32.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13715 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -51.5,33.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13716 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,33.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13717 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,32.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13718 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13719 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13720 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,29.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13723 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -50.5,30.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 13755 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -52.5,28.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14018 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14019 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-28.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 14086 + components: + - type: Transform + pos: -14.5,-32.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14087 + components: + - type: Transform + pos: -14.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14088 + components: + - type: Transform + pos: -12.5,-35.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14130 + components: + - type: Transform + pos: -12.5,-34.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14157 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14171 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14205 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14210 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14264 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14268 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14269 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14270 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14283 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14284 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14289 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14293 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14294 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14309 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14310 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14433 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 16388 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -22.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 16389 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -22.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - proto: GasPipeTJunction entities: + - uid: 2711 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-19.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3424 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3440 + components: + - type: Transform + pos: -16.5,-17.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4328 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -17.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8077 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 22.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8189 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 23.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 8205 + components: + - type: Transform + pos: -10.5,-15.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8213 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8215 + components: + - type: Transform + pos: -16.5,-23.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 8216 + components: + - type: Transform + pos: -8.5,-13.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 9422 components: - type: Transform @@ -70358,18 +71399,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9424 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -5.5,-26.5 - parent: 2 - - uid: 9425 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-26.5 - parent: 2 - uid: 9426 components: - type: Transform @@ -70390,13 +71419,6 @@ entities: rot: -1.5707963267948966 rad pos: -26.5,17.5 parent: 2 - - uid: 9429 - components: - - type: Transform - pos: -51.5,30.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9430 components: - type: Transform @@ -70458,12 +71480,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9439 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -13.5,-26.5 - parent: 2 - uid: 9440 components: - type: Transform @@ -70495,14 +71511,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9444 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-31.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9445 components: - type: Transform @@ -70711,14 +71719,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9472 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -52.5,28.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9473 components: - type: Transform @@ -70859,14 +71859,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9491 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -18.5,-21.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9492 components: - type: Transform @@ -70929,75 +71921,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9500 - components: - - type: Transform - pos: -8.5,-16.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9501 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9502 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -18.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9503 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -18.5,-20.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9504 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-23.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9505 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-18.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9506 - components: - - type: Transform - pos: -16.5,-15.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9507 - components: - - type: Transform - pos: -16.5,-13.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9508 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-14.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9509 components: - type: Transform @@ -71031,7 +71954,7 @@ entities: - uid: 9513 components: - type: Transform - pos: -6.5,-14.5 + pos: -13.5,-21.5 parent: 2 - type: AtmosPipeColor color: '#FF0000FF' @@ -71187,18 +72110,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9534 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-32.5 - parent: 2 - - uid: 9535 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-31.5 - parent: 2 - uid: 9536 components: - type: Transform @@ -71457,14 +72368,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9569 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,30.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9570 components: - type: Transform @@ -71567,14 +72470,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9583 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 23.5,-32.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9584 components: - type: Transform @@ -72162,32 +73057,57 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 13591 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13592 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-27.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13721 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,28.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasPort entities: - - uid: 9660 - components: - - type: Transform - pos: -5.5,-25.5 - parent: 2 - - uid: 9661 - components: - - type: Transform - pos: -4.5,-25.5 - parent: 2 - uid: 9662 components: - type: Transform pos: -24.5,-12.5 parent: 2 - - uid: 9663 + - uid: 10004 components: - type: Transform - pos: -13.5,-25.5 + rot: -1.5707963267948966 rad + pos: -14.5,-27.5 parent: 2 - - uid: 9664 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 10051 components: - type: Transform - pos: -12.5,-25.5 + rot: -1.5707963267948966 rad + pos: -14.5,-26.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 14311 + components: + - type: Transform + pos: -17.5,-29.5 parent: 2 - uid: 18110 components: @@ -72197,58 +73117,55 @@ entities: parent: 16911 - proto: GasPressurePump entities: - - uid: 9665 - components: - - type: Transform - pos: 2.5,-29.5 - parent: 2 - - uid: 9666 + - uid: 5 components: - type: Transform rot: 3.141592653589793 rad - pos: -12.5,-29.5 + pos: -15.5,-34.5 parent: 2 - - uid: 9667 + - uid: 234 components: - type: Transform - pos: -13.5,-29.5 + pos: -9.5,-37.5 parent: 2 - - uid: 9668 + - uid: 3977 + components: + - type: Transform + pos: -13.5,-34.5 + parent: 2 + - uid: 4026 + components: + - type: Transform + pos: -6.5,-37.5 + parent: 2 + - uid: 6038 components: - type: Transform rot: 3.141592653589793 rad - pos: -9.5,-29.5 + pos: -5.5,-37.5 parent: 2 - - uid: 9669 + - uid: 6429 components: - type: Transform rot: 3.141592653589793 rad - pos: -15.5,-29.5 + pos: -2.5,-37.5 + parent: 2 + - uid: 7406 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-37.5 + parent: 2 + - uid: 7953 + components: + - type: Transform + pos: -3.5,-37.5 parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9670 components: - type: Transform - pos: -7.5,-29.5 - parent: 2 - - uid: 9671 - components: - - type: Transform - pos: -10.5,-29.5 - parent: 2 - - uid: 9672 - components: - - type: Transform - pos: -16.5,-29.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9673 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-29.5 + rot: 1.5707963267948966 rad + pos: -9.5,-32.5 parent: 2 - uid: 9674 components: @@ -72260,12 +73177,6 @@ entities: - type: Transform pos: -22.5,-14.5 parent: 2 - - uid: 9676 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-26.5 - parent: 2 - uid: 9677 components: - type: MetaData @@ -72286,17 +73197,32 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9679 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-26.5 - parent: 2 - - uid: 9680 + - uid: 9992 components: - type: Transform rot: 3.141592653589793 rad - pos: 0.5,-29.5 + pos: -11.5,-37.5 + parent: 2 + - uid: 12552 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -15.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 12553 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -15.5,-30.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 13411 + components: + - type: Transform + pos: -0.5,-37.5 parent: 2 - proto: GasThermoMachineFreezer entities: @@ -72318,6 +73244,16 @@ entities: rot: -1.5707963267948966 rad pos: 22.5,22.5 parent: 2 + - uid: 11156 + components: + - type: Transform + pos: -1.5,-25.5 + parent: 2 + - uid: 11157 + components: + - type: Transform + pos: -1.5,-26.5 + parent: 2 - proto: GasThermoMachineFreezerEnabled entities: - uid: 9684 @@ -72330,13 +73266,64 @@ entities: targetTemperature: 73.15 - proto: GasThermoMachineHeater entities: - - uid: 9685 + - uid: 11155 components: - type: Transform - pos: -7.5,-25.5 + pos: -2.5,-25.5 parent: 2 + - uid: 11183 + components: + - type: Transform + pos: -2.5,-26.5 + parent: 2 +- proto: GasValve + entities: + - uid: 7837 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-32.5 + parent: 2 + - uid: 9668 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-36.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' - proto: GasVentPump entities: + - uid: 3293 + components: + - type: Transform + pos: -14.5,-18.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 3438 + components: + - type: Transform + pos: -15.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 4810 + components: + - type: Transform + pos: -7.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 170 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 9686 components: - type: Transform @@ -72458,17 +73445,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9699 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-22.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 10 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9700 components: - type: Transform @@ -72480,17 +73456,6 @@ entities: - 18 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9701 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -17.5,-18.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9702 components: - type: Transform @@ -72566,42 +73531,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,-38.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9710 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -21.5,-37.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9711 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -18.5,-14.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9712 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-20.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 17 - type: AtmosPipeColor color: '#0000FFFF' - uid: 9713 @@ -72816,28 +73745,6 @@ entities: - 97 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9733 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 24.5,-25.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - - type: AtmosPipeColor - color: '#0000FFFF' - - uid: 9734 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 23.5,-31.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 99 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9735 components: - type: Transform @@ -73351,13 +74258,6 @@ entities: - 71 - type: AtmosPipeColor color: '#0000FFFF' - - uid: 9785 - components: - - type: Transform - pos: -50.5,34.5 - parent: 2 - - type: AtmosPipeColor - color: '#0000FFFF' - uid: 9786 components: - type: Transform @@ -73695,13 +74595,86 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0000FFFF' + - uid: 9922 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 17.5,-29.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' + - type: DeviceNetwork + deviceLists: + - 1509 + - uid: 15613 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -20.5,-35.5 + parent: 2 + - type: AtmosPipeColor + color: '#0000FFFF' - uid: 18111 components: - type: Transform pos: 15.5,0.5 parent: 16911 + - uid: 19370 + components: + - type: Transform + pos: -52.5,35.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 19372 + - type: AtmosPipeColor + color: '#0000FFFF' - proto: GasVentScrubber entities: + - uid: 169 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 170 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 1507 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 17.5,-31.5 + parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - type: DeviceNetwork + deviceLists: + - 1509 + - uid: 3291 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -16.5,-18.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#0000FFFF' + - uid: 3443 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-22.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 993 + - type: AtmosPipeColor + color: '#FF0000FF' - uid: 9821 components: - type: Transform @@ -73720,14 +74693,6 @@ entities: - 66 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9823 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-22.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9824 components: - type: Transform @@ -73813,23 +74778,6 @@ entities: - 91 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9832 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-31.5 - parent: 2 - - uid: 9833 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-22.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 10 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9834 components: - type: Transform @@ -73841,17 +74789,6 @@ entities: - 18 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9835 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-20.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 14 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9836 components: - type: Transform @@ -73928,31 +74865,6 @@ entities: rot: -1.5707963267948966 rad pos: -25.5,-36.5 parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9844 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -21.5,-35.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 11 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9845 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -16.5,-14.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 15 - type: AtmosPipeColor color: '#FF0000FF' - uid: 9846 @@ -73985,18 +74897,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9849 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-32.5 - parent: 2 - - uid: 9850 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-32.5 - parent: 2 - uid: 9851 components: - type: Transform @@ -74144,28 +75044,6 @@ entities: - 96 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9865 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 23.5,-26.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 98 - - type: AtmosPipeColor - color: '#FF0000FF' - - uid: 9866 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 22.5,-32.5 - parent: 2 - - type: DeviceNetwork - deviceLists: - - 99 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9867 components: - type: Transform @@ -74720,14 +75598,6 @@ entities: - 71 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 9922 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -53.5,34.5 - parent: 2 - - type: AtmosPipeColor - color: '#FF0000FF' - uid: 9923 components: - type: Transform @@ -75082,14 +75952,26 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' -- proto: GasVolumePump - entities: - - uid: 9959 + - uid: 15612 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -6.5,-26.5 + rot: -1.5707963267948966 rad + pos: -20.5,-38.5 parent: 2 + - type: AtmosPipeColor + color: '#FF0000FF' + - uid: 19371 + components: + - type: Transform + pos: -54.5,35.5 + parent: 2 + - type: DeviceNetwork + deviceLists: + - 19372 + - type: AtmosPipeColor + color: '#FF0000FF' +- proto: GasVolumePump + entities: - uid: 9960 components: - type: Transform @@ -75254,20 +76136,220 @@ entities: - type: Transform pos: -35.5,-31.5 parent: 2 -- proto: GrenadeStinger - entities: - - uid: 9973 - components: - - type: Transform - pos: -10.396269,-22.727524 - parent: 2 - - uid: 9974 - components: - - type: Transform - pos: -10.599394,-22.555649 - parent: 2 - proto: Grille entities: + - uid: 99 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-29.5 + parent: 2 + - uid: 419 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-30.5 + parent: 2 + - uid: 425 + components: + - type: Transform + pos: 3.5,-42.5 + parent: 2 + - uid: 456 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 16.5,-33.5 + parent: 2 + - uid: 759 + components: + - type: Transform + pos: -10.5,-38.5 + parent: 2 + - uid: 762 + components: + - type: Transform + pos: -6.5,-31.5 + parent: 2 + - uid: 866 + components: + - type: Transform + pos: -12.5,-43.5 + parent: 2 + - uid: 968 + components: + - type: Transform + pos: -7.5,-41.5 + parent: 2 + - uid: 978 + components: + - type: Transform + pos: -11.5,-41.5 + parent: 2 + - uid: 979 + components: + - type: Transform + pos: -7.5,-38.5 + parent: 2 + - uid: 988 + components: + - type: Transform + pos: -4.5,-38.5 + parent: 2 + - uid: 1001 + components: + - type: Transform + pos: -5.5,-31.5 + parent: 2 + - uid: 1226 + components: + - type: Transform + pos: -1.5,-42.5 + parent: 2 + - uid: 1274 + components: + - type: Transform + pos: 3.5,-43.5 + parent: 2 + - uid: 1275 + components: + - type: Transform + pos: -18.5,-43.5 + parent: 2 + - uid: 1276 + components: + - type: Transform + pos: -20.5,-47.5 + parent: 2 + - uid: 1279 + components: + - type: Transform + pos: -20.5,-45.5 + parent: 2 + - uid: 2818 + components: + - type: Transform + pos: -2.5,-41.5 + parent: 2 + - uid: 2841 + components: + - type: Transform + pos: -4.5,-32.5 + parent: 2 + - uid: 2842 + components: + - type: Transform + pos: -8.5,-41.5 + parent: 2 + - uid: 2845 + components: + - type: Transform + pos: -10.5,-41.5 + parent: 2 + - uid: 2863 + components: + - type: Transform + pos: -13.5,-37.5 + parent: 2 + - uid: 3089 + components: + - type: Transform + pos: -14.5,-35.5 + parent: 2 + - uid: 3103 + components: + - type: Transform + pos: -9.5,-42.5 + parent: 2 + - uid: 3104 + components: + - type: Transform + pos: -7.5,-42.5 + parent: 2 + - uid: 3418 + components: + - type: Transform + pos: -5.5,-42.5 + parent: 2 + - uid: 4011 + components: + - type: Transform + pos: -16.5,-43.5 + parent: 2 + - uid: 4027 + components: + - type: Transform + pos: -4.5,-41.5 + parent: 2 + - uid: 4038 + components: + - type: Transform + pos: -1.5,-38.5 + parent: 2 + - uid: 5744 + components: + - type: Transform + pos: -7.5,-33.5 + parent: 2 + - uid: 5745 + components: + - type: Transform + pos: -6.5,-33.5 + parent: 2 + - uid: 7004 + components: + - type: Transform + pos: -14.5,-43.5 + parent: 2 + - uid: 7407 + components: + - type: Transform + pos: -5.5,-33.5 + parent: 2 + - uid: 7768 + components: + - type: Transform + pos: -3.5,-42.5 + parent: 2 + - uid: 7870 + components: + - type: Transform + pos: -13.5,-36.5 + parent: 2 + - uid: 8390 + components: + - type: Transform + pos: -1.5,-41.5 + parent: 2 + - uid: 8923 + components: + - type: Transform + pos: -7.5,-31.5 + parent: 2 + - uid: 9733 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 18.5,-33.5 + parent: 2 + - uid: 9734 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 18.5,-27.5 + parent: 2 + - uid: 9865 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-31.5 + parent: 2 + - uid: 9866 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 16.5,-27.5 + parent: 2 - uid: 9975 components: - type: Transform @@ -75348,26 +76430,6 @@ entities: - type: Transform pos: 2.5,-10.5 parent: 2 - - uid: 9991 - components: - - type: Transform - pos: -12.5,-35.5 - parent: 2 - - uid: 9992 - components: - - type: Transform - pos: -1.5,-35.5 - parent: 2 - - uid: 9993 - components: - - type: Transform - pos: -10.5,-35.5 - parent: 2 - - uid: 9994 - components: - - type: Transform - pos: 0.5,-35.5 - parent: 2 - uid: 9995 components: - type: Transform @@ -75388,96 +76450,11 @@ entities: - type: Transform pos: 3.5,-35.5 parent: 2 - - uid: 9999 - components: - - type: Transform - pos: 2.5,-35.5 - parent: 2 - uid: 10000 components: - type: Transform pos: 16.5,-17.5 parent: 2 - - uid: 10001 - components: - - type: Transform - pos: 1.5,-35.5 - parent: 2 - - uid: 10002 - components: - - type: Transform - pos: -9.5,-35.5 - parent: 2 - - uid: 10003 - components: - - type: Transform - pos: -11.5,-35.5 - parent: 2 - - uid: 10004 - components: - - type: Transform - pos: -4.5,-35.5 - parent: 2 - - uid: 10005 - components: - - type: Transform - pos: -8.5,-35.5 - parent: 2 - - uid: 10006 - components: - - type: Transform - pos: -3.5,-35.5 - parent: 2 - - uid: 10007 - components: - - type: Transform - pos: -2.5,-35.5 - parent: 2 - - uid: 10008 - components: - - type: Transform - pos: -10.5,-20.5 - parent: 2 - - uid: 10009 - components: - - type: Transform - pos: -9.5,-20.5 - parent: 2 - - uid: 10010 - components: - - type: Transform - pos: -9.5,-17.5 - parent: 2 - - uid: 10011 - components: - - type: Transform - pos: -10.5,-17.5 - parent: 2 - - uid: 10012 - components: - - type: Transform - pos: -11.5,-18.5 - parent: 2 - - uid: 10013 - components: - - type: Transform - pos: -5.5,-17.5 - parent: 2 - - uid: 10014 - components: - - type: Transform - pos: -4.5,-17.5 - parent: 2 - - uid: 10015 - components: - - type: Transform - pos: -5.5,-20.5 - parent: 2 - - uid: 10016 - components: - - type: Transform - pos: -4.5,-20.5 - parent: 2 - uid: 10017 components: - type: Transform @@ -75498,11 +76475,6 @@ entities: - type: Transform pos: 14.5,-6.5 parent: 2 - - uid: 10021 - components: - - type: Transform - pos: -13.5,-35.5 - parent: 2 - uid: 10022 components: - type: Transform @@ -75517,17 +76489,7 @@ entities: - uid: 10024 components: - type: Transform - pos: -14.5,-35.5 - parent: 2 - - uid: 10025 - components: - - type: Transform - pos: -0.5,-35.5 - parent: 2 - - uid: 10026 - components: - - type: Transform - pos: -51.5,36.5 + pos: -5.5,-41.5 parent: 2 - uid: 10027 components: @@ -75586,24 +76548,6 @@ entities: rot: 3.141592653589793 rad pos: -26.5,-11.5 parent: 2 - - uid: 10037 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-14.5 - parent: 2 - - uid: 10038 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -13.5,-14.5 - parent: 2 - - uid: 10039 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -14.5,-14.5 - parent: 2 - uid: 10040 components: - type: Transform @@ -75628,11 +76572,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,-6.5 parent: 2 - - uid: 10044 - components: - - type: Transform - pos: -54.5,35.5 - parent: 2 - uid: 10045 components: - type: Transform @@ -75651,69 +76590,11 @@ entities: rot: 3.141592653589793 rad pos: -24.5,-4.5 parent: 2 - - uid: 10048 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-33.5 - parent: 2 - - uid: 10049 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-33.5 - parent: 2 - - uid: 10050 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -11.5,-33.5 - parent: 2 - - uid: 10051 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-33.5 - parent: 2 - uid: 10052 components: - type: Transform pos: -27.5,-36.5 parent: 2 - - uid: 10053 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -11.5,-30.5 - parent: 2 - - uid: 10054 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-30.5 - parent: 2 - - uid: 10055 - components: - - type: Transform - pos: -5.5,-35.5 - parent: 2 - - uid: 10056 - components: - - type: Transform - pos: -15.5,-35.5 - parent: 2 - - uid: 10057 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-33.5 - parent: 2 - - uid: 10058 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 1.5,-33.5 - parent: 2 - uid: 10059 components: - type: Transform @@ -75757,11 +76638,6 @@ entities: rot: -1.5707963267948966 rad pos: 11.5,-8.5 parent: 2 - - uid: 10067 - components: - - type: Transform - pos: -17.5,-37.5 - parent: 2 - uid: 10068 components: - type: Transform @@ -75833,16 +76709,6 @@ entities: - type: Transform pos: 2.5,-13.5 parent: 2 - - uid: 10082 - components: - - type: Transform - pos: -17.5,-36.5 - parent: 2 - - uid: 10083 - components: - - type: Transform - pos: -17.5,-38.5 - parent: 2 - uid: 10084 components: - type: Transform @@ -76009,26 +76875,6 @@ entities: - type: Transform pos: 21.5,-26.5 parent: 2 - - uid: 10117 - components: - - type: Transform - pos: 21.5,-27.5 - parent: 2 - - uid: 10118 - components: - - type: Transform - pos: 21.5,-32.5 - parent: 2 - - uid: 10119 - components: - - type: Transform - pos: 21.5,-31.5 - parent: 2 - - uid: 10120 - components: - - type: Transform - pos: 21.5,-33.5 - parent: 2 - uid: 10121 components: - type: Transform @@ -76136,11 +76982,6 @@ entities: - type: Transform pos: -27.5,-34.5 parent: 2 - - uid: 10141 - components: - - type: Transform - pos: -7.5,-35.5 - parent: 2 - uid: 10142 components: - type: Transform @@ -76151,11 +76992,6 @@ entities: - type: Transform pos: -27.5,-37.5 parent: 2 - - uid: 10144 - components: - - type: Transform - pos: -6.5,-35.5 - parent: 2 - uid: 10145 components: - type: Transform @@ -76336,11 +77172,6 @@ entities: - type: Transform pos: -35.5,-7.5 parent: 2 - - uid: 10181 - components: - - type: Transform - pos: -2.5,-30.5 - parent: 2 - uid: 10182 components: - type: Transform @@ -76546,11 +77377,6 @@ entities: rot: 1.5707963267948966 rad pos: 36.5,-3.5 parent: 2 - - uid: 10221 - components: - - type: Transform - pos: -17.5,-35.5 - parent: 2 - uid: 10222 components: - type: Transform @@ -76606,16 +77432,6 @@ entities: - type: Transform pos: 26.5,-8.5 parent: 2 - - uid: 10233 - components: - - type: Transform - pos: -17.5,-30.5 - parent: 2 - - uid: 10234 - components: - - type: Transform - pos: -14.5,-30.5 - parent: 2 - uid: 10235 components: - type: Transform @@ -76631,11 +77447,6 @@ entities: - type: Transform pos: -16.5,-24.5 parent: 2 - - uid: 10238 - components: - - type: Transform - pos: -16.5,-35.5 - parent: 2 - uid: 10239 components: - type: Transform @@ -77082,11 +77893,6 @@ entities: - type: Transform pos: 38.5,-22.5 parent: 2 - - uid: 10322 - components: - - type: Transform - pos: -11.5,-19.5 - parent: 2 - uid: 10323 components: - type: Transform @@ -77122,12 +77928,6 @@ entities: - type: Transform pos: 46.5,-4.5 parent: 2 - - uid: 10330 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 1.5,-30.5 - parent: 2 - uid: 10331 components: - type: Transform @@ -78199,28 +78999,14 @@ entities: - uid: 10540 components: - type: Transform - pos: -54.5,33.5 - parent: 2 - - uid: 10541 - components: - - type: Transform - pos: -53.5,36.5 - parent: 2 - - uid: 10542 - components: - - type: Transform - pos: -57.5,33.5 + rot: -1.5707963267948966 rad + pos: -53.5,37.5 parent: 2 - uid: 10543 components: - type: Transform pos: -52.5,39.5 parent: 2 - - uid: 10544 - components: - - type: Transform - pos: -51.5,39.5 - parent: 2 - uid: 10545 components: - type: Transform @@ -78567,11 +79353,6 @@ entities: - type: Transform pos: 4.5,-49.5 parent: 2 - - uid: 10613 - components: - - type: Transform - pos: 3.5,-44.5 - parent: 2 - uid: 10614 components: - type: Transform @@ -78582,11 +79363,6 @@ entities: - type: Transform pos: 18.5,-49.5 parent: 2 - - uid: 10616 - components: - - type: Transform - pos: 3.5,-42.5 - parent: 2 - uid: 10617 components: - type: Transform @@ -78704,11 +79480,6 @@ entities: - type: Transform pos: -44.5,47.5 parent: 2 - - uid: 10640 - components: - - type: Transform - pos: -19.5,-38.5 - parent: 2 - uid: 10641 components: - type: Transform @@ -78960,37 +79731,33 @@ entities: - type: Transform pos: 3.5,-21.5 parent: 2 - - uid: 10691 + - uid: 12732 components: - type: Transform rot: -1.5707963267948966 rad - pos: -2.5,-33.5 + pos: -51.5,35.5 parent: 2 - - uid: 10692 + - uid: 12735 + components: + - type: Transform + pos: -54.5,39.5 + parent: 2 + - uid: 14281 components: - type: Transform rot: -1.5707963267948966 rad - pos: -1.5,-33.5 + pos: 1.5,-36.5 parent: 2 - - uid: 10693 + - uid: 15322 components: - type: Transform - pos: -19.5,-34.5 + pos: -57.5,36.5 parent: 2 - - uid: 10694 + - uid: 15604 components: - type: Transform - pos: -19.5,-35.5 - parent: 2 - - uid: 10695 - components: - - type: Transform - pos: -19.5,-37.5 - parent: 2 - - uid: 10696 - components: - - type: Transform - pos: -19.5,-36.5 + rot: -1.5707963267948966 rad + pos: -55.5,35.5 parent: 2 - uid: 18119 components: @@ -79758,6 +80525,81 @@ entities: parent: 2 - proto: GrilleSpawner entities: + - uid: 1082 + components: + - type: Transform + pos: -15.5,-45.5 + parent: 2 + - uid: 1242 + components: + - type: Transform + pos: -18.5,-47.5 + parent: 2 + - uid: 1273 + components: + - type: Transform + pos: 0.5,-44.5 + parent: 2 + - uid: 1277 + components: + - type: Transform + pos: -17.5,-45.5 + parent: 2 + - uid: 7900 + components: + - type: Transform + pos: -7.5,-44.5 + parent: 2 + - uid: 8043 + components: + - type: Transform + pos: -12.5,-45.5 + parent: 2 + - uid: 8083 + components: + - type: Transform + pos: -11.5,-45.5 + parent: 2 + - uid: 8093 + components: + - type: Transform + pos: -13.5,-45.5 + parent: 2 + - uid: 8170 + components: + - type: Transform + pos: -1.5,-44.5 + parent: 2 + - uid: 8198 + components: + - type: Transform + pos: -4.5,-44.5 + parent: 2 + - uid: 8199 + components: + - type: Transform + pos: -5.5,-44.5 + parent: 2 + - uid: 8200 + components: + - type: Transform + pos: -0.5,-44.5 + parent: 2 + - uid: 8254 + components: + - type: Transform + pos: -3.5,-44.5 + parent: 2 + - uid: 8259 + components: + - type: Transform + pos: -9.5,-44.5 + parent: 2 + - uid: 10008 + components: + - type: Transform + pos: -16.5,-45.5 + parent: 2 - uid: 10728 components: - type: Transform @@ -79858,21 +80700,11 @@ entities: - type: Transform pos: -0.5,3.5 parent: 2 - - uid: 10748 - components: - - type: Transform - pos: -15.5,-49.5 - parent: 2 - uid: 10749 components: - type: Transform pos: -0.5,-0.5 parent: 2 - - uid: 10750 - components: - - type: Transform - pos: -16.5,-49.5 - parent: 2 - uid: 10751 components: - type: Transform @@ -80873,11 +81705,6 @@ entities: - type: Transform pos: 43.5,-46.5 parent: 2 - - uid: 10951 - components: - - type: Transform - pos: 2.5,-37.5 - parent: 2 - uid: 10952 components: - type: Transform @@ -80953,96 +81780,6 @@ entities: - type: Transform pos: 23.5,-47.5 parent: 2 - - uid: 10967 - components: - - type: Transform - pos: 1.5,-37.5 - parent: 2 - - uid: 10968 - components: - - type: Transform - pos: 0.5,-37.5 - parent: 2 - - uid: 10969 - components: - - type: Transform - pos: -1.5,-37.5 - parent: 2 - - uid: 10970 - components: - - type: Transform - pos: -3.5,-37.5 - parent: 2 - - uid: 10971 - components: - - type: Transform - pos: -2.5,-37.5 - parent: 2 - - uid: 10972 - components: - - type: Transform - pos: -13.5,-37.5 - parent: 2 - - uid: 10973 - components: - - type: Transform - pos: -5.5,-37.5 - parent: 2 - - uid: 10974 - components: - - type: Transform - pos: -7.5,-37.5 - parent: 2 - - uid: 10975 - components: - - type: Transform - pos: -6.5,-37.5 - parent: 2 - - uid: 10976 - components: - - type: Transform - pos: -9.5,-37.5 - parent: 2 - - uid: 10977 - components: - - type: Transform - pos: -11.5,-37.5 - parent: 2 - - uid: 10978 - components: - - type: Transform - pos: -10.5,-37.5 - parent: 2 - - uid: 10979 - components: - - type: Transform - pos: -14.5,-37.5 - parent: 2 - - uid: 10980 - components: - - type: Transform - pos: -14.5,-47.5 - parent: 2 - - uid: 10981 - components: - - type: Transform - pos: -14.5,-46.5 - parent: 2 - - uid: 10982 - components: - - type: Transform - pos: -14.5,-48.5 - parent: 2 - - uid: 10983 - components: - - type: Transform - pos: -17.5,-49.5 - parent: 2 - - uid: 10984 - components: - - type: Transform - pos: -19.5,-49.5 - parent: 2 - uid: 10985 components: - type: Transform @@ -81051,8 +81788,9 @@ entities: - uid: 10986 components: - type: Transform - pos: -20.5,-49.5 - parent: 2 + anchored: False + pos: -23.286892,-48.197464 + parent: 1 - uid: 10987 components: - type: Transform @@ -81880,36 +82618,26 @@ entities: - type: Transform pos: -32.5,-41.5 parent: 2 - - uid: 11152 + - uid: 11310 components: - type: Transform - pos: -14.5,-44.5 + pos: -18.5,-46.5 parent: 2 - - uid: 11153 + - uid: 11337 components: - type: Transform - pos: -14.5,-42.5 + pos: -18.5,-48.5 parent: 2 - - uid: 11154 + - uid: 13653 components: - type: Transform - pos: -14.5,-43.5 + pos: -8.5,-44.5 parent: 2 - - uid: 11155 + - uid: 14230 components: - type: Transform - pos: -14.5,-40.5 - parent: 2 - - uid: 11156 - components: - - type: Transform - pos: -14.5,-38.5 - parent: 2 - - uid: 11157 - components: - - type: Transform - pos: -14.5,-39.5 - parent: 2 + pos: -19.5,-49.5 + parent: 14229 - proto: GunSafe entities: - uid: 1149 @@ -81928,18 +82656,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -82103,20 +82821,20 @@ entities: parent: 16911 - proto: HighSecCommandLocked entities: + - uid: 193 + components: + - type: Transform + pos: -52.5,33.5 + parent: 2 - uid: 11173 components: - type: Transform pos: -35.5,-25.5 parent: 2 - - uid: 11174 + - uid: 13006 components: - type: Transform - pos: -7.5,-20.5 - parent: 2 - - uid: 11175 - components: - - type: Transform - pos: -7.5,-17.5 + pos: -52.5,31.5 parent: 2 - proto: HolopadCargoBay entities: @@ -82160,6 +82878,13 @@ entities: - type: Transform pos: 5.5,-7.5 parent: 2 +- proto: HolopadCommandVault + entities: + - uid: 4661 + components: + - type: Transform + pos: -53.5,35.5 + parent: 2 - proto: HolopadEngineeringAME entities: - uid: 11182 @@ -82167,26 +82892,26 @@ entities: - type: Transform pos: -23.5,-19.5 parent: 2 -- proto: HolopadEngineeringAtmosMain +- proto: HolopadEngineeringBreakroom entities: - - uid: 11183 + - uid: 12017 components: - type: Transform - pos: -15.5,-25.5 + pos: -14.5,-22.5 parent: 2 -- proto: HolopadEngineeringAtmosTeg +- proto: HolopadEngineeringFront entities: - - uid: 11184 + - uid: 12018 components: - type: Transform - pos: -2.5,-25.5 + pos: -15.5,-18.5 parent: 2 - proto: HolopadEngineeringMain entities: - - uid: 11185 + - uid: 11351 components: - type: Transform - pos: -23.5,-32.5 + pos: -22.5,-39.5 parent: 2 - proto: HolopadEngineeringTechVault entities: @@ -82223,6 +82948,13 @@ entities: - type: Transform pos: -73.5,17.5 parent: 2 +- proto: HolopadGeneralEVAStorage + entities: + - uid: 19362 + components: + - type: Transform + pos: 16.5,-30.5 + parent: 2 - proto: HolopadGeneralTheater entities: - uid: 11191 @@ -82571,13 +83303,6 @@ entities: - type: Transform pos: -2.492794,-20.447254 parent: 2 -- proto: IngotGold - entities: - - uid: 11247 - components: - - type: Transform - pos: -5.482785,-21.352592 - parent: 2 - proto: IngotGold1 entities: - uid: 6316 @@ -82669,16 +83394,6 @@ entities: - type: Transform pos: 1.5838315,-22.161379 parent: 2 -- proto: IntercomEngineering - entities: - - uid: 11255 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -11.5,-16.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: IntercomMedical entities: - uid: 11256 @@ -82738,6 +83453,18 @@ entities: - type: Transform pos: 0.5,33.5 parent: 2 +- proto: JetpackMiniFilled + entities: + - uid: 19357 + components: + - type: Transform + pos: 15.811466,-29.435337 + parent: 2 + - uid: 19358 + components: + - type: Transform + pos: 15.311466,-29.450962 + parent: 2 - proto: Jug entities: - uid: 11263 @@ -82776,6 +83503,11 @@ entities: - type: Transform pos: 53.5,-12.5 parent: 2 + - uid: 12409 + components: + - type: Transform + pos: -12.5,-21.5 + parent: 2 - uid: 18233 components: - type: Transform @@ -82817,6 +83549,12 @@ entities: parent: 2 - proto: Lamp entities: + - uid: 1280 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.520041,-23.278364 + parent: 2 - uid: 11275 components: - type: Transform @@ -82824,12 +83562,6 @@ entities: parent: 2 - type: Physics canCollide: True - - uid: 11276 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.479099,-23.272646 - parent: 2 - uid: 11277 components: - type: Transform @@ -82955,30 +83687,6 @@ entities: - type: Transform pos: -27.420113,4.4986906 parent: 2 -- proto: LiquidNitrogenCanister - entities: - - uid: 11298 - components: - - type: Transform - pos: -5.5,-31.5 - parent: 2 - - uid: 11299 - components: - - type: Transform - pos: -8.5,-31.5 - parent: 2 -- proto: LiquidOxygenCanister - entities: - - uid: 11300 - components: - - type: Transform - pos: -5.5,-32.5 - parent: 2 - - uid: 11301 - components: - - type: Transform - pos: -11.5,-31.5 - parent: 2 - proto: LiveLetLiveCircuitBoard entities: - uid: 642 @@ -83010,6 +83718,101 @@ entities: fixtures: {} - proto: LockableButtonAtmospherics entities: + - uid: 2853 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-38.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 2851: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 2861 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-35.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 683: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 3085 + components: + - type: Transform + pos: -8.5,-33.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 19388: + - - Pressed + - Toggle + 19390: + - - Pressed + - Toggle + 19389: + - - Pressed + - Toggle + 19394: + - - Pressed + - Toggle + 19391: + - - Pressed + - Toggle + 19392: + - - Pressed + - Toggle + 19393: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 8087 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-38.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 685: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 10049 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-38.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 2862: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} + - uid: 10050 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-38.5 + parent: 2 + - type: DeviceLinkSource + linkedPorts: + 2836: + - - Pressed + - Toggle + - type: Fixtures + fixtures: {} - uid: 11303 components: - type: Transform @@ -83025,36 +83828,6 @@ entities: - Toggle - type: Fixtures fixtures: {} - - uid: 11304 - components: - - type: MetaData - desc: Эта кнопка открывает вход в цистерну и закрывает подачу газа. Не забудьте опустошить цистерну. - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-30.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 968: - - - Pressed - - Toggle - - type: Fixtures - fixtures: {} - - uid: 11305 - components: - - type: MetaData - desc: Эта кнопка открывает вход в цистерну и закрывает подачу газа. Не забудьте опустошить цистерну. - - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-30.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 967: - - - Pressed - - Toggle - - type: Fixtures - fixtures: {} - proto: LockableButtonBar entities: - uid: 11306 @@ -83144,48 +83917,6 @@ entities: - Toggle - type: Fixtures fixtures: {} -- proto: LockableButtonCommand - entities: - - uid: 11309 - components: - - type: MetaData - desc: Эта кнопка выключает режим тревоги. - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-24.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 12017: - - - Pressed - - Off - 12018: - - - Pressed - - Off - - type: Fixtures - fixtures: {} - - uid: 11310 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -6.5,-17.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 994: - - - Pressed - - Toggle - 993: - - - Pressed - - Toggle - 997: - - - Pressed - - Toggle - 996: - - - Pressed - - Toggle - - type: Fixtures - fixtures: {} - proto: LockableButtonEngineering entities: - uid: 11311 @@ -83530,18 +84261,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -83628,10 +84349,10 @@ entities: parent: 2 - proto: LockerChiefEngineerFilled entities: - - uid: 11337 + - uid: 3450 components: - type: Transform - pos: -12.5,-17.5 + pos: -4.5,-23.5 parent: 2 - proto: LockerChiefMedicalOfficerFilled entities: @@ -83646,18 +84367,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -83682,18 +84393,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -83733,37 +84434,37 @@ entities: - type: Transform pos: 22.5,-43.5 parent: 2 -- proto: LockerEngineerFilled +- proto: LockerEngineerFilledHardsuit entities: - - uid: 11350 + - uid: 10141 components: - type: Transform - pos: -20.5,-35.5 + pos: -17.5,-37.5 parent: 2 - - uid: 11351 + - uid: 10144 components: - type: Transform - pos: -20.5,-37.5 + pos: -18.5,-37.5 parent: 2 - - uid: 11352 + - uid: 10181 components: - type: Transform - pos: -20.5,-36.5 + pos: -19.5,-37.5 parent: 2 - - uid: 11353 + - uid: 10221 components: - type: Transform - pos: -22.5,-36.5 + pos: -17.5,-35.5 parent: 2 - - uid: 11354 + - uid: 10233 components: - type: Transform - pos: -22.5,-37.5 + pos: -18.5,-35.5 parent: 2 - - uid: 11355 + - uid: 10234 components: - type: Transform - pos: -22.5,-35.5 + pos: -19.5,-35.5 parent: 2 - proto: LockerEvidence entities: @@ -83815,29 +84516,14 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: LockerFreezerVaultFilled entities: - - uid: 11364 + - uid: 15319 components: - type: Transform - pos: -6.5,-21.5 - parent: 2 - - uid: 11365 - components: - - type: Transform - pos: -8.5,-21.5 + pos: -52.5,36.5 parent: 2 - proto: LockerHeadOfPersonnelFilled entities: @@ -83852,18 +84538,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -83924,18 +84600,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84020,18 +84686,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - uid: 11384 components: - type: Transform @@ -84062,18 +84718,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84101,18 +84747,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84180,18 +84816,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84219,18 +84845,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -84394,16 +85010,6 @@ entities: parent: 2 - proto: MachineFrame entities: - - uid: 11416 - components: - - type: Transform - pos: -9.5,-25.5 - parent: 2 - - uid: 11417 - components: - - type: Transform - pos: -8.5,-25.5 - parent: 2 - uid: 11418 components: - type: Transform @@ -84412,6 +85018,11 @@ entities: parent: 2 - proto: MachineFrameDestroyed entities: + - uid: 10692 + components: + - type: Transform + pos: -13.5,-39.5 + parent: 2 - uid: 18235 components: - type: Transform @@ -84424,10 +85035,10 @@ entities: parent: 16911 - proto: MachineMaterialSilo entities: - - uid: 11419 + - uid: 14161 components: - type: Transform - pos: -4.5,-22.5 + pos: -54.5,36.5 parent: 2 - proto: MagazineBoxPistol entities: @@ -84673,6 +85284,13 @@ entities: - type: Transform pos: -23.199554,64.70748 parent: 2 +- proto: MaterialBones1 + entities: + - uid: 15603 + components: + - type: Transform + pos: -18.445675,-33.381973 + parent: 2 - proto: MaterialCloth1 entities: - uid: 11448 @@ -84697,13 +85315,6 @@ entities: - type: Transform pos: -24.293915,64.147194 parent: 2 -- proto: MedalCase - entities: - - uid: 11452 - components: - - type: Transform - pos: -10.374162,-21.397001 - parent: 2 - proto: MedicalBed entities: - uid: 11453 @@ -85751,6 +86362,21 @@ entities: parent: 2 - proto: NitrogenCanister entities: + - uid: 2833 + components: + - type: Transform + pos: -5.5,-25.5 + parent: 2 + - uid: 5743 + components: + - type: Transform + pos: -7.5,-39.5 + parent: 2 + - uid: 7851 + components: + - type: Transform + pos: -5.5,-26.5 + parent: 2 - uid: 11491 components: - type: Transform @@ -85776,11 +86402,6 @@ entities: - type: Transform pos: -59.5,33.5 parent: 2 - - uid: 11496 - components: - - type: Transform - pos: -53.5,33.5 - parent: 2 - uid: 11497 components: - type: Transform @@ -85806,6 +86427,11 @@ entities: - type: Transform pos: -25.5,38.5 parent: 2 + - uid: 16719 + components: + - type: Transform + pos: 18.5,-30.5 + parent: 2 - proto: NitrousOxideCanister entities: - uid: 11502 @@ -85826,18 +86452,8 @@ entities: immutable: False temperature: 2.7 moles: - - 1310.1974 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 561.5131 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1310.1974 + NitrousOxide: 561.5131 - proto: NitrousOxideTankFilled entities: - uid: 11504 @@ -85921,7 +86537,8 @@ entities: - uid: 11513 components: - type: Transform - pos: -7.5,-23.5 + rot: 1.5707963267948966 rad + pos: -53.5,36.5 parent: 2 - proto: NuclearBombKeg entities: @@ -85985,6 +86602,16 @@ entities: parent: 2 - proto: OxygenCanister entities: + - uid: 2848 + components: + - type: Transform + pos: -6.5,-25.5 + parent: 2 + - uid: 5742 + components: + - type: Transform + pos: -10.5,-39.5 + parent: 2 - uid: 11519 components: - type: Transform @@ -86020,11 +86647,6 @@ entities: - type: Transform pos: -27.5,59.5 parent: 2 - - uid: 11526 - components: - - type: Transform - pos: -53.5,32.5 - parent: 2 - uid: 11527 components: - type: Transform @@ -86065,6 +86687,11 @@ entities: - type: Transform pos: 39.5,-42.5 parent: 2 + - uid: 16720 + components: + - type: Transform + pos: 17.5,-30.5 + parent: 2 - proto: PaladinCircuitBoard entities: - uid: 631 @@ -86076,6 +86703,18 @@ entities: - type: InsideEntityStorage - proto: Paper entities: + - uid: 4077 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.238791,-23.35649 + parent: 2 + - uid: 7423 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.457541,-23.54399 + parent: 2 - uid: 11535 components: - type: Transform @@ -86368,11 +87007,6 @@ entities: - type: Transform pos: 34.5,-4.5 parent: 2 - - uid: 11569 - components: - - type: Transform - pos: -14.5,-22.5 - parent: 2 - uid: 11570 components: - type: Transform @@ -86567,11 +87201,6 @@ entities: rot: 1.5707963267948966 rad pos: 28.331724,2.1021113 parent: 2 - - uid: 11601 - components: - - type: Transform - pos: -13.918377,-23.281319 - parent: 2 - uid: 11602 components: - type: Transform @@ -86611,6 +87240,12 @@ entities: parent: 2 - proto: PianoInstrument entities: + - uid: 8262 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-19.5 + parent: 2 - uid: 11608 components: - type: Transform @@ -86795,10 +87430,15 @@ entities: canCollide: False - proto: PlasmaCanister entities: - - uid: 11610 + - uid: 2826 components: - type: Transform - pos: -5.5,-30.5 + pos: -9.5,-25.5 + parent: 2 + - uid: 7844 + components: + - type: Transform + pos: -14.5,-36.5 parent: 2 - uid: 11611 components: @@ -86810,31 +87450,8 @@ entities: - type: Transform pos: -24.5,-12.5 parent: 2 - - uid: 11613 - components: - - type: Transform - pos: 1.5,-31.5 - parent: 2 - proto: PlasmaReinforcedWindowDirectional entities: - - uid: 11614 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -7.5,-24.5 - parent: 2 - - uid: 11615 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -8.5,-23.5 - parent: 2 - - uid: 11616 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -6.5,-23.5 - parent: 2 - uid: 11617 components: - type: Transform @@ -87050,17 +87667,24 @@ entities: - type: Transform pos: 36.5,-30.5 parent: 2 +- proto: PortableGeneratorJrPacman + entities: + - uid: 19387 + components: + - type: Transform + pos: -15.5,-25.5 + parent: 2 - proto: PortableScrubber entities: - - uid: 11652 + - uid: 12201 components: - type: Transform - pos: -13.5,-25.5 + pos: -14.5,-27.5 parent: 2 - - uid: 11653 + - uid: 12202 components: - type: Transform - pos: -12.5,-25.5 + pos: -14.5,-26.5 parent: 2 - proto: PosterContrabandCommunistState entities: @@ -87372,6 +87996,26 @@ entities: ent: 6383 - proto: PottedPlantRandom entities: + - uid: 3538 + components: + - type: Transform + pos: -9.5,-21.5 + parent: 2 + - uid: 5982 + components: + - type: Transform + pos: 21.5,-28.5 + parent: 2 + - uid: 7010 + components: + - type: Transform + pos: -18.5,-20.5 + parent: 2 + - uid: 10038 + components: + - type: Transform + pos: -15.5,-23.5 + parent: 2 - uid: 11690 components: - type: Transform @@ -87472,6 +88116,11 @@ entities: - type: Transform pos: -9.5,31.5 parent: 2 + - uid: 19367 + components: + - type: Transform + pos: 21.5,-32.5 + parent: 2 - proto: PottedPlantRandomPlastic entities: - uid: 11710 @@ -87565,6 +88214,11 @@ entities: parent: 2 - proto: PowerCellRecharger entities: + - uid: 6998 + components: + - type: Transform + pos: -17.5,-16.5 + parent: 2 - uid: 11727 components: - type: Transform @@ -87601,6 +88255,11 @@ entities: - type: Transform pos: 21.5,47.5 parent: 2 + - uid: 12480 + components: + - type: Transform + pos: -0.5,-33.5 + parent: 2 - proto: PowerCellSmallPrinted entities: - uid: 11734 @@ -87637,6 +88296,87 @@ entities: parent: 2 - proto: Poweredlight entities: + - uid: 440 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-23.5 + parent: 2 + - uid: 680 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -12.5,-35.5 + parent: 2 + - uid: 758 + components: + - type: Transform + pos: -12.5,-25.5 + parent: 2 + - uid: 969 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-32.5 + parent: 2 + - uid: 973 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-29.5 + parent: 2 + - uid: 2854 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-30.5 + parent: 2 + - uid: 8724 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-18.5 + parent: 2 + - uid: 9676 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-28.5 + parent: 2 + - uid: 9679 + components: + - type: Transform + pos: -4.5,-34.5 + parent: 2 + - uid: 9699 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -18.5,-20.5 + parent: 2 + - uid: 11353 + components: + - type: Transform + pos: -21.5,-34.5 + parent: 2 + - uid: 11354 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-36.5 + parent: 2 + - uid: 11355 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -21.5,-40.5 + parent: 2 + - uid: 11416 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-40.5 + parent: 2 - uid: 11740 components: - type: Transform @@ -87671,12 +88411,6 @@ entities: - type: Transform pos: -23.5,-19.5 parent: 2 - - uid: 11746 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -14.5,-19.5 - parent: 2 - uid: 11747 components: - type: Transform @@ -87787,23 +88521,6 @@ entities: - type: Transform pos: -25.5,-29.5 parent: 2 - - uid: 11766 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-38.5 - parent: 2 - - uid: 11767 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -18.5,-28.5 - parent: 2 - - uid: 11768 - components: - - type: Transform - pos: -11.5,-25.5 - parent: 2 - uid: 11769 components: - type: Transform @@ -87921,29 +88638,11 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,-19.5 parent: 2 - - uid: 11789 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-22.5 - parent: 2 - uid: 11790 components: - type: Transform rot: 3.141592653589793 rad - pos: -17.5,-15.5 - parent: 2 - - uid: 11791 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-31.5 - parent: 2 - - uid: 11792 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-31.5 + pos: -14.5,-19.5 parent: 2 - uid: 11793 components: @@ -88062,11 +88761,6 @@ entities: - type: Transform pos: -9.5,26.5 parent: 2 - - uid: 11813 - components: - - type: Transform - pos: -19.5,-40.5 - parent: 2 - uid: 11814 components: - type: Transform @@ -88154,11 +88848,6 @@ entities: rot: 3.141592653589793 rad pos: -40.5,28.5 parent: 2 - - uid: 11829 - components: - - type: Transform - pos: -52.5,35.5 - parent: 2 - uid: 11830 components: - type: Transform @@ -88586,6 +89275,17 @@ entities: rot: 3.141592653589793 rad pos: 24.5,-13.5 parent: 2 + - uid: 13600 + components: + - type: Transform + pos: -14.5,-21.5 + parent: 2 + - uid: 13654 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -53.5,34.5 + parent: 2 - uid: 18341 components: - type: Transform @@ -88748,6 +89448,12 @@ entities: rot: 3.141592653589793 rad pos: -0.5,-5.5 parent: 16911 + - uid: 19361 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 19.5,-30.5 + parent: 2 - proto: PoweredlightEmpty entities: - uid: 11904 @@ -88910,6 +89616,47 @@ entities: parent: 16911 - proto: PoweredSmallLight entities: + - uid: 760 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-40.5 + parent: 2 + - uid: 763 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-40.5 + parent: 2 + - uid: 2850 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-37.5 + parent: 2 + - uid: 2852 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-40.5 + parent: 2 + - uid: 2856 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-40.5 + parent: 2 + - uid: 7765 + components: + - type: Transform + pos: -51.5,32.5 + parent: 2 + - uid: 9701 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-18.5 + parent: 2 - uid: 11927 components: - type: Transform @@ -88922,29 +89669,11 @@ entities: rot: -1.5707963267948966 rad pos: 24.5,28.5 parent: 2 - - uid: 11929 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-31.5 - parent: 2 - - uid: 11930 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-31.5 - parent: 2 - uid: 11931 components: - type: Transform pos: -41.5,37.5 parent: 2 - - uid: 11932 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -5.5,-31.5 - parent: 2 - uid: 11933 components: - type: Transform @@ -89349,18 +90078,6 @@ entities: rot: 1.5707963267948966 rad pos: 23.5,18.5 parent: 2 - - uid: 12003 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-31.5 - parent: 2 - - uid: 12004 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-31.5 - parent: 2 - uid: 12005 components: - type: Transform @@ -89482,22 +90199,6 @@ entities: ent: 11286 - type: ApcPowerReceiver powerLoad: 0 - - uid: 12017 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-23.5 - parent: 2 - - type: DeviceLinkSink - invokeCounter: 1 - - uid: 12018 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -8.5,-16.5 - parent: 2 - - type: DeviceLinkSink - invokeCounter: 1 - proto: PresentRandom entities: - uid: 6022 @@ -90006,6 +90707,17 @@ entities: parent: 16911 - proto: Rack entities: + - uid: 650 + components: + - type: Transform + pos: -7.5,-21.5 + parent: 2 + - uid: 8393 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-17.5 + parent: 2 - uid: 12032 components: - type: Transform @@ -90017,21 +90729,6 @@ entities: - type: Transform pos: -21.5,6.5 parent: 2 - - uid: 12034 - components: - - type: Transform - pos: -14.5,-19.5 - parent: 2 - - uid: 12035 - components: - - type: Transform - pos: -10.5,-25.5 - parent: 2 - - uid: 12036 - components: - - type: Transform - pos: -11.5,-25.5 - parent: 2 - uid: 12037 components: - type: Transform @@ -90064,11 +90761,6 @@ entities: - type: Transform pos: -22.5,-7.5 parent: 2 - - uid: 12043 - components: - - type: Transform - pos: -14.5,-20.5 - parent: 2 - uid: 12044 components: - type: Transform @@ -90280,37 +90972,50 @@ entities: - type: Transform pos: -25.5,40.5 parent: 2 + - uid: 12204 + components: + - type: Transform + pos: -4.5,-25.5 + parent: 2 + - uid: 13287 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-17.5 + parent: 2 + - uid: 16529 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 15.5,-29.5 + parent: 2 + - uid: 16530 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 15.5,-31.5 + parent: 2 - proto: RadiationCollectorFullTank entities: - - uid: 12085 + - uid: 10616 components: - type: Transform - pos: -20.5,-45.5 + pos: -13.5,-41.5 parent: 2 - - uid: 12086 + - uid: 10640 components: - type: Transform - pos: -18.5,-44.5 + pos: -14.5,-41.5 parent: 2 - - uid: 12087 + - uid: 10693 components: - type: Transform - pos: -20.5,-44.5 + pos: -14.5,-39.5 parent: 2 - - uid: 12088 + - uid: 10694 components: - type: Transform - pos: -18.5,-45.5 - parent: 2 - - uid: 12089 - components: - - type: Transform - pos: -18.5,-43.5 - parent: 2 - - uid: 12090 - components: - - type: Transform - pos: -20.5,-43.5 + pos: -15.5,-39.5 parent: 2 - proto: RadioHandheld entities: @@ -90335,24 +91040,40 @@ entities: parent: 2 - proto: Railing entities: + - uid: 4327 + components: + - type: Transform + pos: -4.5,-16.5 + parent: 2 + - uid: 8392 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-19.5 + parent: 2 + - uid: 9472 + components: + - type: Transform + pos: -7.5,-16.5 + parent: 2 + - uid: 9505 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-18.5 + parent: 2 + - uid: 9507 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-17.5 + parent: 2 - uid: 12094 components: - type: Transform rot: -1.5707963267948966 rad pos: 24.5,26.5 parent: 2 - - uid: 12095 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-29.5 - parent: 2 - - uid: 12096 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -0.5,-29.5 - parent: 2 - uid: 12097 components: - type: Transform @@ -90694,42 +91415,6 @@ entities: rot: 3.141592653589793 rad pos: -35.5,-30.5 parent: 2 - - uid: 12155 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -9.5,-29.5 - parent: 2 - - uid: 12156 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -10.5,-29.5 - parent: 2 - - uid: 12157 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -13.5,-29.5 - parent: 2 - - uid: 12158 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-29.5 - parent: 2 - - uid: 12159 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -15.5,-29.5 - parent: 2 - - uid: 12160 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -12.5,-29.5 - parent: 2 - uid: 12161 components: - type: Transform @@ -90808,6 +91493,42 @@ entities: rot: 3.141592653589793 rad pos: 39.5,22.5 parent: 2 + - uid: 12567 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -9.5,-37.5 + parent: 2 + - uid: 12775 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-37.5 + parent: 2 + - uid: 13088 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,-37.5 + parent: 2 + - uid: 13408 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-37.5 + parent: 2 + - uid: 13409 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-37.5 + parent: 2 + - uid: 13410 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-37.5 + parent: 2 - uid: 18446 components: - type: Transform @@ -91595,17 +92316,35 @@ entities: parent: 16911 - proto: RailingCornerSmall entities: - - uid: 12188 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-28.5 - parent: 2 - - uid: 12189 + - uid: 437 components: - type: Transform rot: 1.5707963267948966 rad - pos: 0.5,-28.5 + pos: -2.5,-36.5 + parent: 2 + - uid: 2827 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-36.5 + parent: 2 + - uid: 4029 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-36.5 + parent: 2 + - uid: 4044 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-36.5 + parent: 2 + - uid: 9504 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-16.5 parent: 2 - uid: 12190 components: @@ -91669,42 +92408,6 @@ entities: rot: 1.5707963267948966 rad pos: 24.5,27.5 parent: 2 - - uid: 12201 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -16.5,-28.5 - parent: 2 - - uid: 12202 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -13.5,-28.5 - parent: 2 - - uid: 12203 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-28.5 - parent: 2 - - uid: 12204 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -9.5,-28.5 - parent: 2 - - uid: 12205 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -12.5,-28.5 - parent: 2 - - uid: 12206 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -15.5,-28.5 - parent: 2 - uid: 12207 components: - type: Transform @@ -91733,6 +92436,24 @@ entities: rot: -1.5707963267948966 rad pos: 37.5,20.5 parent: 2 + - uid: 12408 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-16.5 + parent: 2 + - uid: 13412 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,-36.5 + parent: 2 + - uid: 13413 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,-36.5 + parent: 2 - uid: 18570 components: - type: Transform @@ -91966,6 +92687,11 @@ entities: parent: 2 - proto: RandomPosterLegit entities: + - uid: 8266 + components: + - type: Transform + pos: -14.5,-20.5 + parent: 2 - uid: 12245 components: - type: Transform @@ -92457,12 +93183,6 @@ entities: - type: Transform pos: -18.5,3.5 parent: 2 - - uid: 12333 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -21.5,-39.5 - parent: 2 - uid: 12334 components: - type: Transform @@ -92474,12 +93194,6 @@ entities: rot: -1.5707963267948966 rad pos: -49.5,34.5 parent: 2 - - uid: 12336 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -54.5,34.5 - parent: 2 - uid: 12337 components: - type: Transform @@ -92566,6 +93280,21 @@ entities: rot: 3.141592653589793 rad pos: -27.5,7.5 parent: 2 + - uid: 19384 + components: + - type: Transform + pos: -4.5,-33.5 + parent: 2 + - uid: 19385 + components: + - type: Transform + pos: 5.5,-24.5 + parent: 2 + - uid: 19386 + components: + - type: Transform + pos: -6.5,-24.5 + parent: 2 - proto: RandomSpawner entities: - uid: 12352 @@ -92597,12 +93326,6 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-26.5 parent: 2 - - uid: 12357 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-20.5 - parent: 2 - uid: 12358 components: - type: Transform @@ -92615,18 +93338,6 @@ entities: rot: -1.5707963267948966 rad pos: 32.5,-18.5 parent: 2 - - uid: 12360 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -9.5,-23.5 - parent: 2 - - uid: 12361 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -5.5,-23.5 - parent: 2 - uid: 12362 components: - type: Transform @@ -93353,6 +94064,11 @@ entities: parent: 2 - proto: RandomVendingDrinks entities: + - uid: 8391 + components: + - type: Transform + pos: -18.5,-19.5 + parent: 2 - uid: 12366 components: - type: Transform @@ -93572,20 +94288,20 @@ entities: parent: 16911 - proto: RCDAmmo entities: - - uid: 12408 + - uid: 4949 components: - type: Transform - pos: -14.603725,-19.388529 + pos: -7.66646,-21.566923 parent: 2 - - uid: 12409 + - uid: 6537 components: - type: Transform - pos: -14.436941,-20.340593 + pos: -7.494585,-21.363798 parent: 2 - - uid: 12410 + - uid: 7290 components: - type: Transform - pos: -14.593191,-20.356218 + pos: -7.307085,-21.566923 parent: 2 - proto: Recycler entities: @@ -93659,6 +94375,138 @@ entities: parent: 19351 - proto: ReinforcedPlasmaWindow entities: + - uid: 2772 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-41.5 + parent: 2 + - uid: 2815 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-31.5 + parent: 2 + - uid: 2816 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-31.5 + parent: 2 + - uid: 2819 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-38.5 + parent: 2 + - uid: 2821 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -8.5,-41.5 + parent: 2 + - uid: 2829 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-31.5 + parent: 2 + - uid: 2830 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-32.5 + parent: 2 + - uid: 7848 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-38.5 + parent: 2 + - uid: 7849 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-37.5 + parent: 2 + - uid: 7859 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,-41.5 + parent: 2 + - uid: 7875 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-41.5 + parent: 2 + - uid: 7877 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-41.5 + parent: 2 + - uid: 8067 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-38.5 + parent: 2 + - uid: 9398 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -14.5,-35.5 + parent: 2 + - uid: 9424 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,-33.5 + parent: 2 + - uid: 9664 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-33.5 + parent: 2 + - uid: 9665 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -11.5,-41.5 + parent: 2 + - uid: 9666 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,-33.5 + parent: 2 + - uid: 9993 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-41.5 + parent: 2 + - uid: 10002 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-41.5 + parent: 2 + - uid: 10006 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-38.5 + parent: 2 + - uid: 10048 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-36.5 + parent: 2 - uid: 12413 components: - type: Transform @@ -93683,120 +94531,63 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-15.5 parent: 2 - - uid: 12417 - components: - - type: Transform - pos: -5.5,-24.5 - parent: 2 - - uid: 12418 - components: - - type: Transform - pos: -9.5,-24.5 - parent: 2 - - uid: 12419 - components: - - type: Transform - pos: -5.5,-20.5 - parent: 2 - - uid: 12420 - components: - - type: Transform - pos: -9.5,-20.5 - parent: 2 - - uid: 12421 - components: - - type: Transform - pos: -4.5,-20.5 - parent: 2 - - uid: 12422 - components: - - type: Transform - pos: -10.5,-20.5 - parent: 2 - - uid: 12423 - components: - - type: Transform - pos: -7.5,-24.5 - parent: 2 - - uid: 12473 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-33.5 - parent: 2 - - uid: 12474 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-33.5 - parent: 2 - - uid: 12475 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -14.5,-30.5 - parent: 2 - - uid: 12476 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -12.5,-33.5 - parent: 2 - - uid: 12477 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-33.5 - parent: 2 - - uid: 12478 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-30.5 - parent: 2 - - uid: 12480 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -9.5,-33.5 - parent: 2 - - uid: 12481 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-33.5 - parent: 2 - - uid: 12571 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-30.5 - parent: 2 - - uid: 12572 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -17.5,-30.5 - parent: 2 - - uid: 12755 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-30.5 - parent: 2 - - uid: 12775 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-30.5 - parent: 2 -- proto: ReinforcedWindow +- proto: ReinforcedUraniumWindow entities: - - uid: 12424 + - uid: 10013 components: - type: Transform rot: 3.141592653589793 rad - pos: -11.5,-19.5 + pos: -9.5,-24.5 + parent: 2 + - uid: 10014 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.5,-24.5 + parent: 2 + - uid: 10039 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -7.5,-24.5 + parent: 2 +- proto: ReinforcedWindow + entities: + - uid: 7493 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-29.5 + parent: 2 + - uid: 7521 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-30.5 + parent: 2 + - uid: 9663 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-36.5 + parent: 2 + - uid: 10118 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 18.5,-33.5 + parent: 2 + - uid: 10119 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-31.5 + parent: 2 + - uid: 10120 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 16.5,-27.5 parent: 2 - uid: 12425 components: @@ -93898,26 +94689,6 @@ entities: - type: Transform pos: -6.5,2.5 parent: 2 - - uid: 12445 - components: - - type: Transform - pos: -10.5,-17.5 - parent: 2 - - uid: 12446 - components: - - type: Transform - pos: -9.5,-17.5 - parent: 2 - - uid: 12447 - components: - - type: Transform - pos: -5.5,-17.5 - parent: 2 - - uid: 12448 - components: - - type: Transform - pos: -4.5,-17.5 - parent: 2 - uid: 12449 components: - type: Transform @@ -94286,16 +95057,6 @@ entities: rot: -1.5707963267948966 rad pos: 8.5,-17.5 parent: 2 - - uid: 12526 - components: - - type: Transform - pos: 21.5,-33.5 - parent: 2 - - uid: 12527 - components: - - type: Transform - pos: 21.5,-27.5 - parent: 2 - uid: 12528 components: - type: Transform @@ -94304,12 +95065,14 @@ entities: - uid: 12529 components: - type: Transform - pos: 21.5,-32.5 + rot: 1.5707963267948966 rad + pos: 18.5,-27.5 parent: 2 - uid: 12530 components: - type: Transform - pos: 21.5,-31.5 + rot: 1.5707963267948966 rad + pos: 16.5,-33.5 parent: 2 - uid: 12531 components: @@ -94376,26 +95139,11 @@ entities: - type: Transform pos: -4.5,-5.5 parent: 2 - - uid: 12544 - components: - - type: Transform - pos: -54.5,35.5 - parent: 2 - - uid: 12545 - components: - - type: Transform - pos: -19.5,-38.5 - parent: 2 - uid: 12546 components: - type: Transform pos: -33.5,-19.5 parent: 2 - - uid: 12547 - components: - - type: Transform - pos: -19.5,-36.5 - parent: 2 - uid: 12548 components: - type: Transform @@ -94416,16 +95164,6 @@ entities: - type: Transform pos: -37.5,-25.5 parent: 2 - - uid: 12552 - components: - - type: Transform - pos: -19.5,-34.5 - parent: 2 - - uid: 12553 - components: - - type: Transform - pos: -19.5,-35.5 - parent: 2 - uid: 12554 components: - type: Transform @@ -94494,11 +95232,6 @@ entities: - type: Transform pos: -39.5,-28.5 parent: 2 - - uid: 12567 - components: - - type: Transform - pos: -19.5,-37.5 - parent: 2 - uid: 12568 components: - type: Transform @@ -94946,11 +95679,6 @@ entities: - type: Transform pos: -30.5,19.5 parent: 2 - - uid: 12657 - components: - - type: Transform - pos: -11.5,-18.5 - parent: 2 - uid: 12658 components: - type: Transform @@ -95321,26 +96049,6 @@ entities: - type: Transform pos: 19.5,34.5 parent: 2 - - uid: 12732 - components: - - type: Transform - pos: -54.5,33.5 - parent: 2 - - uid: 12733 - components: - - type: Transform - pos: -53.5,36.5 - parent: 2 - - uid: 12734 - components: - - type: Transform - pos: -51.5,36.5 - parent: 2 - - uid: 12735 - components: - - type: Transform - pos: -51.5,39.5 - parent: 2 - uid: 12736 components: - type: Transform @@ -95349,7 +96057,7 @@ entities: - uid: 12737 components: - type: Transform - pos: -57.5,33.5 + pos: -57.5,36.5 parent: 2 - uid: 12738 components: @@ -95768,6 +96476,29 @@ entities: - type: Transform pos: -13.5,69.5 parent: 2 + - uid: 13624 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -51.5,35.5 + parent: 2 + - uid: 15320 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -55.5,35.5 + parent: 2 + - uid: 15328 + components: + - type: Transform + pos: -54.5,39.5 + parent: 2 + - uid: 15605 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -53.5,37.5 + parent: 2 - proto: RemoteSignaller entities: - uid: 12822 @@ -97138,6 +97869,16 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage + - uid: 12205 + components: + - type: Transform + pos: -4.5,-25.5 + parent: 2 + - uid: 12206 + components: + - type: Transform + pos: -4.5,-25.5 + parent: 2 - uid: 12869 components: - type: Transform @@ -97153,16 +97894,6 @@ entities: - type: Transform pos: 3.3614388,14.621397 parent: 2 - - uid: 12872 - components: - - type: Transform - pos: -10.5,-25.5 - parent: 2 - - uid: 12873 - components: - - type: Transform - pos: -11.5,-25.5 - parent: 2 - uid: 12874 components: - type: Transform @@ -97214,6 +97945,16 @@ entities: - type: Transform pos: -24.602211,64.518074 parent: 2 +- proto: ShelfBar + entities: + - uid: 14488 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -19.5,-32.5 + parent: 2 + - type: Fixtures + fixtures: {} - proto: ShelfRGlass entities: - uid: 1201 @@ -97578,6 +98319,21 @@ entities: parent: 2 - proto: ShuttersNormalOpen entities: + - uid: 3446 + components: + - type: Transform + pos: -5.5,-24.5 + parent: 2 + - uid: 3557 + components: + - type: Transform + pos: -7.5,-24.5 + parent: 2 + - uid: 3572 + components: + - type: Transform + pos: -9.5,-24.5 + parent: 2 - uid: 12904 components: - type: Transform @@ -97714,20 +98470,20 @@ entities: fixtures: {} - proto: SignalButton entities: - - uid: 12927 + - uid: 548 components: - - type: MetaData - desc: Эта кнопка переключает затворы камеры сгорания, ведущие в космос. - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-29.5 + pos: -5.5,-20.5 parent: 2 - type: DeviceLinkSource linkedPorts: - 973: + 3446: - - Pressed - Toggle - 966: + 3557: + - - Pressed + - Toggle + 3572: - - Pressed - Toggle - type: Fixtures @@ -98018,15 +98774,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} -- proto: SignCans - entities: - - uid: 12947 - components: - - type: Transform - pos: -4.5,-30.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: SignCargoDock entities: - uid: 12948 @@ -98526,12 +99273,31 @@ entities: parent: 2 - type: Fixtures fixtures: {} -- proto: SignEVA +- proto: SignEngineering entities: - - uid: 13006 + - uid: 8207 components: - type: Transform - pos: -51.5,31.5 + rot: 3.141592653589793 rad + pos: -11.5,-18.5 + parent: 2 + - type: Fixtures + fixtures: {} + - uid: 8258 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-16.5 + parent: 2 + - type: Fixtures + fixtures: {} +- proto: SignEVA + entities: + - uid: 19363 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 20.5,-30.5 parent: 2 - type: Fixtures fixtures: {} @@ -98555,6 +99321,14 @@ entities: fixtures: {} - proto: SignHead entities: + - uid: 2716 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-21.5 + parent: 2 + - type: Fixtures + fixtures: {} - uid: 13009 components: - type: Transform @@ -98712,6 +99486,16 @@ entities: parent: 2 - type: Fixtures fixtures: {} +- proto: SignRadiationMed + entities: + - uid: 10001 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-39.5 + parent: 2 + - type: Fixtures + fixtures: {} - proto: SignRedFive entities: - uid: 13027 @@ -98969,6 +99753,13 @@ entities: fixtures: {} - proto: SignVault entities: + - uid: 194 + components: + - type: Transform + pos: -51.5,31.5 + parent: 2 + - type: Fixtures + fixtures: {} - uid: 13056 components: - type: Transform @@ -98977,14 +99768,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} - - uid: 13057 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-17.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: SignXenobio entities: - uid: 13058 @@ -99082,23 +99865,19 @@ entities: - type: Transform pos: -33.5,9.5 parent: 2 + - type: Construction + containers: + - machine_parts + - machine_board + - smart_fridge_inventory + - entity_storage - type: EntityStorage air: volume: 200 temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 closeSound: !type:SoundPathSpecifier path: /Audio/Effects/closetclose.ogg openSound: !type:SoundPathSpecifier @@ -99112,6 +99891,14 @@ entities: - 7822 - 7820 - 7821 + machine_board: !type:Container + showEnts: False + occludes: True + ents: [] + machine_parts: !type:Container + showEnts: False + occludes: True + ents: [] - uid: 13071 components: - type: MetaData @@ -99151,63 +99938,68 @@ entities: - 16970 - 16971 - 16966 -- proto: SMESBasic + machine_board: !type:Container + showEnts: False + occludes: True + ents: [] + machine_parts: !type:Container + showEnts: False + occludes: True + ents: [] +- proto: SMESAdvanced entities: - - uid: 13073 - components: - - type: Transform - pos: -33.5,-20.5 - parent: 2 - uid: 13074 components: - type: Transform - pos: -25.5,-29.5 + pos: -25.5,-31.5 parent: 2 - uid: 13075 components: - type: Transform - pos: -25.5,-31.5 + pos: -25.5,-29.5 parent: 2 - uid: 13076 components: - type: Transform pos: -25.5,-30.5 parent: 2 - - uid: 13077 +- proto: SMESBasic + entities: + - uid: 598 components: - type: Transform - pos: -18.5,-40.5 + pos: -20.5,29.5 parent: 2 - - uid: 13078 + - uid: 4146 components: - type: Transform - pos: -26.5,-6.5 + pos: 31.5,-15.5 + parent: 2 + - uid: 4424 + components: + - type: Transform + pos: 26.5,18.5 + parent: 2 + - uid: 13073 + components: + - type: Transform + pos: -33.5,-20.5 parent: 2 - uid: 13079 components: - type: Transform pos: -7.5,48.5 parent: 2 - - uid: 13080 - components: - - type: Transform - pos: -20.5,28.5 - parent: 2 - uid: 13081 components: - type: Transform - pos: 27.5,18.5 + pos: -50.5,44.5 parent: 2 - uid: 13082 components: - type: Transform pos: 20.5,-23.5 parent: 2 - - uid: 13083 - components: - - type: Transform - pos: 32.5,-15.5 - parent: 2 - uid: 13084 components: - type: Transform @@ -99223,21 +100015,16 @@ entities: - type: Transform pos: 23.5,-41.5 parent: 2 - - uid: 13087 - components: - - type: Transform - pos: -51.5,44.5 - parent: 2 - - uid: 13088 - components: - - type: Transform - pos: -19.5,-40.5 - parent: 2 - uid: 18873 components: - type: Transform pos: 23.5,-9.5 parent: 16911 + - uid: 19526 + components: + - type: Transform + pos: -25.5,-7.5 + parent: 2 - proto: SMESBasicEmpty entities: - uid: 18874 @@ -100323,6 +101110,23 @@ entities: - type: Transform pos: 58.5,-10.5 parent: 2 +- proto: SpawnMobButterfly + entities: + - uid: 2695 + components: + - type: Transform + pos: -8.5,-15.5 + parent: 2 + - uid: 7008 + components: + - type: Transform + pos: -8.5,-19.5 + parent: 2 + - uid: 8220 + components: + - type: Transform + pos: -5.5,-19.5 + parent: 2 - proto: SpawnMobCat entities: - uid: 13283 @@ -100353,16 +101157,6 @@ entities: parent: 2 - proto: SpawnMobLizard entities: - - uid: 13287 - components: - - type: Transform - pos: -9.5,-22.5 - parent: 2 - - uid: 13288 - components: - - type: Transform - pos: -5.5,-22.5 - parent: 2 - uid: 13289 components: - type: Transform @@ -100561,15 +101355,20 @@ entities: parent: 2 - proto: SpawnPointChiefEngineer entities: - - uid: 13322 + - uid: 2694 components: - type: Transform - pos: -13.5,-22.5 + pos: -5.5,-22.5 parent: 2 - - uid: 13323 + - uid: 7604 components: - type: Transform - pos: -13.5,-17.5 + pos: -9.5,-22.5 + parent: 2 + - uid: 11768 + components: + - type: Transform + pos: -26.5,-30.5 parent: 2 - proto: SpawnPointChiefMedicalOfficer entities: @@ -101006,10 +101805,20 @@ entities: parent: 2 - proto: SpawnPointSeniorEngineer entities: - - uid: 13401 + - uid: 1284 components: - type: Transform - pos: -17.5,-21.5 + pos: -14.5,-23.5 + parent: 2 + - uid: 11766 + components: + - type: Transform + pos: -25.5,-39.5 + parent: 2 + - uid: 11767 + components: + - type: Transform + pos: -22.5,-35.5 parent: 2 - proto: SpawnPointSeniorOfficer entities: @@ -101051,36 +101860,36 @@ entities: parent: 2 - proto: SpawnPointStationEngineer entities: - - uid: 13408 + - uid: 7007 components: - type: Transform - pos: -21.5,-35.5 + pos: -13.5,-18.5 parent: 2 - - uid: 13409 + - uid: 7606 + components: + - type: Transform + pos: -17.5,-20.5 + parent: 2 + - uid: 10067 + components: + - type: Transform + pos: -19.5,-36.5 + parent: 2 + - uid: 10083 + components: + - type: Transform + pos: -18.5,-36.5 + parent: 2 + - uid: 11652 + components: + - type: Transform + pos: -23.5,-37.5 + parent: 2 + - uid: 11653 components: - type: Transform pos: -21.5,-36.5 parent: 2 - - uid: 13410 - components: - - type: Transform - pos: -21.5,-37.5 - parent: 2 - - uid: 13411 - components: - - type: Transform - pos: -25.5,-36.5 - parent: 2 - - uid: 13412 - components: - - type: Transform - pos: -25.5,-38.5 - parent: 2 - - uid: 13413 - components: - - type: Transform - pos: -25.5,-37.5 - parent: 2 - proto: SpawnPointSurgeon entities: - uid: 13414 @@ -101095,10 +101904,25 @@ entities: parent: 2 - proto: SpawnPointTechnicalAssistant entities: - - uid: 13416 + - uid: 15 components: - type: Transform - pos: -17.5,-19.5 + pos: -16.5,-22.5 + parent: 2 + - uid: 2696 + components: + - type: Transform + pos: -16.5,-17.5 + parent: 2 + - uid: 10082 + components: + - type: Transform + pos: -17.5,-36.5 + parent: 2 + - uid: 11613 + components: + - type: Transform + pos: -21.5,-38.5 parent: 2 - uid: 13417 components: @@ -101115,11 +101939,6 @@ entities: - type: Transform pos: -25.5,-25.5 parent: 2 - - uid: 13420 - components: - - type: Transform - pos: -21.5,-38.5 - parent: 2 - proto: SpawnPointWarden entities: - uid: 13421 @@ -101811,6 +102630,12 @@ entities: parent: 2 - proto: Stool entities: + - uid: 8263 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.62008,-19.378508 + parent: 2 - uid: 13500 components: - type: Transform @@ -101943,27 +102768,29 @@ entities: rot: 3.141592653589793 rad pos: -44.5,43.5 parent: 2 + - uid: 14453 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-33.5 + parent: 2 + - uid: 14470 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -17.5,-32.5 + parent: 2 - proto: StorageCanister entities: - - uid: 13522 + - uid: 970 components: - type: Transform - pos: -5.5,-25.5 + pos: -7.5,-26.5 parent: 2 - - uid: 13523 + - uid: 9680 components: - type: Transform - pos: -4.5,-25.5 - parent: 2 - - uid: 13524 - components: - - type: Transform - pos: -6.5,-25.5 - parent: 2 - - uid: 13525 - components: - - type: Transform - pos: -6.5,-32.5 + pos: -7.5,-25.5 parent: 2 - uid: 13526 components: @@ -101975,11 +102802,6 @@ entities: - type: Transform pos: 28.5,-43.5 parent: 2 - - uid: 13528 - components: - - type: Transform - pos: -17.5,-31.5 - parent: 2 - uid: 13529 components: - type: Transform @@ -102000,6 +102822,11 @@ entities: - type: Transform pos: 34.5,19.5 parent: 2 + - uid: 14312 + components: + - type: Transform + pos: -17.5,-29.5 + parent: 2 - proto: StrangePill entities: - uid: 13533 @@ -102057,6 +102884,20 @@ entities: parent: 2 - proto: SubstationBasic entities: + - uid: 13078 + components: + - type: MetaData + name: подстанция блока снабжения + - type: Transform + pos: 27.5,18.5 + parent: 2 + - uid: 13083 + components: + - type: MetaData + name: подстанция медицинского блока + - type: Transform + pos: -20.5,28.5 + parent: 2 - uid: 13540 components: - type: MetaData @@ -102067,20 +102908,15 @@ entities: - uid: 13541 components: - type: MetaData - name: подстанция медицинского блока + name: северо-западная подстанция - type: Transform - pos: -20.5,29.5 + pos: -51.5,44.5 parent: 2 - uid: 13542 components: - type: Transform pos: -33.5,-24.5 parent: 2 - - uid: 13543 - components: - - type: Transform - pos: -4.5,-23.5 - parent: 2 - uid: 13544 components: - type: MetaData @@ -102095,13 +102931,6 @@ entities: - type: Transform pos: 20.5,-24.5 parent: 2 - - uid: 13546 - components: - - type: MetaData - name: подстанция охранного блока - - type: Transform - pos: 31.5,-15.5 - parent: 2 - uid: 13547 components: - type: MetaData @@ -102119,27 +102948,6 @@ entities: - type: Transform pos: 24.5,-41.5 parent: 2 - - uid: 13550 - components: - - type: MetaData - name: северо-западная подстанция - - type: Transform - pos: -50.5,44.5 - parent: 2 - - uid: 13551 - components: - - type: MetaData - name: подстанция научного блока - - type: Transform - pos: -25.5,-7.5 - parent: 2 - - uid: 13552 - components: - - type: MetaData - name: подстанция блока снабжения - - type: Transform - pos: 26.5,18.5 - parent: 2 - uid: 18915 components: - type: Transform @@ -102150,6 +102958,20 @@ entities: - type: Transform pos: 15.5,-9.5 parent: 16911 + - uid: 19523 + components: + - type: MetaData + name: подстанция охранного блока + - type: Transform + pos: 32.5,-15.5 + parent: 2 + - uid: 19527 + components: + - type: MetaData + name: подстанция научного блока + - type: Transform + pos: -26.5,-7.5 + parent: 2 - proto: SubstationBasicEmpty entities: - uid: 18917 @@ -102207,10 +103029,10 @@ entities: parent: 2 - proto: SuitStorageCE entities: - - uid: 13559 + - uid: 3453 components: - type: Transform - pos: -14.5,-18.5 + pos: -4.5,-21.5 parent: 2 - proto: SuitStorageCMO entities: @@ -102219,44 +103041,42 @@ entities: - type: Transform pos: -25.5,10.5 parent: 2 -- proto: SuitStorageEngi - entities: - - uid: 13561 - components: - - type: Transform - pos: -23.5,-37.5 - parent: 2 - - uid: 13562 - components: - - type: Transform - pos: -23.5,-35.5 - parent: 2 - - uid: 13563 - components: - - type: Transform - pos: -23.5,-36.5 - parent: 2 - proto: SuitStorageEVA entities: - - uid: 13564 + - uid: 12526 components: - type: Transform - pos: -51.5,35.5 + pos: 19.5,-30.5 parent: 2 - - uid: 13565 + - uid: 16527 components: - type: Transform - pos: -50.5,35.5 + pos: 19.5,-28.5 parent: 2 - - uid: 13566 + - uid: 16528 components: - type: Transform - pos: -52.5,35.5 + pos: 17.5,-28.5 parent: 2 - - uid: 13567 + - uid: 16531 components: - type: Transform - pos: -53.5,35.5 + pos: 18.5,-28.5 + parent: 2 + - uid: 16532 + components: + - type: Transform + pos: 19.5,-32.5 + parent: 2 + - uid: 16664 + components: + - type: Transform + pos: 18.5,-32.5 + parent: 2 + - uid: 16666 + components: + - type: Transform + pos: 17.5,-32.5 parent: 2 - uid: 18919 components: @@ -102346,6 +103166,11 @@ entities: - type: InsideEntityStorage - proto: SurveillanceCameraCommand entities: + - uid: 12417 + components: + - type: Transform + pos: 15.5,-32.5 + parent: 2 - uid: 13579 components: - type: Transform @@ -102362,25 +103187,6 @@ entities: - SurveillanceCameraCommand nameSet: True id: Специальное - Ядро ИИ - - uid: 13581 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -4.5,-22.5 - parent: 2 - - type: SurveillanceCamera - id: Хранилище - - uid: 13582 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -50.5,35.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraCommand - nameSet: True - id: Хранилище скафандров - uid: 13583 components: - type: Transform @@ -102413,6 +103219,12 @@ entities: - SurveillanceCameraCommand nameSet: True id: Мостик - Вход + - uid: 19369 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -52.5,36.5 + parent: 2 - proto: SurveillanceCameraConstructed entities: - uid: 13586 @@ -102429,6 +103241,35 @@ entities: id: Комната игр - proto: SurveillanceCameraEngineering entities: + - uid: 187 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-33.5 + parent: 2 + - uid: 8094 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-31.5 + parent: 2 + - uid: 9712 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-17.5 + parent: 2 + - uid: 11746 + components: + - type: Transform + pos: -14.5,-23.5 + parent: 2 + - uid: 12475 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -22.5,-34.5 + parent: 2 - uid: 13587 components: - type: Transform @@ -102448,17 +103289,6 @@ entities: - SurveillanceCameraEngineering nameSet: True id: Генератор Гравитации - - uid: 13589 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -16.5,-20.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Общие - Вход - uid: 13590 components: - type: Transform @@ -102469,50 +103299,6 @@ entities: - SurveillanceCameraEngineering nameSet: True id: Специальное - Двигатель антиматерии - - uid: 13591 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-33.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Общие - Основное помещение - - uid: 13592 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -10.5,-25.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Атмос - Основное помещение - - uid: 13593 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 1.5,-25.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Атмос - ТЭГ - - uid: 13594 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -22.5,-27.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraEngineering - nameSet: True - id: Атмос - Гардероб - uid: 13595 components: - type: Transform @@ -102537,6 +103323,12 @@ entities: id: Специальное - Телекоммуникации - proto: SurveillanceCameraGeneral entities: + - uid: 11616 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-13.5 + parent: 2 - uid: 13597 components: - type: Transform @@ -102570,17 +103362,6 @@ entities: - SurveillanceCameraGeneral nameSet: True id: Общие - Столовая, Ю/З - - uid: 13600 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -4.5,-14.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraGeneral - nameSet: True - id: Общие - Коридор - uid: 13601 components: - type: Transform @@ -102819,27 +103600,6 @@ entities: - SurveillanceCameraGeneral nameSet: True id: Общие - Отбытие/Прибытие - - uid: 13623 - components: - - type: Transform - pos: -48.5,28.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraGeneral - nameSet: True - id: Общие - Коридор прибытия - - uid: 13624 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -45.5,34.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraGeneral - nameSet: True - id: Общие - Склад инструментов - uid: 13625 components: - type: Transform @@ -103087,34 +103847,6 @@ entities: parent: 2 - proto: SurveillanceCameraScience entities: - - uid: 13653 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -22.5,-12.5 - parent: 2 - - type: SurveillanceCamera - id: Ксеноархеология - - uid: 13654 - components: - - type: Transform - pos: -22.5,-10.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraScience - nameSet: True - id: Робототехника - - uid: 13655 - components: - - type: Transform - pos: -14.5,-11.5 - parent: 2 - - type: SurveillanceCamera - setupAvailableNetworks: - - SurveillanceCameraScience - nameSet: True - id: Стойка - uid: 13656 components: - type: Transform @@ -103612,6 +104344,66 @@ entities: parent: 2 - proto: Table entities: + - uid: 3445 + components: + - type: Transform + pos: -10.5,-23.5 + parent: 2 + - uid: 3653 + components: + - type: Transform + pos: -8.5,-23.5 + parent: 2 + - uid: 3654 + components: + - type: Transform + pos: -9.5,-23.5 + parent: 2 + - uid: 7727 + components: + - type: Transform + pos: -53.5,34.5 + parent: 2 + - uid: 9660 + components: + - type: Transform + pos: 0.5,-34.5 + parent: 2 + - uid: 10974 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -22.5,-36.5 + parent: 2 + - uid: 10975 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -22.5,-37.5 + parent: 2 + - uid: 10976 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -22.5,-38.5 + parent: 2 + - uid: 12476 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-33.5 + parent: 2 + - uid: 12477 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-34.5 + parent: 2 + - uid: 13582 + components: + - type: Transform + pos: -54.5,34.5 + parent: 2 - uid: 13702 components: - type: Transform @@ -103675,56 +104467,6 @@ entities: - type: Transform pos: 49.5,-6.5 parent: 2 - - uid: 13714 - components: - - type: Transform - pos: -10.5,-21.5 - parent: 2 - - uid: 13715 - components: - - type: Transform - pos: -10.5,-22.5 - parent: 2 - - uid: 13716 - components: - - type: Transform - pos: -9.5,-21.5 - parent: 2 - - uid: 13717 - components: - - type: Transform - pos: -10.5,-23.5 - parent: 2 - - uid: 13718 - components: - - type: Transform - pos: -5.5,-21.5 - parent: 2 - - uid: 13719 - components: - - type: Transform - pos: -4.5,-21.5 - parent: 2 - - uid: 13720 - components: - - type: Transform - pos: -14.5,-23.5 - parent: 2 - - uid: 13721 - components: - - type: Transform - pos: -13.5,-23.5 - parent: 2 - - uid: 13722 - components: - - type: Transform - pos: -12.5,-23.5 - parent: 2 - - uid: 13723 - components: - - type: Transform - pos: -14.5,-22.5 - parent: 2 - uid: 13724 components: - type: Transform @@ -103889,11 +104631,6 @@ entities: - type: Transform pos: -70.5,36.5 parent: 2 - - uid: 13755 - components: - - type: Transform - pos: -12.5,-19.5 - parent: 2 - uid: 13756 components: - type: Transform @@ -104625,6 +105362,18 @@ entities: - type: Transform pos: 5.5,36.5 parent: 2 + - uid: 14317 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -18.5,-33.5 + parent: 2 + - uid: 14469 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -18.5,-32.5 + parent: 2 - proto: TableCounterMetal entities: - uid: 13891 @@ -104755,6 +105504,42 @@ entities: parent: 2 - proto: TableReinforced entities: + - uid: 678 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-26.5 + parent: 2 + - uid: 682 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-26.5 + parent: 2 + - uid: 7876 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-27.5 + parent: 2 + - uid: 9124 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -16.5,-16.5 + parent: 2 + - uid: 10005 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -13.5,-27.5 + parent: 2 + - uid: 13288 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -17.5,-16.5 + parent: 2 - uid: 13912 components: - type: Transform @@ -105272,6 +106057,16 @@ entities: parent: 16911 - proto: TableWood entities: + - uid: 8265 + components: + - type: Transform + pos: -13.5,-21.5 + parent: 2 + - uid: 12418 + components: + - type: Transform + pos: -12.5,-21.5 + parent: 2 - uid: 14002 components: - type: Transform @@ -105390,29 +106185,26 @@ entities: parent: 2 - proto: TegCenter entities: - - uid: 14018 + - uid: 10613 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-25.5 + pos: 3.5,-28.5 parent: 2 - - type: ApcPowerReceiver - powerDisabled: True - proto: TegCirculator entities: - - uid: 14019 + - uid: 10238 components: - type: Transform rot: -1.5707963267948966 rad - pos: 0.5,-25.5 + pos: 3.5,-29.5 parent: 2 - type: PointLight color: '#FF3300FF' - - uid: 14020 + - uid: 10330 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-25.5 + rot: 1.5707963267948966 rad + pos: 3.5,-27.5 parent: 2 - type: PointLight color: '#FF3300FF' @@ -105711,6 +106503,11 @@ entities: parent: 2 - proto: ToolboxElectricalFilled entities: + - uid: 9506 + components: + - type: Transform + pos: -14.519136,-17.522202 + parent: 2 - uid: 11398 components: - type: Transform @@ -105753,6 +106550,11 @@ entities: - type: Transform pos: -31.45074,64.72638 parent: 2 + - uid: 16667 + components: + - type: Transform + pos: 15.497587,-31.575962 + parent: 2 - uid: 18961 components: - type: Transform @@ -105775,15 +106577,13 @@ entities: - type: Transform pos: 22.538084,4.227873 parent: 16911 -- proto: ToolboxGoldFilled - entities: - - uid: 14054 - components: - - type: Transform - pos: -9.521576,-21.399467 - parent: 2 - proto: ToolboxMechanicalFilled entities: + - uid: 10037 + components: + - type: Transform + pos: -14.503511,-17.225327 + parent: 2 - uid: 11399 components: - type: Transform @@ -105836,6 +106636,11 @@ entities: - type: Transform pos: -24.555336,64.236824 parent: 2 + - uid: 16718 + components: + - type: Transform + pos: 15.497587,-31.279087 + parent: 2 - uid: 18964 components: - type: Transform @@ -106001,87 +106806,6 @@ entities: parent: 2 - proto: TwoWayLever entities: - - uid: 14086 - components: - - type: MetaData - desc: Двухпозиционный рычаг. Правая позиция открывает вход в цистерну, левая - опустошает её. Не забудьте отключить насосы перед входом. - - type: Transform - pos: -14.5,-29.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 979: - - - Left - - Open - - - Middle - - Close - - - Right - - Close - 978: - - - Left - - Open - - - Right - - Close - - - Middle - - Close - 980: - - - Left - - Close - - - Right - - Open - - - Middle - - Close - - uid: 14087 - components: - - type: MetaData - desc: Двухпозиционный рычаг. Правая позиция открывает вход в цистерну, левая - опустошает её. Не забудьте отключить насосы перед входом. - - type: Transform - pos: -17.5,-29.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 969: - - - Left - - Open - - - Right - - Close - - - Middle - - Close - 970: - - - Left - - Open - - - Right - - Close - - - Middle - - Close - 981: - - - Left - - Close - - - Right - - Open - - - Middle - - Close - - uid: 14088 - components: - - type: Transform - pos: 1.5,-29.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 988: - - - Left - - Open - - - Right - - Close - - - Middle - - Close - 1003: - - - Right - - Close - - - Left - - Open - - - Middle - - Open - uid: 14089 components: - type: Transform @@ -106282,13 +107006,6 @@ entities: - Reverse - - Middle - Off - 16516: - - - Left - - Open - - - Right - - Open - - - Middle - - Close 12411: - - Left - Forward @@ -106656,13 +107373,18 @@ entities: parent: 2 - proto: VendingMachineEngiDrobe entities: - - uid: 14130 + - uid: 10984 components: - type: Transform - pos: -22.5,-38.5 + pos: -21.5,-34.5 parent: 2 - proto: VendingMachineEngivend entities: + - uid: 11255 + components: + - type: Transform + pos: -13.5,-19.5 + parent: 2 - uid: 14131 components: - type: Transform @@ -106826,6 +107548,11 @@ entities: parent: 2 - proto: VendingMachineTankDispenserEngineering entities: + - uid: 11352 + components: + - type: Transform + pos: -19.5,-38.5 + parent: 2 - uid: 14156 components: - type: Transform @@ -106833,11 +107560,6 @@ entities: parent: 2 - proto: VendingMachineTankDispenserEVA entities: - - uid: 14157 - components: - - type: Transform - pos: -23.5,-38.5 - parent: 2 - uid: 14158 components: - type: Transform @@ -106853,10 +107575,10 @@ entities: - type: Transform pos: -28.5,68.5 parent: 2 - - uid: 14161 + - uid: 16753 components: - type: Transform - pos: -51.5,32.5 + pos: 15.5,-30.5 parent: 2 - proto: VendingMachineTheater entities: @@ -106912,6 +107634,16 @@ entities: parent: 2 - proto: VendingMachineYouTool entities: + - uid: 11152 + components: + - type: Transform + pos: -22.5,-34.5 + parent: 2 + - uid: 11247 + components: + - type: Transform + pos: -15.5,-19.5 + parent: 2 - uid: 14169 components: - type: Transform @@ -106922,11 +107654,6 @@ entities: - type: Transform pos: -43.5,38.5 parent: 2 - - uid: 14171 - components: - - type: Transform - pos: -20.5,-38.5 - parent: 2 - proto: WallmountGeneratorAPUElectronics entities: - uid: 14172 @@ -106998,6 +107725,531 @@ entities: fixtures: {} - proto: WallReinforced entities: + - uid: 115 + components: + - type: Transform + pos: -3.5,-40.5 + parent: 2 + - uid: 438 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-16.5 + parent: 2 + - uid: 675 + components: + - type: Transform + pos: -4.5,-33.5 + parent: 2 + - uid: 855 + components: + - type: Transform + pos: -0.5,-28.5 + parent: 2 + - uid: 856 + components: + - type: Transform + pos: -0.5,-25.5 + parent: 2 + - uid: 857 + components: + - type: Transform + pos: 1.5,-33.5 + parent: 2 + - uid: 858 + components: + - type: Transform + pos: -0.5,-26.5 + parent: 2 + - uid: 859 + components: + - type: Transform + pos: 0.5,-31.5 + parent: 2 + - uid: 860 + components: + - type: Transform + pos: 0.5,-32.5 + parent: 2 + - uid: 861 + components: + - type: Transform + pos: -0.5,-30.5 + parent: 2 + - uid: 966 + components: + - type: Transform + pos: -16.5,-36.5 + parent: 2 + - uid: 967 + components: + - type: Transform + pos: -16.5,-37.5 + parent: 2 + - uid: 1506 + components: + - type: Transform + pos: -51.5,33.5 + parent: 2 + - uid: 1508 + components: + - type: Transform + pos: -51.5,36.5 + parent: 2 + - uid: 1510 + components: + - type: Transform + pos: -51.5,34.5 + parent: 2 + - uid: 2698 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-20.5 + parent: 2 + - uid: 2699 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-20.5 + parent: 2 + - uid: 2700 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-20.5 + parent: 2 + - uid: 2701 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-18.5 + parent: 2 + - uid: 2703 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -15.5,-16.5 + parent: 2 + - uid: 2771 + components: + - type: Transform + pos: -18.5,-41.5 + parent: 2 + - uid: 2773 + components: + - type: Transform + pos: -20.5,-42.5 + parent: 2 + - uid: 2807 + components: + - type: Transform + pos: -18.5,-38.5 + parent: 2 + - uid: 2809 + components: + - type: Transform + pos: -16.5,-38.5 + parent: 2 + - uid: 2810 + components: + - type: Transform + pos: -0.5,-40.5 + parent: 2 + - uid: 2811 + components: + - type: Transform + pos: -17.5,-38.5 + parent: 2 + - uid: 2812 + components: + - type: Transform + pos: -13.5,-42.5 + parent: 2 + - uid: 2813 + components: + - type: Transform + pos: -12.5,-38.5 + parent: 2 + - uid: 2814 + components: + - type: Transform + pos: -17.5,-42.5 + parent: 2 + - uid: 2817 + components: + - type: Transform + pos: -14.5,-42.5 + parent: 2 + - uid: 2820 + components: + - type: Transform + pos: -0.5,-41.5 + parent: 2 + - uid: 2822 + components: + - type: Transform + pos: -18.5,-42.5 + parent: 2 + - uid: 2823 + components: + - type: Transform + pos: -19.5,-42.5 + parent: 2 + - uid: 2824 + components: + - type: Transform + pos: -18.5,-39.5 + parent: 2 + - uid: 2825 + components: + - type: Transform + pos: -12.5,-40.5 + parent: 2 + - uid: 2831 + components: + - type: Transform + pos: -8.5,-33.5 + parent: 2 + - uid: 2832 + components: + - type: Transform + pos: -0.5,-31.5 + parent: 2 + - uid: 2834 + components: + - type: Transform + pos: -0.5,-38.5 + parent: 2 + - uid: 2838 + components: + - type: Transform + pos: -9.5,-41.5 + parent: 2 + - uid: 3108 + components: + - type: Transform + pos: -14.5,-38.5 + parent: 2 + - uid: 4016 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-16.5 + parent: 2 + - uid: 4017 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -12.5,-16.5 + parent: 2 + - uid: 4020 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -13.5,-16.5 + parent: 2 + - uid: 4032 + components: + - type: Transform + pos: 0.5,-33.5 + parent: 2 + - uid: 4033 + components: + - type: Transform + pos: 0.5,-38.5 + parent: 2 + - uid: 4037 + components: + - type: Transform + pos: -13.5,-35.5 + parent: 2 + - uid: 4039 + components: + - type: Transform + pos: -13.5,-38.5 + parent: 2 + - uid: 4075 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-23.5 + parent: 2 + - uid: 4321 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -14.5,-20.5 + parent: 2 + - uid: 4324 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -12.5,-20.5 + parent: 2 + - uid: 4662 + components: + - type: Transform + pos: -55.5,34.5 + parent: 2 + - uid: 4663 + components: + - type: Transform + pos: -55.5,37.5 + parent: 2 + - uid: 4812 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -4.5,-20.5 + parent: 2 + - uid: 5116 + components: + - type: Transform + pos: -9.5,-38.5 + parent: 2 + - uid: 5336 + components: + - type: Transform + pos: -4.5,-31.5 + parent: 2 + - uid: 5741 + components: + - type: Transform + pos: -16.5,-34.5 + parent: 2 + - uid: 6037 + components: + - type: Transform + pos: -12.5,-41.5 + parent: 2 + - uid: 6447 + components: + - type: Transform + pos: -51.5,37.5 + parent: 2 + - uid: 7484 + components: + - type: Transform + pos: -8.5,-31.5 + parent: 2 + - uid: 7850 + components: + - type: Transform + pos: -6.5,-38.5 + parent: 2 + - uid: 7853 + components: + - type: Transform + pos: -3.5,-41.5 + parent: 2 + - uid: 7993 + components: + - type: Transform + pos: -53.5,33.5 + parent: 2 + - uid: 8257 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -11.5,-21.5 + parent: 2 + - uid: 8404 + components: + - type: Transform + pos: -17.5,-34.5 + parent: 2 + - uid: 8405 + components: + - type: Transform + pos: -19.5,-34.5 + parent: 2 + - uid: 8725 + components: + - type: Transform + pos: -50.5,31.5 + parent: 2 + - uid: 8726 + components: + - type: Transform + pos: -55.5,36.5 + parent: 2 + - uid: 8727 + components: + - type: Transform + pos: -54.5,37.5 + parent: 2 + - uid: 9125 + components: + - type: Transform + pos: -54.5,33.5 + parent: 2 + - uid: 9297 + components: + - type: Transform + pos: -18.5,-34.5 + parent: 2 + - uid: 9425 + components: + - type: Transform + pos: -16.5,-35.5 + parent: 2 + - uid: 9439 + components: + - type: Transform + pos: -6.5,-41.5 + parent: 2 + - uid: 9500 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -7.5,-20.5 + parent: 2 + - uid: 9534 + components: + - type: Transform + pos: -3.5,-39.5 + parent: 2 + - uid: 9569 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -18.5,-16.5 + parent: 2 + - uid: 9672 + components: + - type: Transform + pos: 2.5,-33.5 + parent: 2 + - uid: 9710 + components: + - type: Transform + pos: -12.5,-42.5 + parent: 2 + - uid: 9785 + components: + - type: Transform + pos: -52.5,37.5 + parent: 2 + - uid: 9974 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.5,-20.5 + parent: 2 + - uid: 9991 + components: + - type: Transform + pos: -6.5,-39.5 + parent: 2 + - uid: 9994 + components: + - type: Transform + pos: -6.5,-40.5 + parent: 2 + - uid: 10007 + components: + - type: Transform + pos: -15.5,-38.5 + parent: 2 + - uid: 10021 + components: + - type: Transform + pos: -3.5,-38.5 + parent: 2 + - uid: 10025 + components: + - type: Transform + pos: -9.5,-40.5 + parent: 2 + - uid: 10053 + components: + - type: Transform + pos: -12.5,-39.5 + parent: 2 + - uid: 10054 + components: + - type: Transform + pos: -9.5,-39.5 + parent: 2 + - uid: 10055 + components: + - type: Transform + pos: -0.5,-39.5 + parent: 2 + - uid: 10056 + components: + - type: Transform + pos: -16.5,-42.5 + parent: 2 + - uid: 10057 + components: + - type: Transform + pos: -15.5,-42.5 + parent: 2 + - uid: 10117 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,-27.5 + parent: 2 + - uid: 10542 + components: + - type: Transform + pos: -57.5,37.5 + parent: 2 + - uid: 10544 + components: + - type: Transform + pos: -57.5,33.5 + parent: 2 + - uid: 11364 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,-20.5 + parent: 2 + - uid: 11365 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,-20.5 + parent: 2 + - uid: 11419 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,-20.5 + parent: 2 + - uid: 11452 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,-20.5 + parent: 2 + - uid: 12336 + components: + - type: Transform + pos: -55.5,33.5 + parent: 2 + - uid: 12527 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-28.5 + parent: 2 + - uid: 13401 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -19.5,-16.5 + parent: 2 + - uid: 13416 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -19.5,-15.5 + parent: 2 - uid: 14181 components: - type: Transform @@ -107119,11 +108371,6 @@ entities: - type: Transform pos: -23.5,-27.5 parent: 2 - - uid: 14205 - components: - - type: Transform - pos: -3.5,-29.5 - parent: 2 - uid: 14206 components: - type: Transform @@ -107148,11 +108395,6 @@ entities: rot: 3.141592653589793 rad pos: 2.5,-19.5 parent: 2 - - uid: 14210 - components: - - type: Transform - pos: -0.5,-30.5 - parent: 2 - uid: 14211 components: - type: Transform @@ -107208,11 +108450,6 @@ entities: - type: Transform pos: -19.5,-30.5 parent: 2 - - uid: 14221 - components: - - type: Transform - pos: -11.5,-17.5 - parent: 2 - uid: 14222 components: - type: Transform @@ -107223,11 +108460,6 @@ entities: - type: Transform pos: -1.5,-17.5 parent: 2 - - uid: 14224 - components: - - type: Transform - pos: -6.5,-17.5 - parent: 2 - uid: 14225 components: - type: Transform @@ -107245,26 +108477,6 @@ entities: rot: -1.5707963267948966 rad pos: -4.5,-6.5 parent: 2 - - uid: 14228 - components: - - type: Transform - pos: -11.5,-20.5 - parent: 2 - - uid: 14229 - components: - - type: Transform - pos: -11.5,-21.5 - parent: 2 - - uid: 14230 - components: - - type: Transform - pos: -11.5,-22.5 - parent: 2 - - uid: 14231 - components: - - type: Transform - pos: -11.5,-23.5 - parent: 2 - uid: 14232 components: - type: Transform @@ -107285,11 +108497,6 @@ entities: - type: Transform pos: -3.5,-22.5 parent: 2 - - uid: 14236 - components: - - type: Transform - pos: -6.5,-20.5 - parent: 2 - uid: 14237 components: - type: Transform @@ -107425,11 +108632,6 @@ entities: - type: Transform pos: -25.5,-24.5 parent: 2 - - uid: 14264 - components: - - type: Transform - pos: -13.5,-31.5 - parent: 2 - uid: 14265 components: - type: Transform @@ -107445,40 +108647,15 @@ entities: - type: Transform pos: -19.5,-24.5 parent: 2 - - uid: 14268 - components: - - type: Transform - pos: -10.5,-32.5 - parent: 2 - - uid: 14269 - components: - - type: Transform - pos: -10.5,-31.5 - parent: 2 - - uid: 14270 - components: - - type: Transform - pos: -7.5,-30.5 - parent: 2 - - uid: 14271 - components: - - type: Transform - pos: -4.5,-30.5 - parent: 2 - - uid: 14272 - components: - - type: Transform - pos: -4.5,-32.5 - parent: 2 - uid: 14273 components: - type: Transform - pos: -3.5,-28.5 + pos: 1.5,-34.5 parent: 2 - uid: 14274 components: - type: Transform - pos: -3.5,-30.5 + pos: 1.5,-38.5 parent: 2 - uid: 14275 components: @@ -107508,32 +108685,12 @@ entities: - uid: 14280 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -8.5,-33.5 - parent: 2 - - uid: 14281 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -7.5,-33.5 + pos: 1.5,-37.5 parent: 2 - uid: 14282 components: - type: Transform - rot: -1.5707963267948966 rad - pos: -13.5,-33.5 - parent: 2 - - uid: 14283 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -10.5,-33.5 - parent: 2 - - uid: 14284 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-33.5 + pos: 1.5,-35.5 parent: 2 - uid: 14285 components: @@ -107557,11 +108714,6 @@ entities: - type: Transform pos: -6.5,4.5 parent: 2 - - uid: 14289 - components: - - type: Transform - pos: -16.5,-31.5 - parent: 2 - uid: 14290 components: - type: Transform @@ -107577,17 +108729,6 @@ entities: - type: Transform pos: -57.5,18.5 parent: 2 - - uid: 14293 - components: - - type: Transform - pos: -16.5,-30.5 - parent: 2 - - uid: 14294 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.5,-33.5 - parent: 2 - uid: 14295 components: - type: Transform @@ -107598,11 +108739,6 @@ entities: - type: Transform pos: -39.5,-39.5 parent: 2 - - uid: 14297 - components: - - type: Transform - pos: -15.5,-19.5 - parent: 2 - uid: 14298 components: - type: Transform @@ -107627,26 +108763,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,-24.5 parent: 2 - - uid: 14302 - components: - - type: Transform - pos: -15.5,-17.5 - parent: 2 - - uid: 14303 - components: - - type: Transform - pos: -15.5,-18.5 - parent: 2 - - uid: 14304 - components: - - type: Transform - pos: -13.5,-16.5 - parent: 2 - - uid: 14305 - components: - - type: Transform - pos: -12.5,-16.5 - parent: 2 - uid: 14306 components: - type: Transform @@ -107662,51 +108778,6 @@ entities: - type: Transform pos: 11.5,-13.5 parent: 2 - - uid: 14309 - components: - - type: Transform - pos: -3.5,-33.5 - parent: 2 - - uid: 14310 - components: - - type: Transform - pos: -13.5,-32.5 - parent: 2 - - uid: 14311 - components: - - type: Transform - pos: -13.5,-30.5 - parent: 2 - - uid: 14312 - components: - - type: Transform - pos: -10.5,-30.5 - parent: 2 - - uid: 14313 - components: - - type: Transform - pos: -7.5,-31.5 - parent: 2 - - uid: 14314 - components: - - type: Transform - pos: -7.5,-32.5 - parent: 2 - - uid: 14315 - components: - - type: Transform - pos: -4.5,-31.5 - parent: 2 - - uid: 14316 - components: - - type: Transform - pos: -3.5,-25.5 - parent: 2 - - uid: 14317 - components: - - type: Transform - pos: -3.5,-26.5 - parent: 2 - uid: 14318 components: - type: Transform @@ -107745,18 +108816,6 @@ entities: rot: 1.5707963267948966 rad pos: 5.5,-21.5 parent: 2 - - uid: 14325 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-22.5 - parent: 2 - - uid: 14326 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-23.5 - parent: 2 - uid: 14327 components: - type: Transform @@ -108165,7 +109224,8 @@ entities: - uid: 14405 components: - type: Transform - pos: 21.5,-28.5 + rot: 1.5707963267948966 rad + pos: 20.5,-32.5 parent: 2 - uid: 14406 components: @@ -108205,7 +109265,8 @@ entities: - uid: 14413 components: - type: Transform - pos: 21.5,-29.5 + rot: 1.5707963267948966 rad + pos: 21.5,-33.5 parent: 2 - uid: 14414 components: @@ -108262,11 +109323,6 @@ entities: - type: Transform pos: -2.5,6.5 parent: 2 - - uid: 14425 - components: - - type: Transform - pos: -50.5,36.5 - parent: 2 - uid: 14426 components: - type: Transform @@ -108302,11 +109358,6 @@ entities: - type: Transform pos: 33.5,-3.5 parent: 2 - - uid: 14433 - components: - - type: Transform - pos: -52.5,36.5 - parent: 2 - uid: 14434 components: - type: Transform @@ -108402,11 +109453,6 @@ entities: - type: Transform pos: -31.5,-33.5 parent: 2 - - uid: 14453 - components: - - type: Transform - pos: -21.5,-39.5 - parent: 2 - uid: 14454 components: - type: Transform @@ -108486,18 +109532,6 @@ entities: - type: Transform pos: 45.5,-22.5 parent: 2 - - uid: 14469 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 2.5,-33.5 - parent: 2 - - uid: 14470 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-31.5 - parent: 2 - uid: 14471 components: - type: Transform @@ -108584,12 +109618,6 @@ entities: - type: Transform pos: -32.5,-30.5 parent: 2 - - uid: 14488 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-32.5 - parent: 2 - uid: 14489 components: - type: Transform @@ -108678,12 +109706,6 @@ entities: - type: Transform pos: -35.5,-34.5 parent: 2 - - uid: 14506 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -16.5,-32.5 - parent: 2 - uid: 14507 components: - type: Transform @@ -108880,16 +109902,6 @@ entities: - type: Transform pos: 48.5,-20.5 parent: 2 - - uid: 14546 - components: - - type: Transform - pos: -17.5,-44.5 - parent: 2 - - uid: 14547 - components: - - type: Transform - pos: -17.5,-43.5 - parent: 2 - uid: 14548 components: - type: Transform @@ -110024,11 +111036,6 @@ entities: rot: 1.5707963267948966 rad pos: 14.5,-0.5 parent: 2 - - uid: 14768 - components: - - type: Transform - pos: -15.5,-20.5 - parent: 2 - uid: 14769 components: - type: Transform @@ -111185,7 +112192,8 @@ entities: - uid: 14990 components: - type: Transform - pos: 21.5,-30.5 + rot: 1.5707963267948966 rad + pos: 20.5,-30.5 parent: 2 - uid: 14991 components: @@ -112905,30 +113913,10 @@ entities: - type: Transform pos: -16.5,52.5 parent: 2 - - uid: 15318 - components: - - type: Transform - pos: -54.5,36.5 - parent: 2 - - uid: 15319 - components: - - type: Transform - pos: -54.5,32.5 - parent: 2 - - uid: 15320 - components: - - type: Transform - pos: -54.5,34.5 - parent: 2 - uid: 15321 components: - type: Transform - pos: -54.5,39.5 - parent: 2 - - uid: 15322 - components: - - type: Transform - pos: -57.5,37.5 + pos: -51.5,39.5 parent: 2 - uid: 15323 components: @@ -112955,11 +113943,6 @@ entities: - type: Transform pos: -56.5,39.5 parent: 2 - - uid: 15328 - components: - - type: Transform - pos: -57.5,36.5 - parent: 2 - uid: 15329 components: - type: Transform @@ -113459,11 +114442,6 @@ entities: - type: Transform pos: -31.5,-0.5 parent: 2 - - uid: 15428 - components: - - type: Transform - pos: -0.5,-33.5 - parent: 2 - uid: 15429 components: - type: Transform @@ -113484,11 +114462,6 @@ entities: - type: Transform pos: -57.5,14.5 parent: 2 - - uid: 15433 - components: - - type: Transform - pos: -19.5,-39.5 - parent: 2 - uid: 15434 components: - type: Transform @@ -113499,26 +114472,11 @@ entities: - type: Transform pos: -26.5,-8.5 parent: 2 - - uid: 15436 - components: - - type: Transform - pos: -26.5,-7.5 - parent: 2 - uid: 15437 components: - type: Transform pos: 40.5,15.5 parent: 2 - - uid: 15438 - components: - - type: Transform - pos: -5.5,-33.5 - parent: 2 - - uid: 15439 - components: - - type: Transform - pos: -6.5,-33.5 - parent: 2 - uid: 15440 components: - type: Transform @@ -113584,11 +114542,6 @@ entities: - type: Transform pos: -59.5,21.5 parent: 2 - - uid: 15453 - components: - - type: Transform - pos: 2.5,-30.5 - parent: 2 - uid: 15454 components: - type: Transform @@ -113918,11 +114871,6 @@ entities: - type: Transform pos: 51.5,-3.5 parent: 2 - - uid: 15519 - components: - - type: Transform - pos: -17.5,-45.5 - parent: 2 - uid: 15520 components: - type: Transform @@ -114285,23 +115233,6 @@ entities: - type: Transform pos: 51.5,-1.5 parent: 2 - - uid: 15588 - components: - - type: Transform - pos: -19.5,-46.5 - parent: 2 - - uid: 15589 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-31.5 - parent: 2 - - uid: 15590 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-32.5 - parent: 2 - uid: 15591 components: - type: Transform @@ -114337,11 +115268,6 @@ entities: - type: Transform pos: 38.5,-14.5 parent: 2 - - uid: 15598 - components: - - type: Transform - pos: -17.5,-42.5 - parent: 2 - uid: 15599 components: - type: Transform @@ -114362,21 +115288,6 @@ entities: - type: Transform pos: 44.5,-19.5 parent: 2 - - uid: 15603 - components: - - type: Transform - pos: -17.5,-46.5 - parent: 2 - - uid: 15604 - components: - - type: Transform - pos: -18.5,-46.5 - parent: 2 - - uid: 15605 - components: - - type: Transform - pos: -20.5,-46.5 - parent: 2 - uid: 15606 components: - type: Transform @@ -114392,30 +115303,77 @@ entities: - type: Transform pos: 53.5,-1.5 parent: 2 - - uid: 15609 + - uid: 16394 components: - type: Transform - pos: -17.5,-41.5 + rot: 1.5707963267948966 rad + pos: 15.5,-27.5 parent: 2 - - uid: 15610 + - uid: 16395 components: - type: Transform - pos: -17.5,-39.5 + rot: 1.5707963267948966 rad + pos: 14.5,-27.5 parent: 2 - - uid: 15611 + - uid: 16396 components: - type: Transform - pos: -18.5,-39.5 + rot: 1.5707963267948966 rad + pos: 14.5,-28.5 parent: 2 - - uid: 15612 + - uid: 16397 components: - type: Transform - pos: -17.5,-40.5 + rot: 1.5707963267948966 rad + pos: 17.5,-27.5 parent: 2 - - uid: 15613 + - uid: 16398 components: - type: Transform - pos: -20.5,-39.5 + rot: 1.5707963267948966 rad + pos: 19.5,-27.5 + parent: 2 + - uid: 16399 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-27.5 + parent: 2 + - uid: 16400 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 20.5,-33.5 + parent: 2 + - uid: 16401 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 19.5,-33.5 + parent: 2 + - uid: 16402 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 17.5,-33.5 + parent: 2 + - uid: 16441 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 15.5,-33.5 + parent: 2 + - uid: 16503 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-33.5 + parent: 2 + - uid: 16526 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 14.5,-32.5 parent: 2 - uid: 16836 components: @@ -115733,8 +116691,20 @@ entities: - type: Transform pos: 10.5,16.5 parent: 16911 + - uid: 19525 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -27.5,-8.5 + parent: 2 - proto: WallReinforcedRust entities: + - uid: 13543 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -19.5,-17.5 + parent: 2 - uid: 15614 components: - type: Transform @@ -115780,31 +116750,6 @@ entities: - type: Transform pos: -27.5,-32.5 parent: 2 - - uid: 15623 - components: - - type: Transform - pos: -11.5,-16.5 - parent: 2 - - uid: 15624 - components: - - type: Transform - pos: -8.5,-17.5 - parent: 2 - - uid: 15625 - components: - - type: Transform - pos: -8.5,-20.5 - parent: 2 - - uid: 15626 - components: - - type: Transform - pos: -15.5,-16.5 - parent: 2 - - uid: 15627 - components: - - type: Transform - pos: -14.5,-16.5 - parent: 2 - uid: 15628 components: - type: Transform @@ -116079,11 +117024,6 @@ entities: rot: -1.5707963267948966 rad pos: -44.5,31.5 parent: 2 - - uid: 15680 - components: - - type: Transform - pos: -15.5,-14.5 - parent: 2 - uid: 15681 components: - type: Transform @@ -117451,16 +118391,6 @@ entities: - type: Transform pos: -4.5,18.5 parent: 2 - - uid: 15941 - components: - - type: Transform - pos: -19.5,-16.5 - parent: 2 - - uid: 15942 - components: - - type: Transform - pos: -19.5,-15.5 - parent: 2 - uid: 15943 components: - type: Transform @@ -119383,12 +120313,6 @@ entities: - type: Transform pos: -10.5,-5.5 parent: 2 - - uid: 16306 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -11.5,-14.5 - parent: 2 - uid: 16307 components: - type: Transform @@ -120230,16 +121154,6 @@ entities: - type: Transform pos: -70.5,34.5 parent: 2 - - uid: 16357 - components: - - type: Transform - pos: -19.5,-17.5 - parent: 2 - - uid: 16358 - components: - - type: Transform - pos: -17.5,-16.5 - parent: 2 - uid: 16359 components: - type: Transform @@ -120431,18 +121345,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -120524,154 +121428,61 @@ entities: - type: Transform pos: -35.5,38.5 parent: 2 -- proto: WarningAir - entities: - - uid: 16384 - components: - - type: Transform - pos: -13.5,-30.5 - parent: 2 - - type: Fixtures - fixtures: {} - proto: WarningN2 entities: - - uid: 16385 + - uid: 8092 components: - type: Transform - pos: -7.5,-30.5 + rot: 3.141592653589793 rad + pos: -6.5,-38.5 parent: 2 - type: Fixtures fixtures: {} - proto: WarningO2 entities: - - uid: 16386 + - uid: 8096 components: - type: Transform - pos: -10.5,-30.5 + rot: 3.141592653589793 rad + pos: -9.5,-38.5 parent: 2 - type: Fixtures fixtures: {} - proto: WarningPlasma entities: - - uid: 16387 + - uid: 8124 components: - type: Transform - pos: 2.5,-30.5 + rot: 3.141592653589793 rad + pos: -13.5,-35.5 parent: 2 - type: Fixtures fixtures: {} - proto: WarningWaste entities: - - uid: 16388 + - uid: 8085 components: - type: Transform - pos: -16.5,-30.5 + rot: 3.141592653589793 rad + pos: -0.5,-38.5 parent: 2 - type: Fixtures fixtures: {} -- proto: WarpPointBombing - entities: - - uid: 16389 + - uid: 8086 components: - type: Transform - pos: 24.5,-42.5 + rot: 3.141592653589793 rad + pos: -3.5,-38.5 parent: 2 - - type: WarpPoint - location: Южные Солнечные панели - - uid: 16390 - components: - - type: Transform - pos: 17.5,-18.5 - parent: 2 - - type: WarpPoint - location: Кабинет АВД - - uid: 16391 - components: - - type: Transform - pos: -12.5,-4.5 - parent: 2 - - type: WarpPoint - location: Лаборатория аномалий - - uid: 16392 - components: - - type: Transform - pos: -19.5,-43.5 - parent: 2 - - type: WarpPoint - location: Ядерный реактор - - uid: 16393 - components: - - type: Transform - pos: -13.5,2.5 - parent: 2 - - type: WarpPoint - location: Серверная НИО - - uid: 16394 - components: - - type: Transform - pos: -35.5,-14.5 - parent: 2 - - type: WarpPoint - location: Телекоммуникации - - uid: 16395 - components: - - type: Transform - pos: -23.5,-20.5 - parent: 2 - - type: WarpPoint - location: Двигатель антиматерии - - uid: 16396 - components: - - type: Transform - pos: 8.5,-22.5 - parent: 2 - - type: WarpPoint - location: Каюта ГП - - uid: 16397 - components: - - type: Transform - pos: -0.5,-19.5 - parent: 2 - - type: WarpPoint - location: Технологическое хранилище - - uid: 16398 - components: - - type: Transform - pos: -2.5,-7.5 - parent: 2 - - type: WarpPoint - location: Золотой унитаз капитана - - uid: 16399 - components: - - type: Transform - pos: -29.5,1.5 - parent: 2 - - type: WarpPoint - follow: True - location: Западные Солнечные панели - - uid: 16400 - components: - - type: Transform - pos: -11.5,39.5 - parent: 2 - - type: WarpPoint - location: Храм - - uid: 16401 - components: - - type: Transform - pos: 36.5,-5.5 - parent: 2 - - type: WarpPoint - location: Офис КМа - - uid: 16402 - components: - - type: Transform - pos: 42.5,6.5 - parent: 2 - - type: WarpPoint - location: Стыковочный док снабжения + - type: Fixtures + fixtures: {} - proto: WaterCooler entities: + - uid: 12203 + components: + - type: Transform + pos: -14.5,-25.5 + parent: 2 - uid: 16403 components: - type: Transform @@ -120895,10 +121706,10 @@ entities: parent: 2 - proto: WaterVaporCanister entities: - - uid: 16441 + - uid: 5337 components: - type: Transform - pos: -6.5,-31.5 + pos: -1.5,-39.5 parent: 2 - uid: 16442 components: @@ -121055,6 +121866,13 @@ entities: - type: Transform pos: 31.5,-28.5 parent: 2 +- proto: WeaponShotgunDoubleBarreled + entities: + - uid: 11526 + components: + - type: Transform + pos: -54.390926,34.710518 + parent: 2 - proto: WeaponShotgunDoubleBarreledRubber entities: - uid: 16464 @@ -121067,11 +121885,6 @@ entities: - type: Transform pos: 42.422455,-25.492567 parent: 2 - - uid: 16466 - components: - - type: Transform - pos: -10.509344,-23.150303 - parent: 2 - proto: WeaponShotgunEnforcer entities: - uid: 16467 @@ -121297,10 +122110,10 @@ entities: parent: 2 - proto: WeldingFuelTankHighCapacity entities: - - uid: 16503 + - uid: 11184 components: - type: Transform - pos: -14.5,-25.5 + pos: -3.5,-25.5 parent: 2 - uid: 16504 components: @@ -121389,11 +122202,6 @@ entities: parent: 2 - proto: WindoorSecure entities: - - uid: 16516 - components: - - type: Transform - pos: 29.5,35.5 - parent: 2 - uid: 16517 components: - type: Transform @@ -121448,48 +122256,6 @@ entities: rot: -1.5707963267948966 rad pos: 32.5,-28.5 parent: 2 -- proto: WindoorSecureAtmosphericsLocked - entities: - - uid: 16526 - components: - - type: Transform - pos: -5.5,-29.5 - parent: 2 - - uid: 16527 - components: - - type: Transform - pos: -6.5,-29.5 - parent: 2 - - uid: 16528 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -12.5,-31.5 - parent: 2 - - uid: 16529 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -18.5,-31.5 - parent: 2 - - uid: 16530 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -15.5,-31.5 - parent: 2 - - uid: 16531 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -9.5,-31.5 - parent: 2 - - uid: 16532 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-31.5 - parent: 2 - proto: WindoorSecureChemistryLocked entities: - uid: 16533 @@ -121534,6 +122300,16 @@ entities: parent: 2 - proto: WindoorSecureEngineeringLocked entities: + - uid: 9429 + components: + - type: Transform + pos: -17.5,-16.5 + parent: 2 + - uid: 12421 + components: + - type: Transform + pos: -16.5,-16.5 + parent: 2 - uid: 16540 components: - type: Transform @@ -121564,21 +122340,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,15.5 parent: 2 -- proto: WindoorSecurePlasma - entities: - - uid: 16544 - components: - - type: Transform - pos: -7.5,-22.5 - parent: 2 - - type: DeviceLinkSource - linkedPorts: - 12017: - - - DoorStatus - - On - 12018: - - - DoorStatus - - On - proto: WindoorSecureSalvageLocked entities: - uid: 16545 @@ -121726,21 +122487,6 @@ entities: - type: Transform pos: -11.5,-6.5 parent: 2 - - uid: 16569 - components: - - type: Transform - pos: -14.5,-14.5 - parent: 2 - - uid: 16570 - components: - - type: Transform - pos: -13.5,-14.5 - parent: 2 - - uid: 16571 - components: - - type: Transform - pos: -12.5,-14.5 - parent: 2 - uid: 16572 components: - type: Transform @@ -122289,6 +123035,42 @@ entities: parent: 2 - proto: WindowReinforcedDirectional entities: + - uid: 9669 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-27.5 + parent: 2 + - uid: 9849 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-26.5 + parent: 2 + - uid: 9999 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-25.5 + parent: 2 + - uid: 10003 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -13.5,-25.5 + parent: 2 + - uid: 12188 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-26.5 + parent: 2 + - uid: 12189 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -11.5,-27.5 + parent: 2 - uid: 16661 components: - type: Transform @@ -122306,29 +123088,11 @@ entities: rot: -1.5707963267948966 rad pos: 32.5,-25.5 parent: 2 - - uid: 16664 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-35.5 - parent: 2 - uid: 16665 components: - type: Transform pos: 36.5,-12.5 parent: 2 - - uid: 16666 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -22.5,-39.5 - parent: 2 - - uid: 16667 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -23.5,-39.5 - parent: 2 - uid: 16668 components: - type: Transform @@ -122619,24 +123383,6 @@ entities: rot: 1.5707963267948966 rad pos: -32.5,-16.5 parent: 2 - - uid: 16718 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-36.5 - parent: 2 - - uid: 16719 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-37.5 - parent: 2 - - uid: 16720 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -23.5,-38.5 - parent: 2 - uid: 16721 components: - type: Transform @@ -122820,16 +123566,6 @@ entities: rot: -1.5707963267948966 rad pos: -28.5,4.5 parent: 2 - - uid: 16753 - components: - - type: Transform - pos: -53.5,34.5 - parent: 2 - - uid: 16754 - components: - - type: Transform - pos: -51.5,33.5 - parent: 2 - uid: 16755 components: - type: Transform @@ -123163,6 +123899,33 @@ entities: canCollide: False - proto: WoodenBench entities: + - uid: 994 + components: + - type: Transform + pos: -13.5,-13.5 + parent: 2 + - uid: 1282 + components: + - type: Transform + pos: -14.5,-13.5 + parent: 2 + - uid: 4076 + components: + - type: Transform + pos: -12.5,-13.5 + parent: 2 + - uid: 5796 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 21.5,-30.5 + parent: 2 + - uid: 8221 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -10.5,-16.5 + parent: 2 - uid: 16810 components: - type: Transform @@ -123303,6 +124066,11 @@ entities: parent: 16911 - proto: Wrench entities: + - uid: 14313 + components: + - type: Transform + pos: -16.854548,-29.9884 + parent: 2 - uid: 16828 components: - type: Transform From ba0230239ce88703d6e716bc84241abffc8aa40c Mon Sep 17 00:00:00 2001 From: Dmitry <57028746+DIMMoon1@users.noreply.github.com> Date: Sat, 18 Apr 2026 21:36:30 +0700 Subject: [PATCH 204/247] miniclean --- Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 7a5ac41638..156a6d91f9 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -42,10 +42,7 @@ using Robust.Shared.Utility; using System.Data; using System.Linq; using System.Text; -using Content.Shared.CombatMode.Pacification; -using Content.Shared.Station.Components; -using Content.Shared.Store.Components; -using Robust.Shared.Prototypes; +using Content.Shared.CombatMode.Pacification;//Corvax-DionaPacifist namespace Content.Server.GameTicking.Rules; From 869dd7a743aaa71a0434f777b71e94768f1ae274 Mon Sep 17 00:00:00 2001 From: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Date: Sat, 18 Apr 2026 16:54:30 +0200 Subject: [PATCH 205/247] Changeling clone melee, puller and (most of) temperature (#43609) * init * docs * alphabet and networking * weh --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Cloning/CloningSystem.Subscriptions.cs | 12 ++++++++++++ .../Pulling/Components/PullerComponent.cs | 16 +++++++++++----- .../Movement/Pulling/Systems/PullingSystem.cs | 17 +++++++++++++++++ .../Weapons/Melee/MeleeWeaponComponent.cs | 2 +- .../Prototypes/Entities/Mobs/Player/clone.yml | 6 +++++- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/Content.Server/Cloning/CloningSystem.Subscriptions.cs b/Content.Server/Cloning/CloningSystem.Subscriptions.cs index 10ac8bc9ed..2f8491b2f3 100644 --- a/Content.Server/Cloning/CloningSystem.Subscriptions.cs +++ b/Content.Server/Cloning/CloningSystem.Subscriptions.cs @@ -6,6 +6,8 @@ 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; @@ -32,6 +34,7 @@ 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 PullingSystem _pulling = default!; public override void Initialize() { @@ -54,6 +57,7 @@ public sealed partial class CloningSystem SubscribeLocalEvent(OnCloneStorage); SubscribeLocalEvent(OnCloneInventory); SubscribeLocalEvent(OnCloneMovementSpeedModifier); + SubscribeLocalEvent(OnClonePuller); } private void OnCloneItemStack(Entity ent, ref CloningItemEvent args) @@ -127,4 +131,12 @@ public sealed partial class CloningSystem _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.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs index 197d7cfd7c..172ab449b4 100644 --- a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs @@ -21,7 +21,10 @@ public sealed partial class PullerComponent : Component [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, Access(Other = AccessPermissions.ReadWriteExecute)] public TimeSpan NextThrow; - [DataField] + /// + /// Minimum time between pull throws. + /// + [DataField, AutoNetworkedField] public TimeSpan ThrowCooldown = TimeSpan.FromSeconds(1); // Before changing how this is updated, please see SharedPullerSystem.RefreshMovementSpeed @@ -32,16 +35,19 @@ public sealed partial class PullerComponent : Component /// /// Entity currently being pulled if applicable. /// - [AutoNetworkedField, DataField] + [DataField, AutoNetworkedField] public EntityUid? Pulling; /// - /// Does this entity need hands to be able to pull something? + /// Does this entity need hands to be able to pull something? /// - [DataField] + [DataField, AutoNetworkedField] public bool NeedsHands = true; - [DataField] + /// + /// The alert shown to the puller indicating that they are pulling something. + /// + [DataField, AutoNetworkedField] public ProtoId PullingAlert = "Pulling"; } diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 0fcdf7ebed..03e37ed3e7 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -614,4 +614,21 @@ public sealed class PullingSystem : EntitySystem StopPulling(pullableUid, pullable); return true; } + + /// + /// Copies compatible datafields of onto the target entity. + /// + /// The entity who's component will be taken. + /// The entity to apply it to. + public void CopyPullerComponent(Entity source, EntityUid target) + { + if (!Resolve(source, ref source.Comp)) + return; + + var targetComp = EnsureComp(target); + targetComp.ThrowCooldown = source.Comp.ThrowCooldown; + targetComp.NeedsHands = source.Comp.NeedsHands; + targetComp.PullingAlert = source.Comp.PullingAlert; + Dirty(target, targetComp); + } } diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index 54e6ddbc1c..03276a43ab 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -130,7 +130,7 @@ public sealed partial class MeleeWeaponComponent : Component /// We don't connect it with attack range, because different weapons have different sprites, /// and this value should be adjusted manually for every weapon ideally /// - [DataField] + [DataField, AutoNetworkedField] public float AnimationOffset = 1f; // Sounds diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 6942263c65..7914175cf5 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -9,14 +9,17 @@ id: Body components: # general + - CreamPied - DetailExaminable - Dna - Fingerprint - Grammar # Pronouns - HumanoidProfile # Age, Sex, Gender + - MeleeWeapon # Attack animation, damage and sound - NpcFactionMember - RandomPrice - - CreamPied + - TemperatureDamage + - TemperatureSpeed # traits - BlackAndWhiteOverlay - Clumsy @@ -59,6 +62,7 @@ - SpanishAccent - StutteringAccent eventComponents: + - Puller - Vocal # voice sounds # for job-specific traits etc. From 491600a2bbb8a4c42430fa9eafb34d04a0da6124 Mon Sep 17 00:00:00 2001 From: Dmitry <57028746+DIMMoon1@users.noreply.github.com> Date: Sat, 18 Apr 2026 21:55:15 +0700 Subject: [PATCH 206/247] mapfix --- Resources/Maps/Corvax/corvax_astra.yml | 485 ++-- Resources/Maps/Corvax/corvax_chloris.yml | 2832 ++++++++-------------- Resources/Maps/Corvax/corvax_paper.yml | 385 +-- 3 files changed, 1275 insertions(+), 2427 deletions(-) diff --git a/Resources/Maps/Corvax/corvax_astra.yml b/Resources/Maps/Corvax/corvax_astra.yml index c3e912111c..ac632c3d3a 100644 --- a/Resources/Maps/Corvax/corvax_astra.yml +++ b/Resources/Maps/Corvax/corvax_astra.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 268.0.0 + engineVersion: 275.2.0 forkId: "" forkVersion: "" - time: 12/11/2025 08:13:29 - entityCount: 41135 + time: 04/18/2026 14:29:51 + entityCount: 41133 maps: - 1 grids: @@ -60,7 +60,6 @@ tilemap: 52: FloorGrassLight 53: FloorGrayConcrete 54: FloorGrayConcreteMono - 87: FloorGrayConcreteOutside 55: FloorGrayConcreteSmooth 56: FloorGreenCircuit 57: FloorGym @@ -246,7 +245,7 @@ entities: version: 7 4,0: ind: 4,0 - tiles: cAAAAAAAACkAAAAAAAApAAAAAAAAfgAAAAAAAB8AAAAAAAAXAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAHAAAAAAAAApAAAAAAAAKQAAAAAAAH4AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAfgAAAAAAAGQAAAAAAABkAAAAAAAAZAAAAAAAAH4AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAfgAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAFwAAAAAAAB8AAAAAAAAvAAAAAAAAHwAAAAAAABcAAAAAAAA1AAAAAAAANQAAAAAAADUAAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAAfAAAAAAAAFwAAAAAAAB8AAAAAAAB+AAAAAAAANQAAAAAAADUAAAAAAAA1AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAB+AAAAAAAAHwAAAAAAAC8AAAAAAAAfAAAAAAAAfgAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAADYAAAAAAAA2AAAAAAAAfgAAAAAAADcAAAAAAAA3AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA3AAAAAAAANwAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANwAAAAAAADcAAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADcAAAAAAAA3AAAAAAAAfgAAAAAAAH0AAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAAH4AAAAAAAA3AAAAAAAANwAAAAAAAH4AAAAAAAB9AAAAAAAAAAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcAAAAAAABXAAAAAAAAVwAAAAAAAH4AAAAAAABsAAAAAAAAfgAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAAAAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABsAAAAAAAAbAAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAAAAAVwAAAAAAAFcAAAAAAABXAAAAAAAAfgAAAAAAAGwAAAAAAAB+AAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: cAAAAAAAACkAAAAAAAApAAAAAAAAfgAAAAAAAB8AAAAAAAAXAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAF0AAAAAAABdAAAAAAAAXQAAAAAAAHAAAAAAAAApAAAAAAAAKQAAAAAAAH4AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAfgAAAAAAAGQAAAAAAABkAAAAAAAAZAAAAAAAAH4AAAAAAABdAAAAAAAAXQAAAAAAAF0AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAfgAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAFwAAAAAAAB8AAAAAAAAvAAAAAAAAHwAAAAAAABcAAAAAAAA1AAAAAAAANQAAAAAAADUAAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAAfAAAAAAAAHwAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAAfAAAAAAAAFwAAAAAAAB8AAAAAAAB+AAAAAAAANQAAAAAAADUAAAAAAAA1AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAB8AAAAAAAB+AAAAAAAAHwAAAAAAAC8AAAAAAAAfAAAAAAAAfgAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAADYAAAAAAAA2AAAAAAAAfgAAAAAAADcAAAAAAAA3AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAB8AAAAAAAAfAAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAA2AAAAAAAANgAAAAAAADYAAAAAAAA3AAAAAAAANwAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH4AAAAAAAB+AAAAAAAAHwAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAANgAAAAAAADYAAAAAAAA2AAAAAAAANwAAAAAAADcAAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAADYAAAAAAAA2AAAAAAAANgAAAAAAADcAAAAAAAA3AAAAAAAAfgAAAAAAAH0AAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAAAAANgAAAAAAAH4AAAAAAAA3AAAAAAAANwAAAAAAAH4AAAAAAAB9AAAAAAAAAAAAAAAAAH0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfgAAAAAAAH4AAAAAAAB+AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUAAAAAAAA1AAAAAAAANQAAAAAAAH4AAAAAAABsAAAAAAAAfgAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAAAAAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABsAAAAAAAAbAAAAAAAAH4AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAfQAAAAAAAH0AAAAAAAB9AAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAAAAANQAAAAAAADUAAAAAAAA1AAAAAAAAfgAAAAAAAGwAAAAAAAB+AAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== version: 7 5,-1: ind: 5,-1 @@ -19417,6 +19416,9 @@ entities: id: Astra - type: NavMap - type: ImplicitRoof + - type: TileHistory + chunkHistory: {} + - type: ExplosionAirtightGrid - uid: 4 components: - type: MetaData @@ -20262,6 +20264,9 @@ entities: chunkSize: 4 - type: GasTileOverlay - type: ImplicitRoof + - type: TileHistory + chunkHistory: {} + - type: ExplosionAirtightGrid - proto: AccessConfigurator entities: - uid: 156 @@ -33454,18 +33459,6 @@ entities: - type: Transform pos: -2.5,-53.5 parent: 2 -- proto: BaseChemistryEmptyVial - entities: - - uid: 1953 - components: - - type: Transform - pos: -37.747562,-34.233185 - parent: 2 - - uid: 1954 - components: - - type: Transform - pos: -37.888187,-34.326935 - parent: 2 - proto: BaseComputer entities: - uid: 1955 @@ -103328,6 +103321,18 @@ entities: parent: 1965 - type: Physics canCollide: False +- proto: ChemistryEmptyVial + entities: + - uid: 1953 + components: + - type: Transform + pos: -37.747562,-34.233185 + parent: 2 + - uid: 1954 + components: + - type: Transform + pos: -37.888187,-34.326935 + parent: 2 - proto: ChemistryHotplate entities: - uid: 15563 @@ -106182,8 +106187,8 @@ entities: - type: Transform parent: 159 - type: Clothing - inSlotFlag: HEAD inSlot: head + inSlotFlag: HEAD - type: HandheldLight toggleActionEntity: 161 - type: ContainerContainer @@ -106675,8 +106680,8 @@ entities: - type: Transform parent: 16099 - type: Clothing - inSlotFlag: MASK inSlot: mask + inSlotFlag: MASK - type: Physics canCollide: False - proto: ClothingMaskGasAtmos @@ -106686,8 +106691,8 @@ entities: - type: Transform parent: 159 - type: Clothing - inSlotFlag: MASK inSlot: mask + inSlotFlag: MASK - type: Physics canCollide: False - uid: 16102 @@ -106788,8 +106793,8 @@ entities: - type: TypingIndicatorClothing gotEquippedTime: 5513.9278194 - type: Clothing - inSlotFlag: NECK inSlot: neck + inSlotFlag: NECK - type: Physics canCollide: False - proto: ClothingNeckCloakNanotrasen @@ -108012,8 +108017,8 @@ entities: - type: Transform parent: 16099 - type: Clothing - inSlotFlag: INNERCLOTHING inSlot: jumpsuit + inSlotFlag: INNERCLOTHING - type: Physics canCollide: False - proto: ClothingUniformJumpsuitRepairmanNT @@ -108023,8 +108028,8 @@ entities: - type: Transform parent: 159 - type: Clothing - inSlotFlag: INNERCLOTHING inSlot: jumpsuit + inSlotFlag: INNERCLOTHING - type: Physics canCollide: False - proto: ClothingUniformJumpsuitSafari @@ -110062,8 +110067,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 16561 @@ -110539,8 +110544,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateHydroponics @@ -110605,8 +110610,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateHydroSecure @@ -110788,8 +110793,8 @@ entities: pos: 29.5,21.5 parent: 2 - type: EntityStorage - open: True removedMasks: 28 + open: True - type: Fixtures fixtures: fix1: @@ -111076,8 +111081,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateSecgear @@ -111111,8 +111116,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 16653 @@ -111396,8 +111401,8 @@ entities: immutable: False temperature: 293.147 moles: {} - open: True removedMasks: 20 + open: True - type: Fixtures fixtures: fix1: @@ -111943,7 +111948,6 @@ entities: desc: Это плиткорез. ПЛИТКОРЕЗ. Придайте разнообразия полу вашей станции с помощью интересных узоров! Не засовывайте внутрь пальцы. name: плиткорез - type: Transform - rot: 3.141592653589793 rad pos: 49.5,-46.5 parent: 2 - uid: 16761 @@ -111952,7 +111956,6 @@ entities: desc: Это плиткорез. ПЛИТКОРЕЗ. Придайте разнообразия полу вашей станции с помощью интересных узоров! Не засовывайте внутрь пальцы. name: плиткорез - type: Transform - rot: 3.141592653589793 rad pos: 104.5,-20.5 parent: 2 - proto: d10Dice @@ -128127,7 +128130,7 @@ entities: tags: - DrinkBottle - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.06604335 + sprayFizzinessThresholdRoll: 0.0485634 - type: SolutionContainerManager solutions: null containers: @@ -130223,13 +130226,6 @@ entities: parent: 2 - type: FaxMachine name: Офис Капитана -- proto: FigureSpawner - entities: - - uid: 19862 - components: - - type: Transform - pos: -29.5,13.5 - parent: 2 - proto: filingCabinet entities: - uid: 2452 @@ -174755,7 +174751,14 @@ entities: - type: Transform pos: 17.5,16.5 parent: 2 -- proto: GlowstickBase +- proto: GlowstickBlue + entities: + - uid: 25479 + components: + - type: Transform + pos: -47.2452,-100.72763 + parent: 2 +- proto: GlowstickGreen entities: - uid: 25475 components: @@ -174778,13 +174781,6 @@ entities: - type: Transform pos: -74.14946,-7.76694 parent: 2 -- proto: GlowstickBlue - entities: - - uid: 25479 - components: - - type: Transform - pos: -47.2452,-100.72763 - parent: 2 - proto: GlowstickPurple entities: - uid: 25480 @@ -183900,8 +183896,6 @@ entities: rot: 1.5707963267948966 rad pos: -6.3343987,19.591106 parent: 2 -- proto: HandheldHealthAnalyzerUnpowered - entities: - uid: 27105 components: - type: Transform @@ -184004,37 +183998,6 @@ entities: - type: Transform pos: 31.546932,4.9642363 parent: 2 -- proto: HeadArachnid - entities: - - uid: 27123 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -5.0829196,-42.402122 - parent: 2 -- proto: HeadMoth - entities: - - uid: 27124 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: HeadReptilian - entities: - - uid: 27125 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -64.8308,-32.5277 - parent: 2 -- proto: HeadVox - entities: - - uid: 27126 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 42.36527,-109.73952 - parent: 2 - proto: HeatExchanger entities: - uid: 27127 @@ -186598,66 +186561,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: LeftArmSkeleton - entities: - - uid: 27489 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: LeftFootMoth - entities: - - uid: 27490 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 107.40406,-71.27681 - parent: 2 -- proto: LeftFootSkeleton - entities: - - uid: 27491 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: LeftHandArachnid - entities: - - uid: 27492 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.9422946,-42.714622 - parent: 2 -- proto: LeftHandSkeleton - entities: - - uid: 27493 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: LeftLegArachnid - entities: - - uid: 27494 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.2079196,-41.839622 - parent: 2 -- proto: LeftLegReptilian - entities: - - uid: 27495 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -63.578598,-33.43395 - parent: 2 -- proto: LeftLegSkeleton - entities: - - uid: 27496 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 - proto: LegionnaireBonfire entities: - uid: 27497 @@ -190903,6 +190806,13 @@ entities: - type: Transform pos: -14.487654,-43.432667 parent: 2 +- proto: MechFigurineSpawner50 + entities: + - uid: 19862 + components: + - type: Transform + pos: -29.5,13.5 + parent: 2 - proto: MedalCase entities: - uid: 27942 @@ -192985,6 +192895,22 @@ entities: - type: Transform pos: -15.5,-36.5 parent: 2 +- proto: OrganArachnidHandLeft + entities: + - uid: 27492 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.9422946,-42.714622 + parent: 2 +- proto: OrganArachnidHead + entities: + - uid: 27123 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -5.0829196,-42.402122 + parent: 2 - proto: OrganArachnidHeart entities: - uid: 16610 @@ -193009,6 +192935,14 @@ entities: - type: Transform pos: -14.375384,-30.439037 parent: 2 +- proto: OrganArachnidLegLeft + entities: + - uid: 27494 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.2079196,-41.839622 + parent: 2 - proto: OrganArachnidTongue entities: - uid: 16611 @@ -193018,6 +192952,14 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: OrganArachnidTorso + entities: + - uid: 33991 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.0985446,-42.652122 + parent: 2 - proto: OrganDionaEyes entities: - uid: 28156 @@ -193105,6 +193047,121 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: OrganMothFootLeft + entities: + - uid: 27490 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 107.40406,-71.27681 + parent: 2 +- proto: OrganMothHead + entities: + - uid: 27124 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganReptilianHead + entities: + - uid: 27125 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -64.8308,-32.5277 + parent: 2 +- proto: OrganReptilianLegLeft + entities: + - uid: 27495 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -63.578598,-33.43395 + parent: 2 +- proto: OrganReptilianLegRight + entities: + - uid: 31197 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -64.73485,-33.68395 + parent: 2 +- proto: OrganSkeletonPersonArmLeft + entities: + - uid: 27489 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonArmRight + entities: + - uid: 31194 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonFootLeft + entities: + - uid: 27491 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonFootRight + entities: + - uid: 31195 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonHandLeft + entities: + - uid: 27493 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonHandRight + entities: + - uid: 31196 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonLegLeft + entities: + - uid: 27496 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonLegRight + entities: + - uid: 31198 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 +- proto: OrganSkeletonPersonTorso + entities: + - uid: 33992 + components: + - type: Transform + pos: 65.5,-50.5 + parent: 2 + - uid: 33993 + components: + - type: Transform + pos: 10.016842,-39.05978 + parent: 2 +- proto: OrganVoxHead + entities: + - uid: 27126 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 42.36527,-109.73952 + parent: 2 - proto: OxygenCanister entities: - uid: 28165 @@ -199228,6 +199285,43 @@ entities: - type: Transform pos: -36.339783,-86.55919 parent: 2 +- proto: PlushieSpawner50 + entities: + - uid: 34049 + components: + - type: Transform + pos: -40.5,6.5 + parent: 2 + - uid: 34050 + components: + - type: Transform + pos: -58.5,-9.5 + parent: 2 + - uid: 34051 + components: + - type: Transform + pos: 56.5,-29.5 + parent: 2 + - uid: 34052 + components: + - type: Transform + pos: -66.5,-12.5 + parent: 2 + - uid: 34053 + components: + - type: Transform + pos: -48.5,-114.5 + parent: 2 + - uid: 34054 + components: + - type: Transform + pos: -84.5,2.5 + parent: 2 + - uid: 34055 + components: + - type: Transform + pos: -51.5,-49.5 + parent: 2 - proto: PlushieXeno entities: - uid: 28494 @@ -215290,42 +215384,6 @@ entities: - type: Transform pos: -71.338974,-2.4210052 parent: 2 -- proto: RightArmSkeleton - entities: - - uid: 31194 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: RightFootSkeleton - entities: - - uid: 31195 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: RightHandSkeleton - entities: - - uid: 31196 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 -- proto: RightLegReptilian - entities: - - uid: 31197 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -64.73485,-33.68395 - parent: 2 -- proto: RightLegSkeleton - entities: - - uid: 31198 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 - proto: RiotShield entities: - uid: 31199 @@ -224405,7 +224463,7 @@ entities: - type: Transform pos: 16.554049,-31.147774 parent: 2 -- proto: SpacemenFigureSpawner +- proto: SpacemenFigurineSpawner90 entities: - uid: 32323 components: @@ -224819,13 +224877,6 @@ entities: - type: Transform pos: 36.5,17.5 parent: 2 -- proto: SpawnPointBoxer - entities: - - uid: 32382 - components: - - type: Transform - pos: -22.5,-86.5 - parent: 2 - proto: SpawnPointBrigmedic entities: - uid: 32383 @@ -225430,13 +225481,6 @@ entities: - type: Transform pos: 52.5,-22.5 parent: 2 -- proto: SpawnPointZookeeper - entities: - - uid: 32487 - components: - - type: Transform - pos: -66.5,-48.5 - parent: 2 - proto: SpeedLoaderMagnum entities: - uid: 32488 @@ -235228,26 +235272,6 @@ entities: - type: Transform pos: 109.50452,-22.239302 parent: 2 -- proto: TorsoArachnid - entities: - - uid: 33991 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.0985446,-42.652122 - parent: 2 -- proto: TorsoSkeleton - entities: - - uid: 33992 - components: - - type: Transform - pos: 65.5,-50.5 - parent: 2 - - uid: 33993 - components: - - type: Transform - pos: 10.016842,-39.05978 - parent: 2 - proto: TowelColorNT entities: - uid: 1859 @@ -235638,43 +235662,6 @@ entities: - type: Transform pos: -13.961909,-76.64306 parent: 2 -- proto: ToySpawner - entities: - - uid: 34049 - components: - - type: Transform - pos: -40.5,6.5 - parent: 2 - - uid: 34050 - components: - - type: Transform - pos: -58.5,-9.5 - parent: 2 - - uid: 34051 - components: - - type: Transform - pos: 56.5,-29.5 - parent: 2 - - uid: 34052 - components: - - type: Transform - pos: -66.5,-12.5 - parent: 2 - - uid: 34053 - components: - - type: Transform - pos: -48.5,-114.5 - parent: 2 - - uid: 34054 - components: - - type: Transform - pos: -84.5,2.5 - parent: 2 - - uid: 34055 - components: - - type: Transform - pos: -51.5,-49.5 - parent: 2 - proto: TrackingImplanter entities: - uid: 2527 diff --git a/Resources/Maps/Corvax/corvax_chloris.yml b/Resources/Maps/Corvax/corvax_chloris.yml index 57fe6cc819..cd5cc31b94 100644 --- a/Resources/Maps/Corvax/corvax_chloris.yml +++ b/Resources/Maps/Corvax/corvax_chloris.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 266.0.0 + engineVersion: 275.2.0 forkId: "" forkVersion: "" - time: 09/13/2025 05:51:48 - entityCount: 51455 + time: 04/18/2026 14:31:37 + entityCount: 51437 maps: - 1 grids: @@ -18017,144 +18017,47 @@ entities: uniqueMixes: - volume: 2500 immutable: True - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + moles: {} - volume: 2500 temperature: 293.15 moles: - - 21.824879 - - 82.10312 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 21.824879 + Nitrogen: 82.10312 - volume: 2500 temperature: 293.15 moles: - - 0 - - 103.92799 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Nitrogen: 103.92799 - volume: 2500 temperature: 235 moles: - - 27.225372 - - 102.419266 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 27.225372 + Nitrogen: 102.419266 + - volume: 2500 + temperature: 293.15 + moles: {} - volume: 2500 temperature: 293.15 moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Nitrogen: 6666.982 - volume: 2500 temperature: 293.15 moles: - - 0 - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 6666.982 - volume: 2500 temperature: 293.15 moles: - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - volume: 2500 - temperature: 293.15 - moles: - - 0 - - 0 - - 0 - - 6666.982 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Plasma: 6666.982 - volume: 2500 temperature: 184.23123 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + moles: {} chunkSize: 4 - type: NavMap - type: BecomesStation id: Chloris - type: ImplicitRoof + - type: TileHistory + chunkHistory: {} + - type: ExplosionAirtightGrid - uid: 4 components: - type: MetaData @@ -26684,7 +26587,7 @@ entities: lastSignals: DoorStatus: True - type: Door - secondsUntilStateChange: -194113.25 + secondsUntilStateChange: -194186.08 state: Opening - type: Forensics residues: [] @@ -27535,7 +27438,7 @@ entities: pos: 25.5,132.5 parent: 2 - type: Door - secondsUntilStateChange: -199113.17 + secondsUntilStateChange: -199186 state: Opening - type: DeviceLinkSource lastSignals: @@ -27718,7 +27621,7 @@ entities: pos: 99.5,50.5 parent: 2 - type: Door - secondsUntilStateChange: -116404.414 + secondsUntilStateChange: -116477.24 state: Opening - type: DeviceLinkSource lastSignals: @@ -29101,7 +29004,7 @@ entities: pos: 14.5,132.5 parent: 2 - type: Door - secondsUntilStateChange: -187194.25 + secondsUntilStateChange: -187267.08 state: Opening - type: DeviceLinkSource lastSignals: @@ -34150,124 +34053,6 @@ entities: rot: 3.141592653589793 rad pos: 88.364525,129.74033 parent: 2 -- proto: BaseChemistryEmptyVial - entities: - - uid: 18 - components: - - type: MetaData - name: пробирка семина - - type: Transform - pos: 102.60848,130.69435 - parent: 2 - - type: Tag - tags: - - CentrifugeCompatible - - type: SolutionContainerManager - solutions: null - containers: - - beaker - - type: ContainerContainer - containers: - solution@beaker: !type:ContainerSlot - ent: 19 - - uid: 20 - components: - - type: Transform - pos: 43.725544,126.74478 - parent: 2 - - type: Tag - tags: - - CentrifugeCompatible - - type: SolutionContainerManager - solutions: null - containers: - - beaker - - type: SolutionTransfer - transferAmount: 25 - - type: ContainerContainer - containers: - solution@beaker: !type:ContainerSlot - ent: 21 - - uid: 1868 - components: - - type: Transform - pos: 57.34797,91.7121 - parent: 2 - - uid: 1869 - components: - - type: Transform - pos: 61.26306,142.71603 - parent: 2 - - uid: 1870 - components: - - type: Transform - pos: 61.466034,142.80197 - parent: 2 - - uid: 1871 - components: - - type: Transform - pos: 61.122284,140.72385 - parent: 2 - - uid: 1872 - components: - - type: Transform - pos: 61.26291,140.83322 - parent: 2 - - uid: 1873 - components: - - type: Transform - pos: 57.238594,91.57147 - parent: 2 - - uid: 1874 - components: - - type: Transform - pos: 35.351513,116.66543 - parent: 2 - - uid: 1875 - components: - - type: Transform - pos: 35.476513,116.69668 - parent: 2 - - uid: 1876 - components: - - type: Transform - pos: 44.86971,126.72888 - parent: 2 - - uid: 1877 - components: - - type: Transform - pos: 44.68221,126.74451 - parent: 2 - - uid: 1878 - components: - - type: Transform - pos: 50.39661,73.63436 - parent: 2 - - uid: 1879 - components: - - type: Transform - pos: 50.70911,73.52499 - parent: 2 - - uid: 1880 - components: - - type: Transform - pos: 107.39228,130.74123 - parent: 2 - - uid: 1881 - components: - - type: Transform - pos: 102.470406,130.77248 - parent: 2 - - uid: 1882 - components: - - type: Transform - pos: 17.720512,144.66206 - parent: 2 - - uid: 1883 - components: - - type: Transform - pos: 12.276604,122.647415 - parent: 2 - proto: BaseComputer entities: - uid: 1884 @@ -36394,18 +36179,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: BodyBagFolded entities: - uid: 2191 @@ -114574,6 +114349,124 @@ entities: - type: Transform pos: 43.40096,126.54138 parent: 2 +- proto: ChemistryEmptyVial + entities: + - uid: 18 + components: + - type: MetaData + name: пробирка семина + - type: Transform + pos: 102.60848,130.69435 + parent: 2 + - type: Tag + tags: + - CentrifugeCompatible + - type: SolutionContainerManager + solutions: null + containers: + - beaker + - type: ContainerContainer + containers: + solution@beaker: !type:ContainerSlot + ent: 19 + - uid: 20 + components: + - type: Transform + pos: 43.725544,126.74478 + parent: 2 + - type: Tag + tags: + - CentrifugeCompatible + - type: SolutionContainerManager + solutions: null + containers: + - beaker + - type: SolutionTransfer + transferAmount: 25 + - type: ContainerContainer + containers: + solution@beaker: !type:ContainerSlot + ent: 21 + - uid: 1868 + components: + - type: Transform + pos: 57.34797,91.7121 + parent: 2 + - uid: 1869 + components: + - type: Transform + pos: 61.26306,142.71603 + parent: 2 + - uid: 1870 + components: + - type: Transform + pos: 61.466034,142.80197 + parent: 2 + - uid: 1871 + components: + - type: Transform + pos: 61.122284,140.72385 + parent: 2 + - uid: 1872 + components: + - type: Transform + pos: 61.26291,140.83322 + parent: 2 + - uid: 1873 + components: + - type: Transform + pos: 57.238594,91.57147 + parent: 2 + - uid: 1874 + components: + - type: Transform + pos: 35.351513,116.66543 + parent: 2 + - uid: 1875 + components: + - type: Transform + pos: 35.476513,116.69668 + parent: 2 + - uid: 1876 + components: + - type: Transform + pos: 44.86971,126.72888 + parent: 2 + - uid: 1877 + components: + - type: Transform + pos: 44.68221,126.74451 + parent: 2 + - uid: 1878 + components: + - type: Transform + pos: 50.39661,73.63436 + parent: 2 + - uid: 1879 + components: + - type: Transform + pos: 50.70911,73.52499 + parent: 2 + - uid: 1880 + components: + - type: Transform + pos: 107.39228,130.74123 + parent: 2 + - uid: 1881 + components: + - type: Transform + pos: 102.470406,130.77248 + parent: 2 + - uid: 1882 + components: + - type: Transform + pos: 17.720512,144.66206 + parent: 2 + - uid: 1883 + components: + - type: Transform + pos: 12.276604,122.647415 + parent: 2 - proto: ChemistryHotplate entities: - uid: 17338 @@ -115308,18 +115201,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -115681,18 +115564,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -116061,8 +115934,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: ClosetJanitorFilled @@ -116090,18 +115963,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 17579 components: - type: Transform @@ -116120,18 +115983,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: ClosetL3JanitorFilled entities: - uid: 17581 @@ -116238,18 +116091,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 17599 components: - type: Transform @@ -116450,18 +116293,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 17637 components: - type: Transform @@ -116478,18 +116311,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 17639 components: - type: Transform @@ -116721,18 +116544,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -116755,18 +116568,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -116811,21 +116614,9 @@ entities: volume: 200 immutable: False temperature: 293.14673 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 17688 @@ -116839,18 +116630,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -116965,18 +116746,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -117001,18 +116772,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - proto: ClosetWallEmergencyFilledRandom @@ -117124,18 +116885,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - uid: 17723 @@ -117233,18 +116984,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - uid: 17735 @@ -117269,18 +117010,8 @@ entities: immutable: False temperature: 293.1462 moles: - - 1.606311 - - 6.042789 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.606311 + Nitrogen: 6.042789 - type: ContainerContainer containers: entity_storage: !type:Container @@ -117307,18 +117038,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - uid: 17742 @@ -117332,18 +117053,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - proto: ClosetWallMixed @@ -117360,18 +117071,8 @@ entities: immutable: False temperature: 293.1465 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -117405,18 +117106,8 @@ entities: immutable: False temperature: 293.1462 moles: - - 1.606311 - - 6.042789 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.606311 + Nitrogen: 6.042789 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123121,18 +122812,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123173,8 +122854,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateEmergencyInflatablewall @@ -123380,8 +123061,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateEngineeringAMEControl @@ -123495,18 +123176,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123545,18 +123216,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 18614 components: - type: MetaData @@ -123570,18 +123231,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123610,18 +123261,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 18619 components: - type: Transform @@ -123649,8 +123290,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18620 @@ -123671,18 +123312,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateFunBoxing entities: - uid: 18622 @@ -123708,18 +123339,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateFunPlushie entities: - uid: 18625 @@ -123733,18 +123354,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 18626 components: - type: Transform @@ -123768,18 +123379,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateGenericSteel entities: - uid: 18629 @@ -123803,18 +123404,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123852,18 +123443,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -123915,8 +123496,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateHydroponicsSeeds @@ -123932,18 +123513,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateHydroponicsTools entities: - uid: 18641 @@ -123957,18 +123528,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateHydroSecure entities: - uid: 18642 @@ -124071,8 +123632,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateMedicalSecure @@ -124090,18 +123651,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateMedicalSurgery entities: - uid: 18655 @@ -124117,8 +123668,8 @@ entities: pos: 96.5,141.5 parent: 2 - type: EntityStorage - open: True removedMasks: 28 + open: True - type: Fixtures fixtures: fix1: @@ -124197,8 +123748,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CratePrivateSecure @@ -124254,21 +123805,9 @@ entities: volume: 200 immutable: False temperature: 293.14673 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateScience @@ -124305,8 +123844,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18668 @@ -124336,8 +123875,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - proto: CrateScienceSecure @@ -124360,18 +123899,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateSecure entities: - uid: 18671 @@ -124385,18 +123914,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateSecurityHelmet entities: - uid: 18672 @@ -124431,18 +123950,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrateServicePersonnel entities: - uid: 18676 @@ -124477,18 +123986,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -124535,8 +124034,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18684 @@ -124550,18 +124049,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -124588,18 +124077,8 @@ entities: immutable: False temperature: 293.1462 moles: - - 1.606311 - - 6.042789 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.606311 + Nitrogen: 6.042789 - proto: CrateTrashCart entities: - uid: 14162 @@ -124613,18 +124092,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -124639,7 +124108,6 @@ entities: - 14163 - 14164 - 14166 - - 14171 paper_label: !type:ContainerSlot showEnts: False occludes: True @@ -124675,21 +124143,9 @@ entities: volume: 200 immutable: False temperature: 293.14673 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18690 @@ -124719,8 +124175,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18691 @@ -124782,8 +124238,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18698 @@ -124828,8 +124284,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18702 @@ -124869,8 +124325,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 18705 @@ -124918,18 +124374,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -124955,18 +124401,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -125051,18 +124487,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: CrewMonitoringServer entities: - uid: 18724 @@ -125361,7 +124787,6 @@ entities: desc: Это плиткорез. ПЛИТКОРЕЗ. Он режет. Придайте разнообразия полу вашей станции с помощью интересных узоров! Не засовывайте внутрь пальцы. name: плиткорез - type: Transform - rot: 3.141592653589793 rad pos: 122.5,65.5 parent: 2 - proto: CyberPen @@ -143103,18 +142528,6 @@ entities: rot: 1.5707963267948966 rad pos: 98.5,77.5 parent: 2 -- proto: FigureSpawner - entities: - - uid: 21777 - components: - - type: Transform - pos: 90.5,81.5 - parent: 2 - - uid: 21778 - components: - - type: Transform - pos: 93.5,79.5 - parent: 2 - proto: filingCabinet entities: - uid: 21779 @@ -239063,6 +238476,8 @@ entities: pos: 105.5,160.5 parent: 2 - type: Openable + sound: !type:SoundCollectionSpecifier + collection: canOpenSounds opened: True - proto: GlassBoxLaserFilled entities: @@ -242237,18 +241652,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242275,18 +241680,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242316,18 +241711,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242358,18 +241743,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8968438 - - 7.1357465 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8968438 + Nitrogen: 7.1357465 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242414,8 +241789,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 35132 @@ -242432,18 +241807,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242467,18 +241832,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -242508,18 +241863,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: GunSafeDisabler entities: - uid: 35141 @@ -242675,8 +242020,6 @@ entities: - type: Transform pos: 130.49286,129.59427 parent: 2 -- proto: HandheldHealthAnalyzerUnpowered - entities: - uid: 35161 components: - type: Transform @@ -248681,18 +248024,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: LockerAtmosphericsFilled entities: - uid: 17839 @@ -248706,18 +248039,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248740,18 +248063,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248774,18 +248087,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248812,18 +248115,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248908,18 +248201,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -248988,18 +248271,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249029,18 +248302,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Lock locked: False - type: ContainerContainer @@ -249069,18 +248332,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: Lock locked: False - uid: 35983 @@ -249094,18 +248347,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: LockerDetectiveFilled entities: - uid: 35164 @@ -249119,18 +248362,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249237,18 +248470,8 @@ entities: immutable: False temperature: 293.1465 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249361,8 +248584,8 @@ entities: restitution: 0 friction: 0.4 - type: EntityStorage - open: True removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 36009 @@ -249378,18 +248601,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36010 components: - type: Transform @@ -249403,18 +248616,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36011 components: - type: Transform @@ -249428,18 +248631,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36012 components: - type: Transform @@ -249483,18 +248676,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249561,18 +248744,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249637,18 +248810,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249692,18 +248855,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249742,18 +248895,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249799,18 +248942,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249836,18 +248969,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249876,18 +248999,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -249955,18 +249068,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250080,18 +249183,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250186,18 +249279,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250226,18 +249309,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250267,18 +249340,8 @@ entities: immutable: False temperature: 293.14835 moles: - - 1.8977377 - - 7.139109 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8977377 + Nitrogen: 7.139109 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250355,18 +249418,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250478,18 +249531,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -250542,21 +249585,9 @@ entities: volume: 200 immutable: False temperature: 293.147 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 36084 @@ -250595,21 +249626,9 @@ entities: volume: 200 immutable: False temperature: 293.147 - moles: - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - open: True + moles: {} removedMasks: 20 + open: True - type: PlaceableSurface isPlaceable: True - uid: 36085 @@ -250686,18 +249705,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: Fixtures fixtures: {} - uid: 36094 @@ -250720,18 +249729,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -253311,6 +252310,18 @@ entities: - type: Transform pos: 109.5,164.5 parent: 2 +- proto: MechFigurineSpawner50 + entities: + - uid: 21777 + components: + - type: Transform + pos: 90.5,81.5 + parent: 2 + - uid: 21778 + components: + - type: Transform + pos: 93.5,79.5 + parent: 2 - proto: MedalCase entities: - uid: 36438 @@ -254529,18 +253540,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36547 components: - type: Transform @@ -254553,18 +253554,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 36548 components: - type: Transform @@ -256003,7 +254994,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256076,7 +255073,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256149,7 +255152,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256222,7 +255231,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256295,7 +255310,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256385,7 +255406,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256481,7 +255508,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256539,7 +255572,13 @@ entities: - type: Paper content: "[bold]Добро пожаловать на исследовательскую станцию Хлорис![/bold] \nМы рады приветствовать вас на Венере-2. Это уникальное место, где вы сможете насладиться удивительными видами и провести незабываемое время.\n\n[head=3]Краткая информация о станции[/head]\n\n- Высота: 100 километров над уровнем поверхности Венеры-2\n- Основные цели: Научные исследования, наблюдение за атмосферными явлениями, экзобиология и астрофизика.\n \n- Запланировано: Через несколько месяцев станция начнёт постепенный спуск в более плотные слои атмосферы Венеры-2 для углублённых исследований.\n\nНа такой высоте в атмосфере Венеры-2 абсолютно отсутствует давление. Однако внутри нашей станции поддерживается комфортный микроклимат.\n\n[color=#ff0000]Важно помнить:\n\n- Не покидайте пределы станции без разрешения и жестких скафандров.\n- Обязательно носите ваш кислородный баллон и маску при себе на случай аварийных ситуаций.\n[/color]\n\n[head=3]Услуги для пассажиров\n[/head]\n\n1. Залы и парки с удобными креслами и прекраснымм видом на поверхность планеты.\n2. Бар и кухня с высококлассным сервисом и отличным меню.\n3. Баня, бассейн, театр и библиотека для культурного досуга.\nУже сегодня бронируйте \n\n" - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256635,7 +255674,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256731,7 +255776,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256826,7 +255877,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -256917,7 +255974,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -256988,7 +256051,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257061,7 +256130,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257134,7 +256209,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257224,7 +256305,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257320,7 +256407,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257416,7 +256509,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257512,7 +256611,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257608,7 +256713,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257704,7 +256815,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257800,7 +256917,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -257857,6 +256980,8 @@ entities: name: пропал - type: Transform parent: 49 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -257916,7 +257041,13 @@ entities: [head=1]НАГРАДА - 5.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -258025,7 +257156,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -258114,7 +257251,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -258205,7 +257348,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258300,7 +257449,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258389,7 +257544,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258478,7 +257639,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258567,7 +257734,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258656,7 +257829,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258745,7 +257924,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258834,7 +258019,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -258923,7 +258114,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259012,7 +258209,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259101,7 +258304,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259190,7 +258399,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259279,7 +258494,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259368,7 +258589,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259457,7 +258684,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259546,7 +258779,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259635,7 +258874,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259724,7 +258969,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259819,7 +259070,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -259914,7 +259171,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -260009,7 +259272,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -260104,7 +259373,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -260199,7 +259474,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -260293,7 +259574,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260389,7 +259676,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260485,7 +259778,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260581,7 +259880,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260677,7 +259982,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260773,7 +260084,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260869,7 +260186,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -260959,7 +260282,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261049,7 +260378,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261145,7 +260480,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261241,7 +260582,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261337,7 +260684,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261433,7 +260786,13 @@ entities: 3. Баня, бассейн, театр и библиотека для культурного досуга. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -261513,7 +260872,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -261585,7 +260950,13 @@ entities: Венера-2 была объектом различных миссий, включая исследования ОПЗ в 2970-х годах и более современные спутниковые наблюдения. - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -261666,7 +261037,13 @@ entities: ↓ ↓ ↓ ↓ ↑ <<- --- --- --- --- --- --- --- << Обратка - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -261744,7 +261121,13 @@ entities: [color=#FFA500FF] ↑ ↓ ↓ ↓ ↓ ↓ ↑ <<- --- --- --- --- --- --- --- << Обратка - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -261793,6 +261176,8 @@ entities: name: пропал - type: Transform parent: 165 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -261852,7 +261237,13 @@ entities: [head=1]НАГРАДА - 5.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -261961,7 +261352,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -262063,7 +261460,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -262194,7 +261597,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -262296,7 +261705,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Physics @@ -262347,6 +261762,8 @@ entities: name: пропал - type: Transform parent: 182 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262406,7 +261823,13 @@ entities: [head=1]НАГРАДА - 5.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262516,7 +261939,13 @@ entities: [head=1]НАГРАДА - 1.000.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: Fixtures @@ -262566,6 +261995,8 @@ entities: - type: Transform pos: 33.581455,154.6076 parent: 2 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262625,7 +262056,13 @@ entities: [head=1]НАГРАДА - 5.000 $[/head] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262681,6 +262118,8 @@ entities: name: работа - type: Transform parent: 193 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262721,7 +262160,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262779,6 +262224,8 @@ entities: name: вакансия - type: Transform parent: 193 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262824,7 +262271,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262882,6 +262335,8 @@ entities: name: вакансия - type: Transform parent: 193 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -262927,7 +262382,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -262985,6 +262446,8 @@ entities: name: вакансия - type: Transform parent: 193 + - type: Flammable + nextUpdate: -168.0235617 - type: Paper content: >2- @@ -263030,7 +262493,13 @@ entities: Спасибо, что выбрали NanoTrasen. Помните: если вы не умерли — вы ещё не начали свой рабочий день![/color] - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: EmitSoundOnCollide @@ -265960,7 +265429,13 @@ entities: - type: Pill pillType: 11 - type: SolutionContainerManager - solutions: null + solutions: + food: + temperature: 293.15 + canReact: True + maxVol: 0 + name: food + reagents: [] containers: - food - type: ContainerContainer @@ -266474,7 +265949,7 @@ entities: lastSignals: DoorStatus: True - type: Door - secondsUntilStateChange: -182147.42 + secondsUntilStateChange: -182220.25 state: Opening - type: Airlock autoClose: False @@ -266757,6 +266232,53 @@ entities: - type: Transform pos: 109.43205,54.61765 parent: 2 +- proto: PlushieSpawner50 + entities: + - uid: 43855 + components: + - type: Transform + pos: 64.5,119.5 + parent: 2 + - uid: 43856 + components: + - type: Transform + pos: 92.5,42.5 + parent: 2 + - uid: 43857 + components: + - type: Transform + pos: 94.5,41.5 + parent: 2 + - uid: 43858 + components: + - type: Transform + pos: 93.5,39.5 + parent: 2 + - uid: 43859 + components: + - type: Transform + pos: 104.5,46.5 + parent: 2 + - uid: 43860 + components: + - type: Transform + pos: 102.5,46.5 + parent: 2 + - uid: 43861 + components: + - type: Transform + pos: 139.5,74.5 + parent: 2 + - uid: 43862 + components: + - type: Transform + pos: 134.5,25.5 + parent: 2 + - uid: 43863 + components: + - type: Transform + pos: 133.5,25.5 + parent: 2 - proto: PonderingOrb entities: - uid: 37024 @@ -275360,39 +274882,6 @@ entities: - type: Physics canCollide: False bodyType: Static -- proto: PrefilledSyringe - entities: - - uid: 8 - components: - - type: MetaData - name: шприц галоперидола - - type: Transform - parent: 5 - - type: SolutionContainerManager - solutions: null - containers: - - injector - - type: Physics - canCollide: False - - type: ContainerContainer - containers: - solution@injector: !type:ContainerSlot - ent: 9 - - uid: 38406 - components: - - type: Transform - pos: 22.331291,134.61938 - parent: 2 - - uid: 38407 - components: - - type: Transform - pos: 22.456291,134.52216 - parent: 2 - - uid: 38408 - components: - - type: Transform - pos: 22.636848,134.42494 - parent: 2 - proto: PrintedDocumentSentence entities: - uid: 38409 @@ -296139,7 +295628,7 @@ entities: - type: Transform pos: 158.5,98.5 parent: 2 -- proto: SpacemenFigureSpawner +- proto: SpacemenFigurineSpawner90 entities: - uid: 41531 components: @@ -296535,18 +296024,6 @@ entities: - type: Transform pos: 98.5,131.5 parent: 2 -- proto: SpawnPointBoxer - entities: - - uid: 41598 - components: - - type: Transform - pos: 100.5,90.5 - parent: 2 - - uid: 41599 - components: - - type: Transform - pos: 108.5,90.5 - parent: 2 - proto: SpawnPointBrigmedic entities: - uid: 41600 @@ -297044,13 +296521,6 @@ entities: - type: Transform pos: 138.5,116.5 parent: 2 -- proto: SpawnPointZookeeper - entities: - - uid: 41683 - components: - - type: Transform - pos: 96.5,152.5 - parent: 2 - proto: SpeedLoaderCap entities: - uid: 18687 @@ -297076,15 +296546,6 @@ entities: - type: Transform pos: 137.52753,91.59235 parent: 2 -- proto: SpeedLoaderPistolPractice - entities: - - uid: 14171 - components: - - type: Transform - parent: 14162 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: SpiderWeb entities: - uid: 41685 @@ -298944,18 +298405,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -298980,18 +298431,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -299016,18 +298457,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -299049,18 +298480,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.8856695 - - 7.0937095 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.8856695 + Nitrogen: 7.0937095 - type: ContainerContainer containers: entity_storage: !type:Container @@ -299162,18 +298583,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -302357,6 +301768,25 @@ entities: parent: 2 - proto: Syringe entities: + - uid: 8 + components: + - type: MetaData + name: шприц галоперидола + - type: Transform + parent: 5 + - type: Tag + tags: + - Syringe + - type: SolutionContainerManager + solutions: null + containers: + - injector + - type: Physics + canCollide: False + - type: ContainerContainer + containers: + solution@injector: !type:ContainerSlot + ent: 9 - uid: 2630 components: - type: Transform @@ -302364,6 +301794,21 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage + - uid: 38406 + components: + - type: Transform + pos: 22.331291,134.61938 + parent: 2 + - uid: 38407 + components: + - type: Transform + pos: 22.456291,134.52216 + parent: 2 + - uid: 38408 + components: + - type: Transform + pos: 22.636848,134.42494 + parent: 2 - uid: 42313 components: - type: Transform @@ -310677,80 +310122,6 @@ entities: - type: Transform pos: 143.49774,83.59558 parent: 2 -- proto: TowelColorBlue - entities: - - uid: 43797 - components: - - type: Transform - pos: 107.586754,89.60101 - parent: 2 - - uid: 43798 - components: - - type: Transform - pos: 107.38363,89.50726 - parent: 2 -- proto: TowelColorDarkGreen - entities: - - uid: 43799 - components: - - type: Transform - pos: 86.47167,135.47217 - parent: 2 - - uid: 43800 - components: - - type: Transform - pos: 86.4873,135.6128 - parent: 2 -- proto: TowelColorGold - entities: - - uid: 43801 - components: - - type: Transform - pos: 105.49299,102.53097 - parent: 2 -- proto: TowelColorOrange - entities: - - uid: 43802 - components: - - type: Transform - pos: 102.496445,53.633625 - parent: 2 - - uid: 43803 - components: - - type: Transform - pos: 102.669525,53.584015 - parent: 2 -- proto: TowelColorPink - entities: - - uid: 43804 - components: - - type: Transform - pos: 103.48082,53.64925 - parent: 2 - - uid: 43805 - components: - - type: Transform - pos: 103.544525,53.56839 - parent: 2 -- proto: TowelColorRed - entities: - - uid: 43806 - components: - - type: Transform - pos: 101.430504,89.52289 - parent: 2 - - uid: 43807 - components: - - type: Transform - pos: 101.586754,89.66351 - parent: 2 -- proto: TowelColorSilver - entities: - - uid: 43808 - components: - - type: Transform - pos: 103.50861,102.53097 - parent: 2 - proto: TowelColorWhite entities: - uid: 17884 @@ -311089,53 +310460,6 @@ entities: - type: Transform pos: 153.52248,107.49803 parent: 2 -- proto: ToySpawner - entities: - - uid: 43855 - components: - - type: Transform - pos: 64.5,119.5 - parent: 2 - - uid: 43856 - components: - - type: Transform - pos: 92.5,42.5 - parent: 2 - - uid: 43857 - components: - - type: Transform - pos: 94.5,41.5 - parent: 2 - - uid: 43858 - components: - - type: Transform - pos: 93.5,39.5 - parent: 2 - - uid: 43859 - components: - - type: Transform - pos: 104.5,46.5 - parent: 2 - - uid: 43860 - components: - - type: Transform - pos: 102.5,46.5 - parent: 2 - - uid: 43861 - components: - - type: Transform - pos: 139.5,74.5 - parent: 2 - - uid: 43862 - components: - - type: Transform - pos: 134.5,25.5 - parent: 2 - - uid: 43863 - components: - - type: Transform - pos: 133.5,25.5 - parent: 2 - proto: TrackingImplanter entities: - uid: 2540 @@ -348624,18 +347948,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -348660,18 +347974,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -348784,18 +348088,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -348820,18 +348114,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -348879,18 +348163,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - uid: 50863 components: - type: Transform @@ -348914,18 +348188,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - proto: WardrobeMedicalDoctorFilled entities: - uid: 50866 @@ -349087,18 +348351,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -349128,18 +348382,8 @@ entities: immutable: False temperature: 293.14673 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -349192,18 +348436,8 @@ entities: immutable: False temperature: 293.1465 moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 + Oxygen: 1.7459903 + Nitrogen: 6.568249 - type: ContainerContainer containers: entity_storage: !type:Container @@ -349326,22 +348560,6 @@ entities: parent: 2 - type: Fixtures fixtures: {} -- proto: WarpPointBombing - entities: - - uid: 50903 - components: - - type: Transform - pos: 142.5,111.5 - parent: 2 - - type: WarpPoint - location: Оружейная - - uid: 50904 - components: - - type: Transform - pos: 147.5,32.5 - parent: 2 - - type: WarpPoint - location: ТЭГ - proto: WaterCooler entities: - uid: 50905 diff --git a/Resources/Maps/Corvax/corvax_paper.yml b/Resources/Maps/Corvax/corvax_paper.yml index 2ce9ae02c8..17df6cef9c 100644 --- a/Resources/Maps/Corvax/corvax_paper.yml +++ b/Resources/Maps/Corvax/corvax_paper.yml @@ -1,11 +1,11 @@ meta: format: 7 category: Map - engineVersion: 270.1.0 - forkId: syndicate - forkVersion: 80acc36543c98a5773da7bd8c7011b5fa7e9b105 - time: 01/25/2026 10:50:07 - entityCount: 18367 + engineVersion: 275.2.0 + forkId: "" + forkVersion: "" + time: 04/18/2026 14:34:20 + entityCount: 18327 maps: - 1 grids: @@ -8888,6 +8888,8 @@ entities: - type: NavMap - type: ImplicitRoof - type: ExplosionAirtightGrid + - type: TileHistory + chunkHistory: {} - uid: 4 components: - type: MetaData @@ -49649,7 +49651,6 @@ entities: - uid: 7306 components: - type: Transform - rot: -1.5707963267948966 rad pos: 48.5,73.5 parent: 2 - proto: DefaultStationBeacon @@ -55213,7 +55214,7 @@ entities: - type: Label currentLabel: кофейный ликёр - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.55700105 + sprayFizzinessThresholdRoll: 0.68399715 - type: NameModifier baseName: бутылка кофейного ликёра - type: ContainerContainer @@ -55520,7 +55521,7 @@ entities: - type: Label currentLabel: ром - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.7229936 + sprayFizzinessThresholdRoll: 0.6197575 - type: NameModifier baseName: кубинский пряный ром капитана Пита - type: ContainerContainer @@ -55562,7 +55563,7 @@ entities: - type: Label currentLabel: текила - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.5990371 + sprayFizzinessThresholdRoll: 0.6505251 - type: NameModifier baseName: бутылка текилы Каккаво гарантированного качества - type: ContainerContainer @@ -55603,7 +55604,7 @@ entities: - type: Label currentLabel: вермут - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.08046895 + sprayFizzinessThresholdRoll: 0.0931613 - type: NameModifier baseName: бутылка вермута Золотой глаз - type: ContainerContainer @@ -55626,7 +55627,7 @@ entities: - type: Label currentLabel: водка - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.025131963 + sprayFizzinessThresholdRoll: 0.12908888 - type: NameModifier baseName: бутылка водки - type: ContainerContainer @@ -55666,7 +55667,7 @@ entities: - type: Label currentLabel: вино - type: PressurizedSolution - sprayFizzinessThresholdRoll: 0.19528572 + sprayFizzinessThresholdRoll: 0.117122024 - type: NameModifier baseName: особое двухбородое бородатое вино - type: ContainerContainer @@ -59549,7 +59550,7 @@ entities: pos: 39.5,13.5 parent: 2 - type: Door - secondsUntilStateChange: -51279.234 + secondsUntilStateChange: -51309.305 state: Closing - type: DeviceNetwork deviceLists: @@ -109995,212 +109996,6 @@ entities: - type: Transform pos: 62.5,41.5 parent: 2 -- proto: TowelColorBlack - entities: - - uid: 7028 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7052 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorBlue - entities: - - uid: 7029 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7030 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7053 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7054 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorBrown - entities: - - uid: 7031 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7055 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorDarkBlue - entities: - - uid: 7032 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7056 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorDarkGreen - entities: - - uid: 7033 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7057 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorGold - entities: - - uid: 7034 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7058 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorGray - entities: - - uid: 7035 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7059 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorGreen - entities: - - uid: 7036 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7060 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorLightBlue - entities: - - uid: 7037 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7061 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorLightBrown - entities: - - uid: 7038 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7062 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorMaroon - entities: - - uid: 7039 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7063 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorMime - entities: - - uid: 7040 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7064 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: TowelColorNT entities: - uid: 7041 @@ -110217,86 +110012,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: TowelColorOrange - entities: - - uid: 7042 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7066 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorPink - entities: - - uid: 7043 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7067 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorPurple - entities: - - uid: 7044 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7068 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorRed - entities: - - uid: 7045 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7069 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: TowelColorSilver - entities: - - uid: 7046 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7070 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: TowelColorSyndicate entities: - uid: 7071 @@ -110306,22 +110021,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: TowelColorTeal - entities: - - uid: 7047 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7072 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: TowelColorWhite entities: - uid: 7048 @@ -110338,22 +110037,6 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage -- proto: TowelColorYellow - entities: - - uid: 7049 - components: - - type: Transform - parent: 7026 - - type: Physics - canCollide: False - - type: InsideEntityStorage - - uid: 7074 - components: - - type: Transform - parent: 7050 - - type: Physics - canCollide: False - - type: InsideEntityStorage - proto: ToyFigurineEngineer entities: - uid: 15711 @@ -123068,29 +122751,9 @@ entities: showEnts: False occludes: True ents: - - 7047 - - 7039 - - 7037 - - 7045 - 7041 - - 7031 - - 7040 - 7048 - - 7033 - 7027 - - 7046 - - 7032 - - 7029 - - 7035 - - 7043 - - 7034 - - 7036 - - 7044 - - 7042 - - 7049 - - 7030 - - 7038 - - 7028 paper_label: !type:ContainerSlot showEnts: False occludes: True @@ -123114,30 +122777,10 @@ entities: showEnts: False occludes: True ents: - - 7056 - - 7057 - - 7068 - - 7062 - 7051 - - 7070 - - 7067 - - 7054 - - 7052 - - 7053 - - 7059 - 7065 - - 7069 - - 7058 - - 7061 - 7071 - - 7072 - - 7060 - - 7066 - - 7055 - - 7074 - 7073 - - 7063 - - 7064 paper_label: !type:ContainerSlot showEnts: False occludes: True From 59cb6d401905f5bddc001c1fa68dcddd86eb72f9 Mon Sep 17 00:00:00 2001 From: Dmitry <57028746+DIMMoon1@users.noreply.github.com> Date: Sat, 18 Apr 2026 22:00:34 +0700 Subject: [PATCH 207/247] Breaking the corvax outpost --- Resources/Maps/Corvax/corvax_outpost.yml | 325 ----------------------- 1 file changed, 325 deletions(-) diff --git a/Resources/Maps/Corvax/corvax_outpost.yml b/Resources/Maps/Corvax/corvax_outpost.yml index 37bdb4b2d8..bbf83a6b3b 100644 --- a/Resources/Maps/Corvax/corvax_outpost.yml +++ b/Resources/Maps/Corvax/corvax_outpost.yml @@ -53612,331 +53612,6 @@ entities: rot: 1.5707963267948966 rad pos: -48.5,18.5 parent: 2 -- proto: FigureSpawner - entities: - - uid: 7795 - components: - - type: MetaData - name: которт или хлебопес - - type: Transform - pos: -16.5,-17.5 - parent: 2 - - type: RandomSpawner - prototypes: - - MobCatCake - - MobBreadDog - - uid: 19626 - components: - - type: Transform - anchored: False - pos: 669.5,-1.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19627 - components: - - type: Transform - anchored: False - pos: -405.5,373.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19628 - components: - - type: Transform - anchored: False - pos: 957.5,120.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19629 - components: - - type: Transform - anchored: False - pos: 35.5,-950.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19630 - components: - - type: Transform - anchored: False - pos: 613.5,-583.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19631 - components: - - type: Transform - anchored: False - pos: -400.5,-142.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19632 - components: - - type: Transform - anchored: False - pos: -71.5,-181.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19633 - components: - - type: Transform - anchored: False - pos: -219.5,-161.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19634 - components: - - type: Transform - anchored: False - pos: -876.5,-13.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19635 - components: - - type: Transform - anchored: False - pos: -282.5,142.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19636 - components: - - type: Transform - anchored: False - pos: -195.5,184.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - - uid: 19637 - components: - - type: Transform - anchored: False - pos: -193.5,-217.5 - parent: 1 - - type: RandomSpawner - prototypes: - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisSmall - - AsteroidDebrisMedium - - AsteroidDebrisMedium - - AsteroidDebrisLarge - - AsteroidDebrisLarger - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageSmall - - AsteroidSalvageMedium - - AsteroidSalvageMedium - - AsteroidSalvageLarge - - AsteroidSalvageHuge - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisSmall - - ScrapDebrisMedium - proto: filingCabinetDrawerRandom entities: - uid: 7796 From 81ba668c04e68391e9970f24548504d98568b93e Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Sat, 18 Apr 2026 18:46:12 +0200 Subject: [PATCH 208/247] Add BreakOnAccessBreaker check for the Knock spell (#43641) --- Content.Shared/Magic/SharedMagicSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 4018258ae6..9bdb39ecaf 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -433,7 +433,7 @@ public abstract class SharedMagicSystem : EntitySystem if (TryComp(target, out var doorComp) && doorComp.State is not DoorState.Open) _door.StartOpening(target); - if (TryComp(target, out var lockComp) && lockComp.Locked) + if (TryComp(target, out var lockComp) && lockComp.Locked && lockComp.BreakOnAccessBreaker) _lock.Unlock(target, performer, lockComp); } } From 4443faeee0159557fe50ec7e402498a06b1c8a42 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 18 Apr 2026 17:00:07 +0000 Subject: [PATCH 209/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fd7d037379..da8badcd9c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4034,3 +4027,11 @@ 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 From 8eff480c2960c80fb448ea876deee21aa43bad93 Mon Sep 17 00:00:00 2001 From: Gem Date: Sat, 18 Apr 2026 11:21:55 -0700 Subject: [PATCH 210/247] Try gather markings data method fix (#43637) Added logMissing:false to the beginning of the TryGatherMarkingsData method to silence log errors for missing components. --- Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs b/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs index 6273b0640b..a7f5163110 100644 --- a/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs +++ b/Content.Shared/Body/SharedVisualBodySystem.Modifiers.cs @@ -82,7 +82,7 @@ public abstract partial class SharedVisualBodySystem [NotNullWhen(true)] out Dictionary, OrganMarkingData>? markings, [NotNullWhen(true)] out Dictionary, Dictionary>>? applied) { - if (!Resolve(ent, ref ent.Comp)) + if (!Resolve(ent, ref ent.Comp, logMissing: false)) { profiles = null; markings = null; From 3bff0087387ec34038a63d326c506105f4230f68 Mon Sep 17 00:00:00 2001 From: Jessica M Date: Sat, 18 Apr 2026 14:45:17 -0700 Subject: [PATCH 211/247] Monkeys can now put people into disposal bins (#37983) * ugh * Pathfinidng to bins w/target.. figuring out inserting * yea i dont know why this shit dont work * finally got this shit working lmao * cleanup stuff * cleanup more * do radius better * removing unnecessary tags * refactor to be more reusable --------- Co-authored-by: Jessica M --- .../Specific/DisposalInsertOperator.cs | 55 ++++++++ .../Specific/PickEntityNearMobOperator.cs | 131 ++++++++++++++++++ .../Prototypes/Entities/Mobs/NPCs/animals.yml | 5 +- Resources/Prototypes/NPCs/monkey.yml | 61 ++++++++ 4 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/DisposalInsertOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickEntityNearMobOperator.cs create mode 100644 Resources/Prototypes/NPCs/monkey.yml diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/DisposalInsertOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/DisposalInsertOperator.cs new file mode 100644 index 0000000000..6693c3c63c --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/DisposalInsertOperator.cs @@ -0,0 +1,55 @@ +using Content.Server.Disposal.Unit; +using Content.Shared.Disposal.Components; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific; + +public sealed partial class DisposalInsertOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + private DisposalUnitSystem _disposalSystem = default!; + + /// + /// Target entity to flush + /// + [DataField(required: true)] + public string TargetKey = string.Empty; + + /// + /// Target disposal bin entity + /// + [DataField(required: true)] + public string DisposalTargetKey = string.Empty; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _disposalSystem = sysManager.GetEntitySystem(); + } + + public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status) + { + base.TaskShutdown(blackboard, status); + blackboard.Remove(TargetKey); + blackboard.Remove(DisposalTargetKey); + } + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(TargetKey, out var target, _entManager) || _entManager.Deleted(target)) + return HTNOperatorStatus.Failed; + + if (!blackboard.TryGetValue(DisposalTargetKey, out var disposalUnitTarget, _entManager) || _entManager.Deleted(target)) + return HTNOperatorStatus.Failed; + + if (!_entManager.TryGetComponent(disposalUnitTarget, out var disposalComp)) + return HTNOperatorStatus.Failed; + + if (!_disposalSystem.TryInsert(disposalUnitTarget, target, owner, disposalComp)) + return HTNOperatorStatus.Failed; + + return HTNOperatorStatus.Finished; + + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickEntityNearMobOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickEntityNearMobOperator.cs new file mode 100644 index 0000000000..d07a9c2917 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Specific/PickEntityNearMobOperator.cs @@ -0,0 +1,131 @@ +using System.Threading; +using System.Threading.Tasks; +using Content.Server.NPC.Pathfinding; +using Content.Shared.Interaction; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Whitelist; +using Robust.Server.Containers; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific; + +/// +/// Queries for nearby entities matching a whitelist/blacklist, and then searches for a mob near to that entity. +/// +public sealed partial class PickEntityNearMobOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + private EntityLookupSystem _lookup = default!; + private PathfindingSystem _pathfinding = default!; + private ContainerSystem _container = default!; + private EntityWhitelistSystem _entityWhitelist = default!; + + /// + /// Range to search for entities + /// + [DataField(required: true)] + public string RangeKey = default!; + + /// + /// Target mob that was found near NearbyEntityTargetKey + /// + [DataField(required: true)] + public string TargetKey = string.Empty; + + /// + /// Target entity that was found + /// + [DataField(required: true)] + public string NearbyEntityTargetKey = string.Empty; + + /// + /// Target entitycoordinates to move to. + /// + [DataField(required: true)] + public string TargetMoveKey = string.Empty; + + /// + /// Whitelist for what entities will get picked + /// + [DataField] + public EntityWhitelist? Whitelist; + + /// + /// Blacklist for what entities will NOT get picked + /// + [DataField] + public EntityWhitelist? Blacklist; + + /// + /// Range to search for mobs near the target entity + /// + [DataField(required: true)] + public string MobRangeKey = default!; + + /// + /// MobState to check for + /// + [DataField] + public MobState? MobState; + + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _lookup = sysManager.GetEntitySystem(); + _pathfinding = sysManager.GetEntitySystem(); + _container = sysManager.GetEntitySystem(); + _entityWhitelist = sysManager.GetEntitySystem(); + } + + public override async Task<(bool Valid, Dictionary? Effects)> Plan(NPCBlackboard blackboard, + CancellationToken cancelToken) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(RangeKey, out var range, _entManager)) + return (false, null); + + if (!blackboard.TryGetValue(RangeKey, out var mobRange, _entManager)) + return (false, null); + + var mobState = _entManager.GetEntityQuery(); + + foreach (var entity in _lookup.GetEntitiesInRange(owner, range)) + { + if (!_entityWhitelist.CheckBoth(entity, Blacklist, Whitelist)) + continue; + + //checking if there is anyone NEAR the entity we found + foreach (var mob in _lookup.GetEntitiesInRange(entity, mobRange)) + { + if (mob == owner) + continue; + + if (_container.IsEntityInContainer(mob)) + continue; + + if (mobState.TryGetComponent(mob, out var state)) + { + if (MobState != null && state.CurrentState != MobState) + continue; + + var pathRange = SharedInteractionSystem.InteractionRange; + var path = await _pathfinding.GetPath(owner, mob, pathRange, cancelToken); + + if (path.Result == PathResult.NoPath) + return (false, null); + + return (true, new Dictionary() + { + {TargetKey, mob}, + {NearbyEntityTargetKey, entity}, + {TargetMoveKey, _entManager.GetComponent(mob).Coordinates}, + {NPCBlackboard.PathfindKey, path}, + }); + } + } + } + + return (false, null); + } +} diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index aaf94e0d2d..c30a1f4d63 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -1439,7 +1439,10 @@ - Passive - type: HTN rootTask: - task: SimpleHostileCompound + task: MonkeyCompound + blackboard: + NavInteract: !type:Bool + true - type: IdExaminable - type: Tag tags: diff --git a/Resources/Prototypes/NPCs/monkey.yml b/Resources/Prototypes/NPCs/monkey.yml new file mode 100644 index 0000000000..3e321a2d38 --- /dev/null +++ b/Resources/Prototypes/NPCs/monkey.yml @@ -0,0 +1,61 @@ +- type: htnCompound + id: MonkeyCompound + branches: + - tasks: + - !type:HTNCompoundTask + task: DisposalsNearbyCompound + - tasks: + - !type:HTNCompoundTask + task: SimpleHostileCompound + +- type: htnCompound + id: DisposalsNearbyCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:PickEntityNearMobOperator + rangeKey: VisionRadius + targetKey: FlushableTarget + nearbyEntityTargetKey: DisposalBinTarget + targetMoveKey: TargetCoordinates + mobState: Critical + mobRangeKey: InteractRange + whitelist: + components: + - DisposalUnit + blacklist: + components: + - MailingUnit + + - !type:HTNPrimitiveTask + operator: !type:MoveToOperator + pathfindInPlanning: false + + - !type:HTNPrimitiveTask + operator: !type:SetFloatOperator + targetKey: IdleTime + amount: 1 + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime + + - !type:HTNPrimitiveTask + operator: !type:DisposalInsertOperator + targetKey: FlushableTarget + disposalTargetKey: DisposalBinTarget + + - !type:HTNPrimitiveTask + operator: !type:SetFloatOperator + targetKey: IdleTime + amount: 1 + + - !type:HTNPrimitiveTask + operator: !type:WaitOperator + key: IdleTime + preconditions: + - !type:KeyExistsPrecondition + key: IdleTime From 0c18d867c934bc75cb8d8afdbed774b1f5ea87a8 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 18 Apr 2026 22:01:24 +0000 Subject: [PATCH 212/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index da8badcd9c..d62a407ae0 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4035,3 +4028,10 @@ 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 From 592e9bbfd920d028765514dab4ab55817a3f678a Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:05:18 -0700 Subject: [PATCH 213/247] IHeatContainer and HeatContainerHelper methods as generic (#42800) * IHeatContainer and methods as generic * fixes * fix sandbox violation * how did I miss this * whos gonna tell em * multi-generic, also fix a nasty bug in EquilibriumTemperatureQuery * weh * reduce temp object creation --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../HeatContainer/HeatContainer.cs | 30 ++---- .../HeatContainerHelpers.Conduct.cs | 62 ++++++------ .../HeatContainerHelpers.Divide.cs | 64 ++++++++----- .../HeatContainerHelpers.Exchange.cs | 94 +++++++++++-------- .../HeatContainerHelpers.Merge.cs | 54 +++++------ .../HeatContainer/HeatContainerHelpers.cs | 20 ++-- .../HeatContainer/IHeatContainer.cs | 35 +++++++ 7 files changed, 204 insertions(+), 155 deletions(-) create mode 100644 Content.Shared/Temperature/HeatContainer/IHeatContainer.cs diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainer.cs b/Content.Shared/Temperature/HeatContainer/HeatContainer.cs index c88d11b15a..cfa5a43c59 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainer.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainer.cs @@ -6,40 +6,24 @@ namespace Content.Shared.Temperature.HeatContainer; /// /// A general-purpose container for heat energy. -/// Any object that contains, stores, or transfers heat should use a -/// instead of implementing its own system. -/// This allows for consistent heat transfer mechanics across different objects and systems. /// [Serializable, NetSerializable, DataDefinition] [Access(typeof(HeatContainerHelpers), typeof(SharedAtmosphereSystem))] -public partial struct HeatContainer : IRobustCloneable +public partial struct HeatContainer : IRobustCloneable, IHeatContainer { - /// - /// The heat capacity of this container in Joules per Kelvin. - /// This determines how much energy is required to change the temperature of the container. - /// Higher values mean the container can absorb or release more heat energy - /// without a significant change in temperature. - /// + /// [DataField] - public float HeatCapacity = 4000f; // about 1kg of water + public float HeatCapacity { get; set; } = 4000f; // about 1kg of water - /// - /// The current temperature of the container in Kelvin. - /// + /// [DataField] - public float Temperature = Atmospherics.T20C; // room temperature + public float Temperature { get; set; } = Atmospherics.T20C; // room temperature - /// - /// The current temperature of the container in Celsius. - /// Ideal if you just need to read the temperature for UI. - /// Do not perform computations in Celsius/set this value, use Kelvin instead. - /// + /// [ViewVariables] public float TemperatureC => TemperatureHelpers.KelvinToCelsius(Temperature); - /// - /// The current thermal energy of the container in Joules. - /// + /// [ViewVariables] public float InternalEnergy => Temperature * HeatCapacity; diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs index c0de657fa9..8551f5688c 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,19 @@ public static partial class HeatContainerHelpers /// integration steps with adaptive step size. /// [PublicAPI] - public static float ConductHeat(this ref HeatContainer cA, ref HeatContainer cB, float deltaTime, float g) + public static float ConductHeat(ref T cA, ref T cB, float deltaTime, float g) where T : 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 +85,7 @@ public static partial class HeatContainerHelpers /// integration steps with adaptive step size. /// [PublicAPI] - public static float ConductHeatQuery(this ref HeatContainer c, float temp, float deltaTime, float g) + public static float ConductHeatQuery(ref T c, float temp, float deltaTime, float g) where T : IHeatContainer { var dQ = g * (temp - c.Temperature) * deltaTime; var dQMax = Math.Abs(ConductHeatToTempQuery(ref c, temp)); @@ -95,11 +95,11 @@ public static partial class HeatContainerHelpers } /// - /// Calculates the amount of heat that would be conducted between two s, + /// Calculates the amount of heat that would be conducted between two s, /// given some conductivity constant k and a time delta. Does not modify the containers. /// - /// The first to conduct heat to. - /// The second to conduct heat to. + /// The first that should conduct heat. + /// The second that should conduct heat. /// /// The amount of time that the heat is allowed to conduct, in seconds. /// This value should be small such that deltaTime << C / g where C is the heat capacity of the container. @@ -116,22 +116,28 @@ public static partial class HeatContainerHelpers /// integration steps with adaptive step size. /// [PublicAPI] - public static float ConductHeatQuery(this ref HeatContainer c1, ref HeatContainer c2, float deltaTime, float g) + public static float ConductHeatQuery(ref T1 c1, ref T2 c2, float deltaTime, float g) + where T1 : IHeatContainer + where T2 : IHeatContainer { - return ConductHeatQuery(ref c1, c2.Temperature, deltaTime, g); + var dQ = g * (c2.Temperature - c1.Temperature) * deltaTime; + var dQMax = Math.Abs(EquilibriumHeatQuery(ref c1, ref c2)); + + // Clamp the transferred heat amount in case we are overshooting the equilibrium temperature because our time step was too large. + return Math.Clamp(dQ, -dQMax, dQMax); } /// - /// Changes the temperature of a to a target temperature by + /// Changes the temperature of a to a target temperature by /// adding or removing the necessary amount of heat. /// - /// The to change the temperature of. + /// The to change the temperature of. /// The desired temperature to reach. - /// The amount of heat in joules that was transferred to or from the + /// The amount of heat in joules that was transferred to or from the /// to reach the target temperature. /// A positive value indicates heat must be added to the container to reach the target temperature. [PublicAPI] - public static float ConductHeatToTemp(this ref HeatContainer c, float targetTemp) + public static float ConductHeatToTemp(ref T c, float targetTemp) where T : IHeatContainer { var dQ = ConductHeatToTempQuery(ref c, targetTemp); c.Temperature = targetTemp; @@ -139,16 +145,16 @@ public static partial class HeatContainerHelpers } /// - /// Determines the amount of heat that must be transferred to or from a + /// Determines the amount of heat that must be transferred to or from a /// to reach a target temperature. Does not modify the heat container. /// - /// The to query. + /// The to query. /// The desired temperature to reach. - /// The amount of heat in joules that must be transferred to or from the + /// The amount of heat in joules that must be transferred to or from the /// to reach the target temperature. /// A positive value indicates heat must be added to the container to reach the target temperature. [PublicAPI] - public static float ConductHeatToTempQuery(this ref HeatContainer c, float targetTemp) + public static float ConductHeatToTempQuery(ref T c, float targetTemp) where T : IHeatContainer { return (targetTemp - c.Temperature) * c.HeatCapacity; } diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs index e745c59305..0e516446da 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs @@ -5,50 +5,66 @@ namespace Content.Shared.Temperature.HeatContainer; public static partial class HeatContainerHelpers { /// - /// Splits a into two. + /// Splits a into two. /// - /// The to split. This will be modified to contain the remaining heat capacity. + /// The to split. This will be modified to contain the remaining heat capacity. + /// A that will be modified to contain + /// the specified fraction of the original container's heat capacity and the same temperature. /// The fraction of the heat capacity to move to the new container. Clamped between 0 and 1. - /// A new containing the specified fraction of the original container's heat capacity and the same temperature. [PublicAPI] - public static HeatContainer Split(this ref HeatContainer c, float fraction = 0.5f) + public static void Split(ref T1 c, ref T2 cSplit, float fraction = 0.5f) + where T1 : IHeatContainer + where T2 : IHeatContainer { fraction = Math.Clamp(fraction, 0f, 1f); var newHeatCapacity = c.HeatCapacity * fraction; - var newContainer = new HeatContainer - { - HeatCapacity = newHeatCapacity, - Temperature = c.Temperature, - }; + cSplit.HeatCapacity = newHeatCapacity; + cSplit.Temperature = c.Temperature; c.HeatCapacity -= newHeatCapacity; - - return newContainer; } /// - /// Divides a source into a specified number of equal parts. + /// Splits a into two, + /// modifying the original container to contain the specified fraction of the original heat capacity and the same temperature. /// - /// The input to split. - /// The number of s - /// to split the source into. - /// Thrown when attempting to divide the source container by zero. - /// An array of s equally split from the source . + /// A that will be modified to contain + /// the specified fraction of the original container's heat capacity and the same temperature. + /// The fraction of the heat capacity to move to the new container. Clamped between 0 and 1. + /// This discards the leftover fraction. Be very careful with using this as you may void heat unintentionally. [PublicAPI] - public static HeatContainer[] Divide(this HeatContainer c, uint num) + public static void Split(ref T c, float fraction = 0.5f) + where T : IHeatContainer { - ArgumentOutOfRangeException.ThrowIfZero(num); + fraction = Math.Clamp(fraction, 0f, 1f); + var newHeatCapacity = c.HeatCapacity * fraction; + c.HeatCapacity = newHeatCapacity; + } + + /// + /// Divides a source into a specified number of equal parts. + /// + /// The input to split. + /// An array of s equally split from the source . + /// This will be written to. This must be the same length as num. + /// The number of s + /// to split the source into. + /// Thrown when attempting to divide the source container by zero. + /// Thrown when the length of the divided array does not match the specified number of divisions. + [PublicAPI] + public static void Divide(this T c, T[] dividedArray, int num) + where T : struct, IHeatContainer // if we allowed classes you'd just have an array reffing the same obj + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(num); + ArgumentOutOfRangeException.ThrowIfNotEqual(dividedArray.Length, num); var fraction = 1f / num; - var cFrac = c.Split(fraction); - var containers = new HeatContainer[num]; + Split(ref c, fraction); for (var i = 0; i < num; i++) { - containers[i] = cFrac; + dividedArray[i] = c; } - - return containers; } } diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs index 75611d449a..b12dd026ef 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs @@ -11,14 +11,16 @@ public static partial class HeatContainerHelpers /// to bring them into thermal equilibrium. /// Does not modify the containers. /// - /// The first to exchange heat. - /// The second to exchange heat with. - /// The amount of heat in joules that is needed + /// The first to exchange heat. + /// The second to exchange heat with. + /// The amount of transferred heat in joules that is needed /// to bring the containers to thermal equilibrium. /// A positive value indicates heat transfer from a hot cA to a cold cB. /// Thrown when the combined heat capacity of both containers is zero or negative. [PublicAPI] - public static float EquilibriumHeatQuery(this ref HeatContainer cA, ref HeatContainer cB) + public static float EquilibriumHeatQuery(ref T1 cA, ref T2 cB) + where T1 : IHeatContainer + where T2 : IHeatContainer { var cTotal = cA.HeatCapacity + cB.HeatCapacity; ArgumentOutOfRangeException.ThrowIfNegativeOrZero(cTotal); @@ -43,26 +45,30 @@ public static partial class HeatContainerHelpers /// Determines the resulting temperature if two heat containers are brought into thermal equilibrium. /// Does not modify the containers. /// - /// The first to exchange heat. - /// The second to exchange heat with. + /// The first to exchange heat. + /// The second to exchange heat with. /// The resulting equilibrium temperature both containers will be at. /// Thrown when the combined heat capacity of both containers is zero or negative. [PublicAPI] - public static float EquilibriumTemperatureQuery(this ref HeatContainer cA, ref HeatContainer cB) + public static float EquilibriumTemperatureQuery(ref T1 cA, ref T2 cB) + where T1 : IHeatContainer + where T2 : IHeatContainer { var cTotal = cA.HeatCapacity + cB.HeatCapacity; ArgumentOutOfRangeException.ThrowIfNegativeOrZero(cTotal); // Insert the above solution for Q into T_A_final = T_A_initial - Q / C_A and rearrange the result. - return (cA.HeatCapacity * cA.Temperature - cB.HeatCapacity * cB.Temperature) / cTotal; + return (cA.HeatCapacity * cA.Temperature + cB.HeatCapacity * cB.Temperature) / cTotal; } /// - /// Brings two s into thermal equilibrium by exchanging heat. + /// Brings two s into thermal equilibrium by exchanging heat. /// - /// The first to exchange heat. - /// The second to exchange heat with. + /// The first to exchange heat. + /// The second to exchange heat with. [PublicAPI] - public static void Equilibrate(this ref HeatContainer cA, ref HeatContainer cB) + public static void Equilibrate(ref T1 cA, ref T2 cB) + where T1 : IHeatContainer + where T2 : IHeatContainer { var tFinal = EquilibriumTemperatureQuery(ref cA, ref cB); cA.Temperature = tFinal; @@ -70,13 +76,15 @@ public static partial class HeatContainerHelpers } /// - /// Brings two s into thermal equilibrium by exchanging heat. + /// Brings two s into thermal equilibrium by exchanging heat. /// - /// The first to exchange heat. - /// The second to exchange heat with. + /// The first to exchange heat. + /// The second to exchange heat with. /// The amount of heat in joules that was transferred from container A to B. [PublicAPI] - public static void Equilibrate(this ref HeatContainer cA, ref HeatContainer cB, out float dQ) + public static void Equilibrate(ref T1 cA, ref T2 cB, out float dQ) + where T1 : IHeatContainer + where T2 : IHeatContainer { var tInitialA = cA.Temperature; var tFinal = EquilibriumTemperatureQuery(ref cA, ref cB); @@ -91,13 +99,13 @@ public static partial class HeatContainerHelpers #region N-Body Exchange /// - /// Brings an array of s into thermal equilibrium by exchanging heat. + /// Brings an array of s into thermal equilibrium by exchanging heat. /// - /// The array of s to bring into thermal equilibrium. + /// The array of s to bring into thermal equilibrium. [PublicAPI] - public static void Equilibrate(this HeatContainer[] cN) + public static void Equilibrate(this T[] cN) where T : IHeatContainer { - var tF = cN.EquilibriumTemperatureQuery(); + var tF = EquilibriumTemperatureQuery(cN); for (var i = 0; i < cN.Length; i++) { cN[i].Temperature = tF; @@ -105,15 +113,17 @@ public static partial class HeatContainerHelpers } /// - /// Brings a into thermal equilibrium - /// with an array of other s by exchanging heat. + /// Brings a into thermal equilibrium + /// with an array of other s by exchanging heat. /// - /// The first to bring into thermal equilibrium. - /// The array of s to bring into thermal equilibrium. + /// The first to bring into thermal equilibrium. + /// The array of s to bring into thermal equilibrium. [PublicAPI] - public static void Equilibrate(this ref HeatContainer cA, HeatContainer[] cN) + public static void Equilibrate(ref T1 cA, T2[] cN) + where T1 : IHeatContainer + where T2 : IHeatContainer { - var tF = cA.EquilibriumTemperatureQuery(cN); + var tF = EquilibriumTemperatureQuery(ref cA, cN); cA.Temperature = tF; for (var i = 0; i < cN.Length; i++) @@ -123,14 +133,14 @@ public static partial class HeatContainerHelpers } /// - /// Determines the final temperature of an array of s + /// Determines the final temperature of an array of s /// when they are brought into thermal equilibrium. Does not modify the containers. /// - /// The array of s to bring into thermal equilibrium. - /// The temperature of all s involved after reaching thermal equilibrium. + /// The array of s to bring into thermal equilibrium. + /// The temperature of all s involved after reaching thermal equilibrium. /// Thrown when the combined heat capacity of all containers is zero or negative. [PublicAPI] - public static float EquilibriumTemperatureQuery(this HeatContainer[] cN) + public static float EquilibriumTemperatureQuery(this T[] cN) where T : IHeatContainer { /* The solution is derived via the following: @@ -176,15 +186,15 @@ public static partial class HeatContainerHelpers } /// - /// Determines the final temperature of an array of s + /// Determines the final temperature of an array of s /// when they are brought into thermal equilibrium. Does not modify the containers. /// - /// The array of s to bring into thermal equilibrium. + /// The array of s to bring into thermal equilibrium. /// The amount of heat in joules that was added to each container /// to reach thermal equilibrium. - /// The temperature of all s involved after reaching thermal equilibrium. + /// The temperature of all s involved after reaching thermal equilibrium. [PublicAPI] - public static float EquilibriumTemperatureQuery(this HeatContainer[] cN, out float[] dQ) + public static float EquilibriumTemperatureQuery(this T[] cN, out float[] dQ) where T : IHeatContainer { /* For finding the total heat exchanged during the equalization between a group of bodies @@ -205,16 +215,18 @@ public static partial class HeatContainerHelpers } /// - /// Determines the final temperature of a when it is brought into thermal equilibrium - /// with an array of other s. Does not modify the containers. + /// Determines the final temperature of a when it is brought into thermal equilibrium + /// with an array of other s. Does not modify the containers. /// - /// The first to bring into thermal equilibrium. - /// The array of s to bring into thermal equilibrium. - /// The temperature of all s involved after reaching thermal equilibrium. + /// The first to bring into thermal equilibrium. + /// The array of s to bring into thermal equilibrium. + /// The temperature of all s involved after reaching thermal equilibrium. [PublicAPI] - public static float EquilibriumTemperatureQuery(this ref HeatContainer cA, HeatContainer[] cN) + public static float EquilibriumTemperatureQuery(ref T1 cA, T2[] cN) + where T1 : IHeatContainer + where T2 : IHeatContainer { - var cAll = new HeatContainer[cN.Length + 1]; + var cAll = new T1[cN.Length + 1]; cAll[0] = cA; cN.CopyTo(cAll, 1); diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs index 5aa3dcbbde..d3cb2e258a 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs @@ -7,48 +7,49 @@ public static partial class HeatContainerHelpers /// /// Merges two heat containers into one, conserving total internal energy. /// - /// The first to merge. This will be modified to contain the merged result. - /// The second to merge. + /// The first to merge. This will be modified to contain the merged result. + /// The second to merge. /// Thrown when the combined heat capacity of both containers is zero or negative. [PublicAPI] - public static void Merge(this ref HeatContainer cA, HeatContainer cB) + public static void Merge(ref T1 cA, ref T2 cB) + where T1 : IHeatContainer + where T2 : IHeatContainer { var combinedHeatCapacity = cA.HeatCapacity + cB.HeatCapacity; ArgumentOutOfRangeException.ThrowIfNegativeOrZero(combinedHeatCapacity); - var merged = new HeatContainer - { - HeatCapacity = combinedHeatCapacity, - Temperature = (cA.InternalEnergy + cB.InternalEnergy) / combinedHeatCapacity, - }; - cA = merged; + cA.HeatCapacity = combinedHeatCapacity; + cA.Temperature = (cA.InternalEnergy + cB.InternalEnergy) / combinedHeatCapacity; } /// - /// Merges an array of s into a single heat container, conserving total internal energy. + /// Merges an array of s into a single heat container, conserving total internal energy. /// - /// The first to merge. + /// The first to merge. /// This will be modified to contain the merged result. - /// The array of s to merge. + /// The array of s to merge. [PublicAPI] - public static void Merge(this ref HeatContainer cA, HeatContainer[] cN) + public static void Merge(ref T1 cA, T2[] cN) + where T1 : IHeatContainer + where T2 : IHeatContainer { - var cAcN = new HeatContainer[cN.Length + 1]; - cAcN[0] = cA; - cN.CopyTo(cAcN, 1); - - cA = cAcN.Merge(); + // merge the first array and then merge the result with cA to avoid alloc + var temp = new HeatContainer(); + cN.Merge(ref temp); + Merge(ref cA, ref temp); } /// - /// Merges an array of s into a single heat container, conserving total internal energy. + /// Merges an array of s into a single heat container, conserving total internal energy. /// - /// The array of s to merge. - /// A new representing the merged result. + /// The array of s to merge. + /// The modified containing the merged result. /// Thrown when the combined heat capacity of all containers is zero or negative. [PublicAPI] - public static HeatContainer Merge(this HeatContainer[] cN) + public static void Merge(this T1[] cN, ref T2 result) + where T1 : IHeatContainer + where T2 : IHeatContainer { var totalHeatCapacity = 0f; var totalEnergy = 0f; @@ -61,12 +62,7 @@ public static partial class HeatContainerHelpers ArgumentOutOfRangeException.ThrowIfNegativeOrZero(totalHeatCapacity); - var result = new HeatContainer - { - HeatCapacity = totalHeatCapacity, - Temperature = totalEnergy / totalHeatCapacity, - }; - - return result; + result.HeatCapacity = totalHeatCapacity; + result.Temperature = totalEnergy / totalHeatCapacity; } } diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs index ed1eadc74a..42b7ae7ca8 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs @@ -3,22 +3,22 @@ using JetBrains.Annotations; namespace Content.Shared.Temperature.HeatContainer; /// -/// Class containing helper methods for working with s. +/// Class containing helper methods for working with s. /// Use these classes instead of implementing your own heat transfer logic. /// public static partial class HeatContainerHelpers { /// - /// Adds or removes heat energy from the container. + /// Adds or removes heat energy from the . /// Positive values add heat, negative values remove heat. /// The temperature can never become lower than 0K even if more heat is removed. /// - /// The to add or remove energy. + /// The to add or remove energy. /// The energy in joules to add or remove. [PublicAPI] - public static void AddHeat(this ref HeatContainer c, float dQ) + public static void AddHeat(ref T c, float dQ) where T : IHeatContainer { - c.Temperature = c.AddHeatQuery(dQ); + c.Temperature = AddHeatQuery(ref c, dQ); } /// @@ -26,12 +26,12 @@ public static partial class HeatContainerHelpers /// Positive values add heat, negative values remove heat. This method doesn't change the container's state. /// The temperature can never become lower than 0K even if more heat is removed. /// - /// The to query. + /// The to query. /// The energy in joules to add or remove. /// The resulting temperature in kelvin after the heat change. /// Thrown when the heat capacity of the container is zero or negative. [PublicAPI] - public static float AddHeatQuery(this ref HeatContainer c, float dQ) + public static float AddHeatQuery(ref T c, float dQ) where T : IHeatContainer { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(c.HeatCapacity); // Don't allow the temperature to go below the absolute minimum. @@ -39,14 +39,14 @@ public static partial class HeatContainerHelpers } /// - /// Sets the heat capacity of a without altering its thermal energy. + /// Sets the heat capacity of a without altering its thermal energy. /// Adjusts the temperature accordingly to maintain the same internal energy. /// - /// The to modify. + /// The to modify. /// The new heat capacity to set. /// Thrown when the new heat capacity is zero or negative. [PublicAPI] - public static void SetHeatCapacity(this ref HeatContainer c, float newHeatCapacity) + public static void SetHeatCapacity(ref T c, float newHeatCapacity) where T : IHeatContainer { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(c.HeatCapacity); var currentEnergy = c.InternalEnergy; diff --git a/Content.Shared/Temperature/HeatContainer/IHeatContainer.cs b/Content.Shared/Temperature/HeatContainer/IHeatContainer.cs new file mode 100644 index 0000000000..95cf9c1728 --- /dev/null +++ b/Content.Shared/Temperature/HeatContainer/IHeatContainer.cs @@ -0,0 +1,35 @@ +namespace Content.Shared.Temperature.HeatContainer; + +/// +/// Interface that defines a general-purpose container for heat energy. +/// Any object that contains, stores, or transfers heat should use a +/// or inherit instead of implementing its own system. +/// This allows for consistent heat transfer mechanics across different objects and systems. +/// +public interface IHeatContainer +{ + /// + /// The heat capacity of this container in Joules per Kelvin. + /// This determines how much energy is required to change the temperature of the container. + /// Higher values mean the container can absorb or release more heat energy + /// without a significant change in temperature. + /// + float HeatCapacity { get; set; } + + /// + /// The current temperature of the container in Kelvin. + /// + float Temperature { get; set; } + + /// + /// The current temperature of the container in Celsius. + /// Ideal if you just need to read the temperature for UI. + /// + float TemperatureC => TemperatureHelpers.KelvinToCelsius(Temperature); + + /// + /// The current thermal energy of the container in Joules. + /// + float InternalEnergy => Temperature * HeatCapacity; +} + From adac43d4f04abab425937e5646fa78eaa08f0d66 Mon Sep 17 00:00:00 2001 From: Jessica M Date: Sat, 18 Apr 2026 15:18:50 -0700 Subject: [PATCH 214/247] Move the rest of AirlockSystem to shared and also Entity (#43614) * move airlocksystem to shared and Entity * forgot * nullable --- Content.Server/Doors/Systems/AirlockSystem.cs | 63 +--------- .../Doors/Components/AirlockComponent.cs | 3 + .../Doors/Systems/SharedAirlockSystem.cs | 110 +++++++++++++----- 3 files changed, 83 insertions(+), 93 deletions(-) diff --git a/Content.Server/Doors/Systems/AirlockSystem.cs b/Content.Server/Doors/Systems/AirlockSystem.cs index 1bf19a4ece..40ecdc21be 100644 --- a/Content.Server/Doors/Systems/AirlockSystem.cs +++ b/Content.Server/Doors/Systems/AirlockSystem.cs @@ -1,66 +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 -{ - 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 (component.KeepOpenIfClicked && component.AutoClose) - { - // Disable auto close - component.AutoClose = false; - Dirty(uid, component); - } - } -} +public sealed class AirlockSystem : SharedAirlockSystem; diff --git a/Content.Shared/Doors/Components/AirlockComponent.cs b/Content.Shared/Doors/Components/AirlockComponent.cs index f8357dd954..136f49b4fc 100644 --- a/Content.Shared/Doors/Components/AirlockComponent.cs +++ b/Content.Shared/Doors/Components/AirlockComponent.cs @@ -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 /// diff --git a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs index 5bbde04aed..23c8c463e6 100644 --- a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs +++ b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs @@ -1,6 +1,9 @@ +using Content.Shared.DeviceLinking.Events; using Content.Shared.Doors.Components; +using Content.Shared.Interaction; using Robust.Shared.Audio.Systems; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Prying.Components; using Content.Shared.Wires; using Robust.Shared.Timing; @@ -27,66 +30,69 @@ public abstract class SharedAirlockSystem : EntitySystem SubscribeLocalEvent(OnBeforeDoorDenied); SubscribeLocalEvent(OnGetPryMod); SubscribeLocalEvent(OnBeforePry); + SubscribeLocalEvent(OnSignalReceived); + SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnActivate, before: new[] { typeof(SharedDoorSystem) }); } - private void OnBeforeDoorClosed(EntityUid uid, AirlockComponent airlock, BeforeDoorClosedEvent args) + private void OnBeforeDoorClosed(Entity ent, ref BeforeDoorClosedEvent args) { if (args.Cancelled) return; - if (!airlock.Safety) + if (!ent.Comp.Safety) args.PerformCollisionCheck = false; // only block based on bolts / power status when initially closing the door, not when its already // mid-transition. Particularly relevant for when the door was pried-closed with a crowbar, which bypasses // the initial power-check. - if (TryComp(uid, out DoorComponent? door) + if (HasComp(ent) && !args.Partial - && !CanChangeState(uid, airlock)) + && !CanChangeState(ent)) { args.Cancel(); } } - private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args) + private void OnStateChanged(Entity ent, ref DoorStateChangedEvent args) { // This is here so we don't accidentally bulldoze state values and mispredict. if (_timing.ApplyingState) return; // Only show the maintenance panel if the airlock is closed - if (TryComp(uid, out var wiresPanel)) + if (TryComp(ent, out var wiresPanel)) { - _wiresSystem.ChangePanelVisibility(uid, wiresPanel, component.OpenPanelVisible || args.State != DoorState.Open); + _wiresSystem.ChangePanelVisibility(ent, wiresPanel, ent.Comp.OpenPanelVisible || args.State != DoorState.Open); } // If the door is closed, we should look if the bolt was locked while closing - UpdateAutoClose(uid, component); + UpdateAutoClose((ent, ent.Comp)); // Make sure the airlock auto closes again next time it is opened if (args.State == DoorState.Closed) { - component.AutoClose = true; - Dirty(uid, component); + ent.Comp.AutoClose = true; + Dirty(ent); } } - private void OnBoltsChanged(EntityUid uid, AirlockComponent component, DoorBoltsChangedEvent args) + private void OnBoltsChanged(Entity ent, ref DoorBoltsChangedEvent args) { // If unbolted, reset the auto close timer if (!args.BoltsDown) - UpdateAutoClose(uid, component); + UpdateAutoClose((ent, ent.Comp)); } - private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args) + private void OnBeforeDoorOpened(Entity ent, ref BeforeDoorOpenedEvent args) { - if (!CanChangeState(uid, component)) + if (!CanChangeState(ent)) args.Cancel(); } - private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args) + private void OnBeforeDoorDenied(Entity ent, ref BeforeDoorDeniedEvent args) { - if (!CanChangeState(uid, component)) + if (!CanChangeState(ent)) args.Cancel(); } @@ -102,44 +108,86 @@ public abstract class SharedAirlockSystem : EntitySystem /// /// Updates the auto close timer. /// - public void UpdateAutoClose(EntityUid uid, AirlockComponent? airlock = null, DoorComponent? door = null) + public void UpdateAutoClose(Entity ent) { - if (!Resolve(uid, ref airlock, ref door)) + if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2)) return; - if (door.State != DoorState.Open) + if (ent.Comp2.State != DoorState.Open) return; - if (!airlock.AutoClose) + if (!ent.Comp1.AutoClose) return; - if (!CanChangeState(uid, airlock)) + if (!CanChangeState((ent.Owner, ent.Comp1))) return; var autoev = new BeforeDoorAutoCloseEvent(); - RaiseLocalEvent(uid, autoev); + RaiseLocalEvent(ent, autoev); if (autoev.Cancelled) return; - DoorSystem.SetNextStateChange(uid, airlock.AutoCloseDelay * airlock.AutoCloseDelayModifier); + DoorSystem.SetNextStateChange(ent, ent.Comp1.AutoCloseDelay * ent.Comp1.AutoCloseDelayModifier); } - private void OnBeforePry(EntityUid uid, AirlockComponent component, ref BeforePryEvent args) + private void OnBeforePry(Entity ent, ref BeforePryEvent args) { if (args.Cancelled) return; - if (!component.Powered || args.PryPowered) + if (!ent.Comp.Powered || args.PryPowered) return; - args.Message = "airlock-component-cannot-pry-is-powered-message"; + args.Message = ent.Comp.PryFailedPopup; args.Cancelled = true; } - public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component) + private void OnSignalReceived(Entity ent, ref SignalReceivedEvent args) { - Appearance.SetData(uid, DoorVisuals.EmergencyLights, component.EmergencyAccess); + if (args.Port == ent.Comp.AutoClosePort && ent.Comp.AutoClose) + { + ent.Comp.AutoClose = false; + Dirty(ent); + } + } + + private void OnPowerChanged(Entity ent, ref PowerChangedEvent args) + { + ent.Comp.Powered = args.Powered; + Dirty(ent); + + if (!TryComp(ent, out DoorComponent? door)) + return; + + if (!args.Powered) + { + // stop any scheduled auto-closing + if (door.State == DoorState.Open) + DoorSystem.SetNextStateChange(ent, null); + } + else + { + UpdateAutoClose((ent, ent.Comp, door)); + } + } + + private void OnActivate(Entity ent, ref ActivateInWorldEvent args) + { + if (args.Handled || !args.Complex) + return; + + if (ent.Comp.KeepOpenIfClicked && ent.Comp.AutoClose) + { + // Disable auto close + ent.Comp.AutoClose = false; + Dirty(ent); + } + } + + public void UpdateEmergencyLightStatus(Entity ent) + { + Appearance.SetData(ent, DoorVisuals.EmergencyLights, ent.Comp.EmergencyAccess); } public void SetEmergencyAccess(Entity ent, bool value, EntityUid? user = null, bool predicted = false) @@ -152,7 +200,7 @@ public abstract class SharedAirlockSystem : EntitySystem ent.Comp.EmergencyAccess = value; Dirty(ent, ent.Comp); // This only runs on the server apparently so we need this. - UpdateEmergencyLightStatus(ent, ent.Comp); + UpdateEmergencyLightStatus(ent); var sound = ent.Comp.EmergencyAccess ? ent.Comp.EmergencyOnSound : ent.Comp.EmergencyOffSound; if (predicted) @@ -174,8 +222,8 @@ public abstract class SharedAirlockSystem : EntitySystem component.Safety = value; } - public bool CanChangeState(EntityUid uid, AirlockComponent component) + public bool CanChangeState(Entity ent) { - return component.Powered && !DoorSystem.IsBolted(uid); + return ent.Comp.Powered && !DoorSystem.IsBolted(ent); } } From 77dd6871834906ae5c601c36e8d8f8658f9ab8bc Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sat, 18 Apr 2026 22:22:42 -0700 Subject: [PATCH 215/247] The Grand Max Cap Rewrite (#43281) * Working commit * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA * fix the tests I broke * Remove methods that the client shouldn't be calling. Hamburger. * light cleanup for now * review * whoop * Commit this shit * More stages. Something something, hamburger. * last of the sprites. Now it's just lights, shaking, and sounds * move comments * bomb * fuck it I'm done * whoops! * fix test fails * review * actually this is better:tm: * forgot about you! * remove unused space * make it actually build smile * borger * grah!!!! * review time * Adj powerfactor --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> --- .../Components/MaxPressureVisualsComponent.cs | 36 ++ .../EntitySystems/AtmosphereSystem.Gases.cs | 14 + .../Atmos/EntitySystems/GasTankSystem.cs | 6 + .../EntitySystems/MaxPressureVisualsSystem.cs | 77 +++ .../Piping/Unary/Systems/GasCanisterSystem.cs | 7 + .../Atmos/UI/GasCanisterBoundUserInterface.cs | 2 +- .../GasTank/GasTankBoundUserInterface.cs | 2 +- .../EntitySystems/AtmosphereSystem.API.cs | 1 + .../EntitySystems/AtmosphereSystem.Gases.cs | 219 +------- .../Atmos/EntitySystems/GasTankSystem.cs | 327 +++++------- .../Unary/EntitySystems/GasCanisterSystem.cs | 87 ++-- .../Body/Systems/InternalsSystem.cs | 10 +- Content.Shared/Atmos/Atmospherics.cs | 17 +- .../GasMaxPressureHolderComponent.cs | 80 +++ .../Atmos/Components/GasTankComponent.cs | 68 +-- .../Atmos/Components/IGasMaxPressureHolder.cs | 44 ++ .../EntitySystems/GasMaxPressureSystem.cs | 144 ++++++ .../SharedAtmosphereSystem.API.cs | 15 + .../SharedAtmosphereSystem.Gases.cs | 466 +++++++++++++++++- .../EntitySystems/SharedGasTankSystem.cs | 50 +- .../Unary/Components/GasCanisterComponent.cs | 33 +- .../Unary/Systems/SharedGasCanisterSystem.cs | 32 +- Content.Shared/CCVar/CCVars.Atmos.cs | 2 +- .../Destructible/SharedDestructibleSystem.cs | 2 +- Resources/Locale/en-US/gases/gas-tank.ftl | 1 + .../Catalog/Fills/Items/gas_tanks.yml | 106 +++- .../Entities/Clothing/Shoes/magboots.yml | 2 +- .../Entities/Objects/Tools/gas_tanks.yml | 27 +- .../Entities/Objects/Tools/jetpacks.yml | 24 +- .../Structures/Power/Generation/teg.yml | 2 +- .../Storage/Canisters/gas_canisters.yml | 214 +++++++- .../black.rsi/integrity-unshaded-0.png | Bin 0 -> 5114 bytes .../black.rsi/integrity-unshaded-1.png | Bin 0 -> 5237 bytes .../black.rsi/integrity-unshaded-2.png | Bin 0 -> 6270 bytes .../black.rsi/integrity-unshaded-3.png | Bin 0 -> 6413 bytes .../black.rsi/integrity-unshaded-4.png | Bin 0 -> 6099 bytes .../Objects/Tanks/Jetpacks/black.rsi/mask.png | Bin 0 -> 5471 bytes .../Tanks/Jetpacks/black.rsi/meta.json | 20 +- .../blue.rsi/integrity-unshaded-0.png | Bin 0 -> 5114 bytes .../blue.rsi/integrity-unshaded-1.png | Bin 0 -> 5237 bytes .../blue.rsi/integrity-unshaded-2.png | Bin 0 -> 6270 bytes .../blue.rsi/integrity-unshaded-3.png | Bin 0 -> 6413 bytes .../blue.rsi/integrity-unshaded-4.png | Bin 0 -> 6088 bytes .../Objects/Tanks/Jetpacks/blue.rsi/mask.png | Bin 0 -> 6114 bytes .../Objects/Tanks/Jetpacks/blue.rsi/meta.json | 20 +- .../captain.rsi/integrity-unshaded-0.png | Bin 0 -> 5114 bytes .../captain.rsi/integrity-unshaded-1.png | Bin 0 -> 5237 bytes .../captain.rsi/integrity-unshaded-2.png | Bin 0 -> 6270 bytes .../captain.rsi/integrity-unshaded-3.png | Bin 0 -> 6413 bytes .../captain.rsi/integrity-unshaded-4.png | Bin 0 -> 6088 bytes .../Tanks/Jetpacks/captain.rsi/mask.png | Bin 0 -> 6110 bytes .../Tanks/Jetpacks/captain.rsi/meta.json | 20 +- .../mini.rsi/integrity-unshaded-0.png | Bin 0 -> 5370 bytes .../mini.rsi/integrity-unshaded-1.png | Bin 0 -> 5578 bytes .../mini.rsi/integrity-unshaded-2.png | Bin 0 -> 6444 bytes .../mini.rsi/integrity-unshaded-3.png | Bin 0 -> 6749 bytes .../mini.rsi/integrity-unshaded-4.png | Bin 0 -> 7191 bytes .../Objects/Tanks/Jetpacks/mini.rsi/mask.png | Bin 0 -> 5742 bytes .../Objects/Tanks/Jetpacks/mini.rsi/meta.json | 20 +- .../security.rsi/integrity-unshaded-0.png | Bin 0 -> 5114 bytes .../security.rsi/integrity-unshaded-1.png | Bin 0 -> 5237 bytes .../security.rsi/integrity-unshaded-2.png | Bin 0 -> 6270 bytes .../security.rsi/integrity-unshaded-3.png | Bin 0 -> 6413 bytes .../security.rsi/integrity-unshaded-4.png | Bin 0 -> 6088 bytes .../Tanks/Jetpacks/security.rsi/mask.png | Bin 0 -> 5903 bytes .../Tanks/Jetpacks/security.rsi/meta.json | 20 +- .../void.rsi/integrity-unshaded-0.png | Bin 0 -> 4928 bytes .../void.rsi/integrity-unshaded-1.png | Bin 0 -> 5118 bytes .../void.rsi/integrity-unshaded-2.png | Bin 0 -> 5263 bytes .../void.rsi/integrity-unshaded-3.png | Bin 0 -> 5473 bytes .../void.rsi/integrity-unshaded-4.png | Bin 0 -> 5462 bytes .../Objects/Tanks/Jetpacks/void.rsi/mask.png | Bin 0 -> 5555 bytes .../Objects/Tanks/Jetpacks/void.rsi/meta.json | 20 +- .../anesthetic.rsi/integrity-unshaded-0.png | Bin 0 -> 4893 bytes .../anesthetic.rsi/integrity-unshaded-1.png | Bin 0 -> 4984 bytes .../anesthetic.rsi/integrity-unshaded-2.png | Bin 0 -> 5606 bytes .../anesthetic.rsi/integrity-unshaded-3.png | Bin 0 -> 6002 bytes .../anesthetic.rsi/integrity-unshaded-4.png | Bin 0 -> 6066 bytes .../Objects/Tanks/anesthetic.rsi/mask.png | Bin 0 -> 6063 bytes .../Objects/Tanks/anesthetic.rsi/meta.json | 162 +++--- .../emergency.rsi/integrity-unshaded-0.png | Bin 0 -> 4974 bytes .../emergency.rsi/integrity-unshaded-1.png | Bin 0 -> 5113 bytes .../emergency.rsi/integrity-unshaded-2.png | Bin 0 -> 5318 bytes .../emergency.rsi/integrity-unshaded-3.png | Bin 0 -> 5705 bytes .../emergency.rsi/integrity-unshaded-4.png | Bin 0 -> 5826 bytes .../Objects/Tanks/emergency.rsi/mask.png | Bin 0 -> 5815 bytes .../Objects/Tanks/emergency.rsi/meta.json | 40 +- .../integrity-unshaded-0.png | Bin 0 -> 4976 bytes .../integrity-unshaded-1.png | Bin 0 -> 5061 bytes .../integrity-unshaded-2.png | Bin 0 -> 5304 bytes .../integrity-unshaded-3.png | Bin 0 -> 5691 bytes .../integrity-unshaded-4.png | Bin 0 -> 5905 bytes .../Tanks/emergency_clown.rsi/mask.png | Bin 0 -> 6411 bytes .../Tanks/emergency_clown.rsi/meta.json | 20 +- .../integrity-unshaded-0.png | Bin 0 -> 5000 bytes .../integrity-unshaded-1.png | Bin 0 -> 5169 bytes .../integrity-unshaded-2.png | Bin 0 -> 5484 bytes .../integrity-unshaded-3.png | Bin 0 -> 5891 bytes .../integrity-unshaded-4.png | Bin 0 -> 6175 bytes .../Tanks/emergency_double.rsi/mask.png | Bin 0 -> 6359 bytes .../Tanks/emergency_double.rsi/meta.json | 20 +- .../integrity-unshaded-0.png | Bin 0 -> 5000 bytes .../integrity-unshaded-1.png | Bin 0 -> 5169 bytes .../integrity-unshaded-2.png | Bin 0 -> 5484 bytes .../integrity-unshaded-3.png | Bin 0 -> 5891 bytes .../integrity-unshaded-4.png | Bin 0 -> 6175 bytes .../Tanks/emergency_double_red.rsi/mask.png | Bin 0 -> 6301 bytes .../Tanks/emergency_double_red.rsi/meta.json | 20 +- .../integrity-unshaded-0.png | Bin 0 -> 4976 bytes .../integrity-unshaded-1.png | Bin 0 -> 5061 bytes .../integrity-unshaded-2.png | Bin 0 -> 5304 bytes .../integrity-unshaded-3.png | Bin 0 -> 5691 bytes .../integrity-unshaded-4.png | Bin 0 -> 5905 bytes .../Tanks/emergency_extended.rsi/mask.png | Bin 0 -> 5903 bytes .../Tanks/emergency_extended.rsi/meta.json | 20 +- .../integrity-unshaded-0.png | Bin 0 -> 4976 bytes .../integrity-unshaded-1.png | Bin 0 -> 5061 bytes .../integrity-unshaded-2.png | Bin 0 -> 5304 bytes .../integrity-unshaded-3.png | Bin 0 -> 5691 bytes .../integrity-unshaded-4.png | Bin 0 -> 5905 bytes .../Tanks/emergency_extended_red.rsi/mask.png | Bin 0 -> 5840 bytes .../emergency_extended_red.rsi/meta.json | 20 +- .../integrity-unshaded-0.png | Bin 0 -> 4974 bytes .../integrity-unshaded-1.png | Bin 0 -> 5113 bytes .../integrity-unshaded-2.png | Bin 0 -> 5318 bytes .../integrity-unshaded-3.png | Bin 0 -> 5705 bytes .../integrity-unshaded-4.png | Bin 0 -> 5826 bytes .../Objects/Tanks/emergency_red.rsi/mask.png | Bin 0 -> 5766 bytes .../Objects/Tanks/emergency_red.rsi/meta.json | 40 +- .../integrity-unshaded-0.png | Bin 0 -> 4974 bytes .../integrity-unshaded-1.png | Bin 0 -> 5113 bytes .../integrity-unshaded-2.png | Bin 0 -> 5318 bytes .../integrity-unshaded-3.png | Bin 0 -> 5705 bytes .../integrity-unshaded-4.png | Bin 0 -> 5826 bytes .../Tanks/emergency_yellow.rsi/mask.png | Bin 0 -> 5806 bytes .../Tanks/emergency_yellow.rsi/meta.json | 20 +- .../generic.rsi/integrity-unshaded-0.png | Bin 0 -> 4893 bytes .../generic.rsi/integrity-unshaded-1.png | Bin 0 -> 4984 bytes .../generic.rsi/integrity-unshaded-2.png | Bin 0 -> 5606 bytes .../generic.rsi/integrity-unshaded-3.png | Bin 0 -> 6002 bytes .../generic.rsi/integrity-unshaded-4.png | Bin 0 -> 6066 bytes .../Objects/Tanks/generic.rsi/mask.png | Bin 0 -> 5859 bytes .../Objects/Tanks/generic.rsi/meta.json | 162 +++--- .../Tanks/oxygen.rsi/integrity-unshaded-0.png | Bin 0 -> 4893 bytes .../Tanks/oxygen.rsi/integrity-unshaded-1.png | Bin 0 -> 4984 bytes .../Tanks/oxygen.rsi/integrity-unshaded-2.png | Bin 0 -> 5606 bytes .../Tanks/oxygen.rsi/integrity-unshaded-3.png | Bin 0 -> 6002 bytes .../Tanks/oxygen.rsi/integrity-unshaded-4.png | Bin 0 -> 6066 bytes .../Objects/Tanks/oxygen.rsi/mask.png | Bin 0 -> 6351 bytes .../Objects/Tanks/oxygen.rsi/meta.json | 162 +++--- .../Tanks/plasma.rsi/integrity-unshaded-0.png | Bin 0 -> 5310 bytes .../Tanks/plasma.rsi/integrity-unshaded-1.png | Bin 0 -> 5539 bytes .../Tanks/plasma.rsi/integrity-unshaded-2.png | Bin 0 -> 5813 bytes .../Tanks/plasma.rsi/integrity-unshaded-3.png | Bin 0 -> 6089 bytes .../Tanks/plasma.rsi/integrity-unshaded-4.png | Bin 0 -> 6297 bytes .../Objects/Tanks/plasma.rsi/mask.png | Bin 0 -> 6292 bytes .../Objects/Tanks/plasma.rsi/meta.json | 72 ++- .../Tanks/red.rsi/integrity-unshaded-0.png | Bin 0 -> 4893 bytes .../Tanks/red.rsi/integrity-unshaded-1.png | Bin 0 -> 4984 bytes .../Tanks/red.rsi/integrity-unshaded-2.png | Bin 0 -> 5606 bytes .../Tanks/red.rsi/integrity-unshaded-3.png | Bin 0 -> 6002 bytes .../Tanks/red.rsi/integrity-unshaded-4.png | Bin 0 -> 6066 bytes .../Textures/Objects/Tanks/red.rsi/mask.png | Bin 0 -> 6299 bytes .../Textures/Objects/Tanks/red.rsi/meta.json | 162 +++--- .../canister.rsi/integrity-unshaded-0.png | Bin 0 -> 5142 bytes .../canister.rsi/integrity-unshaded-1.png | Bin 0 -> 5289 bytes .../canister.rsi/integrity-unshaded-2.png | Bin 0 -> 5630 bytes .../canister.rsi/integrity-unshaded-3.png | Bin 0 -> 6042 bytes .../canister.rsi/integrity-unshaded-4.png | Bin 0 -> 6147 bytes .../Storage/canister.rsi/mask-black.png | Bin 0 -> 5792 bytes .../Storage/canister.rsi/mask-blue.png | Bin 0 -> 6943 bytes .../Storage/canister.rsi/mask-darkblue.png | Bin 0 -> 6849 bytes .../Storage/canister.rsi/mask-frezon.png | Bin 0 -> 7186 bytes .../Storage/canister.rsi/mask-green.png | Bin 0 -> 7691 bytes .../Storage/canister.rsi/mask-greenys.png | Bin 0 -> 7956 bytes .../Storage/canister.rsi/mask-grey.png | Bin 0 -> 5981 bytes .../Storage/canister.rsi/mask-orange.png | Bin 0 -> 7218 bytes .../Storage/canister.rsi/mask-red.png | Bin 0 -> 7137 bytes .../Storage/canister.rsi/mask-redws.png | Bin 0 -> 7195 bytes .../Storage/canister.rsi/mask-water_vapor.png | Bin 0 -> 6443 bytes .../Storage/canister.rsi/mask-yellow.png | Bin 0 -> 7527 bytes .../Structures/Storage/canister.rsi/meta.json | 51 ++ 182 files changed, 2237 insertions(+), 971 deletions(-) create mode 100644 Content.Client/Atmos/Components/MaxPressureVisualsComponent.cs create mode 100644 Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs create mode 100644 Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs create mode 100644 Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs create mode 100644 Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs create mode 100644 Resources/Locale/en-US/gases/gas-tank.ftl create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/anesthetic.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/emergency.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_clown.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_double_red.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_extended_red.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_red.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_red.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/emergency_yellow.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/generic.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/oxygen.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/plasma.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/plasma.rsi/mask.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Objects/Tanks/red.rsi/mask.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-0.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-1.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-2.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-3.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-4.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-black.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-blue.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-darkblue.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-frezon.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-green.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-greenys.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-grey.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-orange.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-red.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-redws.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-water_vapor.png create mode 100644 Resources/Textures/Structures/Storage/canister.rsi/mask-yellow.png diff --git a/Content.Client/Atmos/Components/MaxPressureVisualsComponent.cs b/Content.Client/Atmos/Components/MaxPressureVisualsComponent.cs new file mode 100644 index 0000000000..e71f212786 --- /dev/null +++ b/Content.Client/Atmos/Components/MaxPressureVisualsComponent.cs @@ -0,0 +1,36 @@ +using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; + +namespace Content.Client.Atmos.Components; + +/// +/// This listens to appearance changes from +/// and applies sprite changes to a gas holder currently experiencing loss. +/// +[RegisterComponent] +public sealed partial class MaxPressureVisualsComponent : Component +{ + /// + /// What RsiState we use for our integrity visuals. + /// + [DataField] + public string? IntegrityState = "integrity"; + + /// + /// What RsiState we use for the mask that goes over integrity visuals. + /// + [DataField] + public string? IntegrityMask = "mask"; + + /// + /// How many steps there are + /// + [DataField("steps")] + public int IntegritySteps = 5; +} + +public enum MaxPressureVisualLayers : byte +{ + Base, + BaseUnshaded, +} diff --git a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index de544fba99..24f3ffaa26 100644 --- a/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Client/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -37,6 +37,20 @@ public sealed partial class AtmosphereSystem return NumericsHelpers.HorizontalAdd(tmp) > epsilon; } + public override float GetMass(GasMixture mix) + { + return GetMass(mix.Moles); + } + + public override float GetMass(float[] moles) + { + var tmp = new float[moles.Length]; + NumericsHelpers.Multiply(moles, GasMolarMasses, tmp); + + // Conversion of grams to kilograms. + return NumericsHelpers.HorizontalAdd(tmp) * Atmospherics.gToKg; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override float GetHeatCapacityCalculation(float[] moles, bool space) { diff --git a/Content.Client/Atmos/EntitySystems/GasTankSystem.cs b/Content.Client/Atmos/EntitySystems/GasTankSystem.cs index 696e7939f6..bae70ea2bd 100644 --- a/Content.Client/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Client/Atmos/EntitySystems/GasTankSystem.cs @@ -11,6 +11,12 @@ public sealed class GasTankSystem : SharedGasTankSystem SubscribeLocalEvent(OnGasTankState); } + protected override void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + // Atmos not predicted :( + throw new NotImplementedException(); + } + private void OnGasTankState(Entity ent, ref AfterAutoHandleStateEvent args) { if (UI.TryGetOpenUi(ent.Owner, SharedGasTankUiKey.Key, out var bui)) diff --git a/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs b/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs new file mode 100644 index 0000000000..d92a7b6741 --- /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 int integrity) + return; + + if (!args.AppearanceData.TryGetValue(GasIntegrity.MaxIntegrity, out obj) || obj is not int 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/Piping/Unary/Systems/GasCanisterSystem.cs b/Content.Client/Atmos/Piping/Unary/Systems/GasCanisterSystem.cs index cae184edbb..13513e2a95 100644 --- a/Content.Client/Atmos/Piping/Unary/Systems/GasCanisterSystem.cs +++ b/Content.Client/Atmos/Piping/Unary/Systems/GasCanisterSystem.cs @@ -1,4 +1,5 @@ using Content.Client.Atmos.UI; +using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Systems; @@ -14,6 +15,12 @@ public sealed class GasCanisterSystem : SharedGasCanisterSystem SubscribeLocalEvent(OnGasState); } + protected override void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + // Atmos not predicted :( + throw new NotImplementedException(); + } + private void OnGasState(Entity ent, ref AfterAutoHandleStateEvent args) { if (UI.TryGetOpenUi(ent.Owner, GasCanisterUiKey.Key, out var bui)) diff --git a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs index 0456426b1f..56be898a01 100644 --- a/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs +++ b/Content.Client/Atmos/UI/GasCanisterBoundUserInterface.cs @@ -74,7 +74,7 @@ namespace Content.Client.Atmos.UI _window.SetTankPressure(cast.TankPressure); _window.SetReleasePressureRange(component.MinReleasePressure, component.MaxReleasePressure); _window.SetReleasePressure(component.ReleasePressure); - _window.SetReleaseValve(component.ReleaseValve); + _window.SetReleaseValve(component.ReleaseValveOpen); } protected override void Dispose(bool disposing) diff --git a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs index 19e578fda3..52951b8eb6 100644 --- a/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs +++ b/Content.Client/UserInterface/Systems/Atmos/GasTank/GasTankBoundUserInterface.cs @@ -46,7 +46,7 @@ namespace Content.Client.UserInterface.Systems.Atmos.GasTank if (EntMan.TryGetComponent(Owner, out GasTankComponent? component)) { var canConnect = EntMan.System().CanConnectToInternals((Owner, component)); - _window?.Update(canConnect, component.IsConnected, component.OutputPressure); + _window?.Update(canConnect, component.IsConnected, component.ReleasePressure); } if (state is GasTankBoundUserInterfaceState cast) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index 8c3681d543..35782060a7 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -7,6 +7,7 @@ using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.Reactions; using JetBrains.Annotations; using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Atmos.EntitySystems; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 1fae93ef94..db89d53fea 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -28,6 +28,20 @@ namespace Content.Server.Atmos.EntitySystems Array.Sort(_gasReactions, (a, b) => b.Priority.CompareTo(a.Priority)); } + public override float GetMass(GasMixture mix) + { + return GetMass(mix.Moles); + } + + public override float GetMass(float[] moles) + { + Span tmp = stackalloc float[moles.Length]; + NumericsHelpers.Multiply(moles, GasMolarMasses, tmp); + + // Conversion of grams to kilograms. + return NumericsHelpers.HorizontalAdd(tmp) * Atmospherics.gToKg; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected override float GetHeatCapacityCalculation(float[] moles, bool space) { @@ -196,211 +210,6 @@ namespace Content.Server.Atmos.EntitySystems Merge(destination, buffer); } - /// - /// Calculates the dimensionless fraction of gas required to equalize pressure between two gas mixtures. - /// - /// The first gas mixture involved in the pressure equalization. - /// This mixture should be the one you always expect to be the highest pressure. - /// The second gas mixture involved in the pressure equalization. - /// A float (from 0 to 1) representing the dimensionless fraction of gas that needs to be transferred from the - /// mixture of higher pressure to the mixture of lower pressure. - /// - /// - /// This properly takes into account the effect - /// of gas merging from inlet to outlet affecting the temperature - /// (and possibly increasing the pressure) in the outlet. - /// - /// - /// The gas is assumed to expand freely, - /// so the temperature of the gas with the greater pressure is not changing. - /// - /// - /// - /// If you want to calculate the moles required to equalize pressure between an inlet and an outlet, - /// multiply the fraction returned by the source moles. - /// - public float FractionToEqualizePressure(GasMixture gasMixture1, GasMixture gasMixture2) - { - /* - Problem: the gas being merged from the inlet to the outlet could affect the - temp. of the gas and cause a pressure rise. - We want the pressure to be equalized, so we have to account for this. - - For clarity, let's assume that gasMixture1 is the inlet and gasMixture2 is the outlet. - - We require mechanical equilibrium, so \( P_1' = P_2' \) - - Before the transfer, we have: - \( P_1 = \frac{n_1 R T_1}{V_1} \) - \( P_2 = \frac{n_2 R T_2}{V_2} \) - - After removing fraction \( x \) moles from the inlet, we have: - \( P_1' = \frac{(1 - x) n_1 R T_1}{V_1} \) - - The outlet will gain the same \( x n_1 \) moles of gas. - So \( n_2' = n_2 + x n_1 \) - - After mixing, the outlet temperature will be changed. - Denote the new mixture temperature as \( T_2' \). - Volume is constant. - So we have: - \( P_2' = \frac{(n_2 + x n_1) R T_2}{V_2} \) - - The total energy of the incoming inlet to outlet gas at \( T_1 \) plus the existing energy of the outlet gas at \( T_2 \) - will be equal to the energy of the new outlet gas at \( T_2' \). - This leads to the following derivation: - \( x n_1 C_1 T_1 + n_2 C_2 T_2 = (x n_1 C_1 + n_2 C_2) T_2' \) - - Where \( C_1 \) and \( C_2 \) are the heat capacities of the inlet and outlet gases, respectively. - - Solving for \( T_2' \) gives us: - \( T_2' = \frac{x n_1 C_1 T_1 + n_2 C_2 T_2}{x n_1 C_1 + n_2 C_2} \) - - Once again, we require mechanical equilibrium (\( P_1' = P_2' \)), - so we can substitute \( T_2' \) into the pressure equation: - - \( \frac{(1 - x) n_1 R T_1}{V_1} = - \frac{(n_2 + x n_1) R}{V_2} \cdot - \frac{x n_1 C_1 T_1 + n_2 C_2 T_2} - {x n_1 C_1 + n_2 C_2} \) - - Now it's a matter of solving for \( x \). - Not going to show the full derivation here, just steps. - 1. Cancel common factor \( R \). - 2. Multiply both sides by \( x n_1 C_1 + n_2 C_2 \), so that everything - becomes a polynomial in terms of \( x \). - 3. Expand both sides. - 4. Collect like powers of \( x \). - 5. After collecting, you should end up with a polynomial of the form: - - \( (-n_1 C_1 T_1 (1 + \frac{V_2}{V_1})) x^2 + - (n_1 T_1 \frac{V_2}{V_1} (C_1 - C_2) - n_2 C_1 T_1 - n_1 C_2 T_2) x + - (n_1 T_1 \frac{V_2}{V_1} C_2 - n_2 C_2 T_2) = 0 \) - - Divide through by \( n_1 C_1 T_1 \) and replace each ratio with a symbol for clarity: - \( k_V = \frac{V_2}{V_1} \) - \( k_n = \frac{n_2}{n_1} \) - \( k_T = \frac{T_2}{T_1} \) - \( k_C = \frac{C_2}{C_1} \) - */ - - // Ensure that P_1 > P_2 so the quadratic works out. - if (gasMixture1.Pressure < gasMixture2.Pressure) - { - (gasMixture1, gasMixture2) = (gasMixture2, gasMixture1); - } - - // Establish the dimensionless ratios. - var volumeRatio = gasMixture2.Volume / gasMixture1.Volume; - var molesRatio = gasMixture2.TotalMoles / gasMixture1.TotalMoles; - var temperatureRatio = gasMixture2.Temperature / gasMixture1.Temperature; - var heatCapacityRatio = GetHeatCapacity(gasMixture2) / GetHeatCapacity(gasMixture1); - - // The quadratic equation is solved for the transfer fraction. - var quadraticA = 1 + volumeRatio; - var quadraticB = molesRatio - volumeRatio + heatCapacityRatio * (temperatureRatio + volumeRatio); - var quadraticC = heatCapacityRatio * (molesRatio * temperatureRatio - volumeRatio); - - return (-quadraticB + MathF.Sqrt(quadraticB * quadraticB - 4 * quadraticA * quadraticC)) / (2 * quadraticA); - } - - /// - /// Determines the fraction of gas to be removed and transferred from a source - /// to a target to reach a target pressure - /// in the target . - /// - /// The source that gas will be removed from. - /// This should always be of higher pressure than the second . - /// The target that will increase in pressure - /// to the target pressure. - /// The target mixture's desired pressure to target. - /// A float representing the dimensionless fraction of gas to transfer from the source - /// to the target. This may return negative if you have your mixtures swapped. - /// Note that this method doesn't take into account the heat capacity of the - /// transferred volume causing a pressure rise in the target . - [PublicAPI] - public static float FractionToMaxPressure(GasMixture mix1, GasMixture mix2, float targetPressure) - { - var molesToTransfer = MolesToMaxPressure(mix1, mix2, targetPressure); - return molesToTransfer / mix1.TotalMoles; - } - - /// - /// Determines the number of moles to be removed and transferred from a source - /// to a target to reach a target pressure - /// in the target . - /// - /// The source that gas will be removed from. - /// This should always be of higher pressure than the second . - /// The target that will increase in pressure - /// to the target pressure. - /// The target mixture's desired pressure to target. - /// The difference in moles required to reach the target pressure. - /// Note that this method doesn't take into account the heat capacity of the - /// transferred volume causing a pressure rise in the target . - [PublicAPI] - public static float MolesToMaxPressure(GasMixture mix1, GasMixture mix2, float targetPressure) - { - /* - Calculate the moles required to reach the target pressure. - The formula is derived from the ideal gas law and the - general Richman's law, under the simplification that all the specific heat capacities are equal. - Derivation can also be seen at - https://github.com/space-wizards/space-station-14/pull/35211/files/a0ae787fe07a4e792570f55b49d9dd8038eb6e4d#r1961183456 - TODO ATMOS Make this properly obey the heat capacity change on the target mixture. - - Derivation is as follows. - Assume A is mix1, B is mix2, C is the combined mixture after transfer. - We can express the number of moles in C: - n_C = n_A + n_B - - We can then determine the temperature of C: - T_C = \frac{T_A n_A c_A + T_B n_B c_B}{n_A c_A + n_B c_B} - - Where c_A and c_B are the specific heats of mixtures A and B, respectively. - We can then express the pressure of C: - P_C = \frac{n_C R T_C}{V_C} - - Using the above equations, we can express P_C as follows: - P_C = \frac{(n_A + n_B) R (\frac{T_a n_A + T_B n_B}{n_A + n_B}}{V_C} - - Which can be reduced to: - P_C = \frac{R (T_A n_A + T_B n_B)}{V_C} - - Solving for n_A gives: - n_A = \frac{P_C V_C - R T_B n_B}{R T_A} - - Using the ideal gas law to substitute: - n_A = \frac{P_C V_C - P_B V_B}{R T_A} - - The output volume doesn't change: - V_B = V_C - - So: - n_A = \frac{(P_C - P_B) V_B}{R T_A} - */ - - var delta = targetPressure - mix2.Pressure; - var requiredMoles = (delta * mix2.Volume) / (mix1.Temperature * Atmospherics.R); - - // Return the fraction of moles to transfer. - return requiredMoles; - } - - /// - /// Determines the number of moles that need to be removed from a to reach a target pressure threshold. - /// - /// The gas mixture whose moles and properties will be used in the calculation. - /// The target pressure threshold to calculate against. - /// The difference in moles required to reach the target pressure threshold. - /// The temperature of the gas is assumed to be not changing due to a free expansion. - public static float MolesToPressureThreshold(GasMixture gasMixture, float targetPressure) - { - // Kid named PV = nRT. - return gasMixture.TotalMoles - - targetPressure * gasMixture.Volume / (Atmospherics.R * gasMixture.Temperature); - } - /// /// Checks whether a gas mixture is probably safe. /// This only checks temperature and pressure, not gas composition. diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index 420db3f732..74f4856d78 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -1,238 +1,175 @@ -using Content.Server.Explosion.EntitySystems; +using System.Numerics; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Atmos.EntitySystems; using Content.Shared.Cargo; +using Content.Shared.Popups; using Content.Shared.Throwing; using JetBrains.Annotations; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; +using Robust.Shared.Physics.Systems; using Robust.Shared.Random; -using Robust.Shared.Configuration; -using Content.Shared.CCVar; -namespace Content.Server.Atmos.EntitySystems +namespace Content.Server.Atmos.EntitySystems; + +[UsedImplicitly] +public sealed class GasTankSystem : SharedGasTankSystem { - [UsedImplicitly] - public sealed class GasTankSystem : SharedGasTankSystem + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + + private const float MinimumSoundValvePressure = 10.0f; + + private const float ReleaseArea = 0.0001f; // About 1cm^2 + + // A vector bias for throwing our gas tanks in radians. Averages about -43 degrees since the sprite is at a 45-degree angle. + private static readonly Vector2 ThrowVector = new (-1.0f, -0.5f); + + public override void Initialize() { - [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; - [Dependency] private readonly ExplosionSystem _explosions = default!; - [Dependency] private readonly SharedAudioSystem _audioSys = default!; - [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; + base.Initialize(); + SubscribeLocalEvent(OnParentChange); + SubscribeLocalEvent(OnAnalyzed); + SubscribeLocalEvent(OnGasTankPrice); + } - private const float TimerDelay = 0.5f; - private float _timer = 0f; - private const float MinimumSoundValvePressure = 10.0f; - private float _maxExplosionRange; - - public override void Initialize() + protected override void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + // Release gas if valve is open + // Disconnect from internals if valve is open + if (entity.Comp.ReleaseValveOpen) { - base.Initialize(); - SubscribeLocalEvent(OnParentChange); - SubscribeLocalEvent(OnAnalyzed); - SubscribeLocalEvent(OnGasTankPrice); - Subs.CVar(_cfg, CCVars.AtmosTankFragment, UpdateMaxRange, true); + DisconnectFromInternals(entity); + ReleaseGas(entity, args.dt); } - - private void UpdateMaxRange(float value) + else if (entity.Comp.CheckUser) { - _maxExplosionRange = value; - } - - public override void UpdateUserInterface(Entity ent) - { - var (owner, component) = ent; - _ui.SetUiState(owner, SharedGasTankUiKey.Key, - new GasTankBoundUserInterfaceState - { - TankPressure = component.Air?.Pressure ?? 0, - }); - } - - private void OnParentChange(EntityUid uid, GasTankComponent component, ref EntParentChangedMessage args) - { - // When an item is moved from hands -> pockets, the container removal briefly dumps the item on the floor. - // So this is a shitty fix, where the parent check is just delayed. But this really needs to get fixed - // properly at some point. - component.CheckUser = true; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - _timer += frameTime; - - if (_timer < TimerDelay) - return; - - _timer -= TimerDelay; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) + entity.Comp.CheckUser = false; + if (Transform(entity).ParentUid != entity.Comp.User) { - var gasTank = (uid, comp); - if (comp.IsValveOpen && !comp.IsLowPressure && comp.OutputPressure > 0) - { - ReleaseGas(gasTank); - } - - if (comp.CheckUser) - { - comp.CheckUser = false; - if (Transform(uid).ParentUid != comp.User) - { - DisconnectFromInternals(gasTank); - continue; - } - } - - if (comp.Air != null) - { - _atmosphereSystem.React(comp.Air, comp); - } - - CheckStatus(gasTank); - - if ((comp.IsConnected || comp.IsValveOpen) && _ui.IsUiOpen(uid, SharedGasTankUiKey.Key)) - { - UpdateUserInterface(gasTank); - } + DisconnectFromInternals(entity); } } - private void ReleaseGas(Entity gasTank) - { - var removed = RemoveAirVolume(gasTank, gasTank.Comp.ValveOutputRate * TimerDelay); - var environment = _atmosphereSystem.GetContainingMixture(gasTank.Owner, false, true); - if (environment != null) + Atmos.React(entity.Comp.Air, entity.Comp); + + if ((entity.Comp.IsConnected || entity.Comp.ReleaseValveOpen) && UI.IsUiOpen(entity.Owner, SharedGasTankUiKey.Key)) + UpdateUserInterface(entity); + } + + public override void UpdateUserInterface(Entity ent) + { + var (owner, component) = ent; + UI.SetUiState(owner, + SharedGasTankUiKey.Key, + new GasTankBoundUserInterfaceState { - _atmosphereSystem.Merge(environment, removed); - } - var strength = removed.TotalMoles * MathF.Sqrt(removed.Temperature); - var dir = _random.NextAngle().ToWorldVec(); - _throwing.TryThrow(gasTank, dir * strength, strength); - if (gasTank.Comp.OutputPressure >= MinimumSoundValvePressure) - _audioSys.PlayPvs(gasTank.Comp.RuptureSound, gasTank); - } + TankPressure = component.Air.Pressure + }); + } - public GasMixture? RemoveAir(Entity gasTank, float amount) - { - var gas = gasTank.Comp.Air?.Remove(amount); - CheckStatus(gasTank); - return gas; - } + private void OnParentChange(EntityUid uid, GasTankComponent component, ref EntParentChangedMessage args) + { + // When an item is moved from hands -> pockets, the container removal briefly dumps the item on the floor. + // So this is a shitty fix, where the parent check is just delayed. But this really needs to get fixed + // properly at some point. + component.CheckUser = true; + } - public GasMixture RemoveAirVolume(Entity gasTank, float volume) - { - var component = gasTank.Comp; - if (component.Air == null) - return new GasMixture(volume); + /// + /// Tries to release gas through the pressure release valve. + /// + /// The gas tank entity releasing gas + /// The amount of time since the last update + /// + private void ReleaseGas(Entity entity, float dt) + { + var environment = _atmosphereSystem.GetContainingMixture(entity.Owner, false, true); - var molesNeeded = component.OutputPressure * volume / (Atmospherics.R * component.Air.Temperature); + var deltaP = environment == null + ? entity.Comp.Air.Pressure + : entity.Comp.Air.Pressure - environment.Pressure; - var air = RemoveAir(gasTank, molesNeeded); + // Cap deltaP by the maximum output pressure of the tank. + if (deltaP < entity.Comp.SafetyPressure) + deltaP = Math.Min(entity.Comp.ReleasePressure, deltaP); - if (air != null) - air.Volume = volume; - else - return new GasMixture(volume); + var removed = _atmosphereSystem.FlowGas(entity.Comp.Air, deltaP, dt, ReleaseArea); - return air; - } + if (removed == null) + return; - public void AssumeAir(Entity ent, GasMixture giver) - { - _atmosphereSystem.Merge(ent.Comp.Air, giver); - CheckStatus(ent); - } + if (environment != null) + _atmosphereSystem.Merge(environment, removed); - public void CheckStatus(Entity ent) - { - var (owner, component) = ent; - if (component.Air == null) - return; + // If we wouldn't produce a sound, don't throw or play a sound. + if (deltaP < MinimumSoundValvePressure) + return; - var pressure = component.Air.Pressure; + Audio.PlayPvs(entity.Comp.ReleaseSound, entity); - if (pressure > component.TankFragmentPressure && _maxExplosionRange > 0) - { - // Give the gas a chance to build up more pressure. - for (var i = 0; i < 3; i++) - { - _atmosphereSystem.React(component.Air, component); - } + var strength = Atmos.GetOverPressure(removed) * Atmospherics.kPaToKg_m2; - pressure = component.Air.Pressure; - var range = MathF.Sqrt((pressure - component.TankFragmentPressure) / component.TankFragmentScale); + if (strength <= 0) + return; - // Let's cap the explosion, yeah? - // !1984 - range = Math.Min(Math.Min(range, GasTankComponent.MaxExplosionRange), _maxExplosionRange); + // TODO: I hate throwing system. I shouldn't need to do this boilerplate to get a nice looking throw + var rot = _xform.GetWorldRotation(entity); + var ang = _random.NextAngle(rot + ThrowVector.X, rot + ThrowVector.Y); - _explosions.TriggerExplosive(owner, radius: range); + // We bias by angle to make sure it doesn't rotate too much and flies relatively straight. + _physics.ApplyAngularImpulse(entity, (float)(strength * ang)); - return; - } + // TODO ATMOS: If we can predict ReleaseGas at some point, we should have this apply an impulse to a person holding this gas tank. + _throwing.TryThrow(entity, ang.ToWorldVec() * strength, strength, doSpin: false); + } - if (pressure > component.TankRupturePressure) - { - if (component.Integrity <= 0) - { - var environment = _atmosphereSystem.GetContainingMixture(owner, false, true); - if (environment != null) - _atmosphereSystem.Merge(environment, component.Air); + public GasMixture RemoveAirOutput(Entity gasTank, float volume) + { + var mixture = _atmosphereSystem.RemoveVolumeAtPressure(gasTank.Comp.Air, volume, gasTank.Comp.ReleasePressure); + // We resize the volume because lungs breathe in volume rather than being pressure based atm. + // If we don't do this, they won't consume all of the outputted gas or will consume way too much. + mixture.Volume = volume; + return mixture; + } - _audioSys.PlayPvs(component.RuptureSound, Transform(owner).Coordinates, AudioParams.Default.WithVariation(0.125f)); + public GasMixture RemoveAir(Entity gasTank, float amount) + { + return gasTank.Comp.Air.Remove(amount); + } - QueueDel(owner); - return; - } + public void AssumeAir(Entity ent, GasMixture giver) + { + _atmosphereSystem.Merge(ent.Comp.Air, giver); + CheckStatus(ent); + } - component.Integrity--; - return; - } + protected override void SafetyMeasures(Entity entity) + { + if (entity.Comp.ReleaseValveOpen) + return; - if (pressure > component.TankLeakPressure) - { - if (component.Integrity <= 0) - { - var environment = _atmosphereSystem.GetContainingMixture(owner, false, true); - if (environment == null) - return; + ToggleValve(entity); + if (entity.Comp.SafetyAlert != null) + _popup.PopupEntity(Loc.GetString(entity.Comp.SafetyAlert), entity, PopupType.LargeCaution); - var leakedGas = component.Air.RemoveRatio(0.25f); - _atmosphereSystem.Merge(environment, leakedGas); - } - else - { - component.Integrity--; - } + Dirty(entity); + } - return; - } + /// + /// 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)); + } - if (component.Integrity < 3) - component.Integrity++; - } - - /// - /// Returns the gas mixture for the gas analyzer - /// - private void OnAnalyzed(EntityUid uid, GasTankComponent component, GasAnalyzerScanEvent args) - { - args.GasMixtures ??= new List<(string, GasMixture?)>(); - args.GasMixtures.Add((Name(uid), component.Air)); - } - - private void OnGasTankPrice(EntityUid uid, GasTankComponent component, ref PriceCalculationEvent args) - { - args.Price += _atmosphereSystem.GetPrice(component.Air); - } + private void OnGasTankPrice(EntityUid uid, GasTankComponent component, ref PriceCalculationEvent args) + { + args.Price += _atmosphereSystem.GetPrice(component.Air); } } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 06807640a8..632e06caa8 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -5,11 +5,11 @@ using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Piping.Binary.Components; -using Content.Shared.Atmos.Piping.Components; using Content.Shared.Atmos.Piping.Unary.Systems; using Content.Shared.Cargo; using Content.Shared.Database; using Content.Shared.NodeContainer; +using Content.Shared.Popups; using GasCanisterComponent = Content.Shared.Atmos.Piping.Unary.Components.GasCanisterComponent; namespace Content.Server.Atmos.Piping.Unary.EntitySystems; @@ -17,14 +17,16 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems; public sealed class GasCanisterSystem : SharedGasCanisterSystem { [Dependency] private readonly AtmosphereSystem _atmos = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + private const float ReleaseArea = 0.05f; // 500cm^2 Number chosen for balance reasons. It's quite large, but so are gas canisters (holding 1.5 cubic meters of gas!) public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnCanisterUpdated); SubscribeLocalEvent(CalculateCanisterPrice); SubscribeLocalEvent(OnAnalyzed); } @@ -64,64 +66,83 @@ public sealed class GasCanisterSystem : SharedGasCanisterSystem tankPressure = tankComponent.Air.Pressure; } - UI.SetUiState(uid, GasCanisterUiKey.Key, + UI.SetUiState(uid, + GasCanisterUiKey.Key, new GasCanisterBoundUserInterfaceState(canister.Air.Pressure, portStatus, tankPressure)); } - private void OnCanisterUpdated(EntityUid uid, GasCanisterComponent canister, ref AtmosDeviceUpdateEvent args) + protected override void SafetyMeasures(Entity entity) { - _atmos.React(canister.Air, canister); - - if (!TryComp(uid, out var nodeContainer) - || !TryComp(uid, out var appearance)) + if (entity.Comp.SafetyValveOpen) return; - if (!_nodeContainer.TryGetNode(nodeContainer, canister.PortName, out PortablePipeNode? portNode)) + ToggleSafetyValve(entity, open: true); + if (entity.Comp.SafetyAlert != null) + _popup.PopupEntity(Loc.GetString(entity.Comp.SafetyAlert), entity, PopupType.LargeCaution); + } + + private void ToggleSafetyValve(Entity entity, bool open) + { + entity.Comp.SafetyValveOpen = true; + Audio.PlayPvs(entity.Comp.ValveSound, entity); + } + + protected override void DeviceUpdated(Entity entity, ref AtmosDeviceUpdateEvent args) + { + _atmos.React(entity.Comp.Air, entity.Comp); + + if (!TryComp(entity, out var nodeContainer) + || !TryComp(entity, out var appearance)) + return; + + if (!_nodeContainer.TryGetNode(nodeContainer, entity.Comp.PortName, out PortablePipeNode? portNode)) return; if (portNode.NodeGroup is PipeNet {NodeCount: > 1} net) { - MixContainerWithPipeNet(canister.Air, net.Air); + MixContainerWithPipeNet(entity.Comp.Air, net.Air); } - // Release valve is open, release gas. - if (canister.ReleaseValve) + // If safety valve is open, we release gas through there ignoring other outputs. + if (entity.Comp.SafetyValveOpen) { - if (canister.GasTankSlot.Item != null) - { - var gasTank = Comp(canister.GasTankSlot.Item.Value); - _atmos.ReleaseGasTo(canister.Air, gasTank.Air, canister.ReleasePressure); - } - else - { - var environment = _atmos.GetContainingMixture(uid, args.Grid, args.Map, false, true); - _atmos.ReleaseGasTo(canister.Air, environment, canister.ReleasePressure); - } + var environment = _atmos.GetContainingMixture(entity.Owner, args.Grid, args.Map, false, true); + _atmos.FlowGas(entity.Comp.Air, environment, args.dt, ReleaseArea); + if (entity.Comp.Air.Pressure < entity.Comp.SafetyPressure) + ToggleSafetyValve(entity, false); + } + else if (entity.Comp.ReleaseValveOpen) // Release valve is open, release gas. + { + var output = entity.Comp.GasTankSlot.Item == null + ? _atmos.GetContainingMixture(entity.Owner, args.Grid, args.Map, false, true) + : CompOrNull(entity.Comp.GasTankSlot.Item.Value)?.Air; + + _atmos.FlowGas(entity.Comp.Air, output, entity.Comp.ReleasePressure, args.dt,ReleaseArea); } // If last pressure is very close to the current pressure, do nothing. - if (MathHelper.CloseToPercent(canister.Air.Pressure, canister.LastPressure)) + if (MathHelper.CloseToPercent(entity.Comp.Air.Pressure, entity.Comp.LastPressure)) return; - DirtyUI(uid, canister, nodeContainer); + DirtyUI(entity, entity.Comp, nodeContainer); - canister.LastPressure = canister.Air.Pressure; + entity.Comp.LastPressure = entity.Comp.Air.Pressure; - if (canister.Air.Pressure < 10) + if (entity.Comp.Air.Pressure < 10) { - _appearance.SetData(uid, GasCanisterVisuals.PressureState, 0, appearance); + _appearance.SetData(entity, GasCanisterVisuals.PressureState, 0, appearance); } - else if (canister.Air.Pressure < Atmospherics.OneAtmosphere) + else if (entity.Comp.Air.Pressure < Atmospherics.OneAtmosphere) { - _appearance.SetData(uid, GasCanisterVisuals.PressureState, 1, appearance); + _appearance.SetData(entity, GasCanisterVisuals.PressureState, 1, appearance); } - else if (canister.Air.Pressure < (15 * Atmospherics.OneAtmosphere)) + else if (entity.Comp.Air.Pressure < (15 * Atmospherics.OneAtmosphere)) { - _appearance.SetData(uid, GasCanisterVisuals.PressureState, 2, appearance); + _appearance.SetData(entity, GasCanisterVisuals.PressureState, 2, appearance); } else { - _appearance.SetData(uid, GasCanisterVisuals.PressureState, 3, appearance); + _appearance.SetData(entity, GasCanisterVisuals.PressureState, 3, appearance); } } diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index c470ae3f0d..da7ac33479 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -1,14 +1,9 @@ using Content.Server.Atmos.EntitySystems; -using Content.Server.Body.Components; -using Content.Server.Popups; using Content.Shared.Alert; -using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Systems; -using Content.Shared.DoAfter; using Content.Shared.Internals; -using Content.Shared.Inventory; using Content.Shared.Roles; namespace Content.Server.Body.Systems; @@ -59,8 +54,9 @@ public sealed class InternalsSystem : SharedInternalsSystem if (AreInternalsWorking(ent)) { var gasTank = Comp(ent.Comp.GasTankEntity!.Value); - args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), args.Respirator.BreathVolume); - // TODO: Should listen to gas tank updates instead I guess? + // TODO ATMOS: TODO BODY: This is an incredibly stupid workaround to stop Urist McHands from horfing all 5 litres of the gas tank in 10 seconds. + args.Gas = _gasTank.RemoveAirOutput((ent.Comp.GasTankEntity.Value, gasTank), args.Respirator.BreathVolume); + // TODO ATMOS: Should listen to gas tank updates instead I guess? _alerts.ShowAlert(ent.Owner, ent.Comp.InternalsAlert, GetSeverity(ent)); } } diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs index 12af967526..ffd0daee5b 100644 --- a/Content.Shared/Atmos/Atmospherics.cs +++ b/Content.Shared/Atmos/Atmospherics.cs @@ -104,6 +104,21 @@ namespace Content.Shared.Atmos public const float OxygenMolesGasMiner = MolesCellGasMiner * OxygenStandard; public const float NitrogenMolesGasMiner = MolesCellGasMiner * NitrogenStandard; + /// + /// Converts Grams to Kilograms. + /// + public const float gToKg = 0.001f; + + /// + /// Convert kPa to Kg/m^2 + /// + public const float kPaToKg_m2 = 0.00980665f; + + /// + /// Convert Kg/m^2 to kPa + /// + public const float Kg_m2TokPa = 101.9716212978f; + #endregion /// @@ -219,7 +234,7 @@ namespace Content.Shared.Atmos /// /// Amount of heat released per mole of burnt hydrogen or tritium (hydrogen isotope) /// - public const float FireHydrogenEnergyReleased = 284e4f; + public const float FireHydrogenEnergyReleased = 284e3f; public const float FireMinimumTemperatureToExist = T0C + 100f; public const float FireMinimumTemperatureToSpread = T0C + 150f; public const float FireSpreadRadiosityScale = 0.85f; diff --git a/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs b/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs new file mode 100644 index 0000000000..7c6ac9cfaf --- /dev/null +++ b/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs @@ -0,0 +1,80 @@ +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 int DefaultIntegrity = 5; + 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"); + + /// + /// 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 int MaxIntegrity { get; set; } = DefaultIntegrity; + + [DataField] + public int Integrity { get; set; } = DefaultIntegrity; +} diff --git a/Content.Shared/Atmos/Components/GasTankComponent.cs b/Content.Shared/Atmos/Components/GasTankComponent.cs index 7ca6098555..31fa3a0d5a 100644 --- a/Content.Shared/Atmos/Components/GasTankComponent.cs +++ b/Content.Shared/Atmos/Components/GasTankComponent.cs @@ -5,18 +5,12 @@ using Robust.Shared.Prototypes; namespace Content.Shared.Atmos.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] -public sealed partial class GasTankComponent : Component, IGasMixtureHolder +public sealed partial class GasTankComponent : GasMaxPressureHolderComponent { - public const float MaxExplosionRange = 26f; - private const float DefaultLowPressure = 0f; - private const float DefaultOutputPressure = Atmospherics.OneAtmosphere; + private const float DefaultLowPressure = Atmospherics.OneAtmosphere; - public int Integrity = 3; public bool IsLowPressure => Air.Pressure <= TankLowPressure; - [DataField] - public SoundSpecifier RuptureSound = new SoundPathSpecifier("/Audio/Effects/spray.ogg"); - [DataField] public SoundSpecifier? ConnectSound = new SoundPathSpecifier("/Audio/Effects/internals.ogg") @@ -32,27 +26,12 @@ public sealed partial class GasTankComponent : Component, IGasMixtureHolder public EntityUid? ConnectStream; public EntityUid? DisconnectStream; - [DataField] - public GasMixture Air { get; set; } = new(); - /// /// Pressure at which tank should be considered 'low' such as for internals. /// [DataField] public float TankLowPressure = DefaultLowPressure; - /// - /// Distributed pressure. - /// - [DataField, AutoNetworkedField] - public float OutputPressure = DefaultOutputPressure; - - /// - /// The maximum allowed output pressure. - /// - [DataField] - public float MaxOutputPressure = 3 * DefaultOutputPressure; - /// /// Tank is connected to internals. /// @@ -69,52 +48,9 @@ public sealed partial class GasTankComponent : Component, IGasMixtureHolder [ViewVariables] public bool CheckUser; - /// - /// Pressure at which tanks start leaking. - /// - [DataField] - public float TankLeakPressure = 30 * Atmospherics.OneAtmosphere; - - /// - /// Pressure at which tank spills all contents into atmosphere. - /// - [DataField] - public float TankRupturePressure = 40 * Atmospherics.OneAtmosphere; - - /// - /// Base 3x3 explosion. - /// - [DataField] - public float TankFragmentPressure = 50 * Atmospherics.OneAtmosphere; - - /// - /// Increases explosion for each scale kPa above threshold. - /// - [DataField] - public float TankFragmentScale = 2.25f * Atmospherics.OneAtmosphere; - [DataField] public EntProtoId ToggleAction = "ActionToggleInternals"; [DataField, AutoNetworkedField] public EntityUid? ToggleActionEntity; - - /// - /// Valve to release gas from tank - /// - [DataField, AutoNetworkedField] - public bool IsValveOpen; - - /// - /// Gas release rate in L/s - /// - [DataField, AutoNetworkedField] - public float ValveOutputRate = 100f; - - [DataField] - public SoundSpecifier ValveSound = - new SoundCollectionSpecifier("valveSqueak") - { - Params = AudioParams.Default.WithVolume(-5f), - }; } diff --git a/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs b/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs new file mode 100644 index 0000000000..88386a5c74 --- /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 + /// + int MaxIntegrity { get; set; } + + /// + /// How many over-pressures until this gas tank detonates. + /// An overpressure is defined as pressure exceeding + /// This determines the current value + /// + int Integrity { get; set; } +} diff --git a/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs b/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs new file mode 100644 index 0000000000..68bfaa6a11 --- /dev/null +++ b/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs @@ -0,0 +1,144 @@ +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)) + 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. + /// True if the device hasn't failed. False if the device has failed and been destroyed. + protected bool CheckStatus(Entity entity) + { + 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--; + 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++; + Appearance.SetData(entity.Owner, GasIntegrity.Integrity, entity.Comp.Integrity); + Appearance.SetData(entity.Owner, GasIntegrity.MaxIntegrity, entity.Comp.MaxIntegrity); + } + + // Device tries to prevent failure. + if (pressure > entity.Comp.SafetyPressure) + SafetyMeasures(entity); + + return true; + } +} + +[Serializable, NetSerializable, Flags] +public enum GasIntegrity +{ + Integrity, + MaxIntegrity +} diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs index 3852c30974..5b953e6df4 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.API.cs @@ -86,4 +86,19 @@ public abstract partial class SharedAtmosphereSystem return ev.Handled; } + + /// + /// Gets the potential energy from overpressure between two gas mixtures. + /// + /// + /// Returns the potential energy of the overpressure in Joules. + /// Value will be positive if the potential energy is outward (mix1 -> mix2) + /// Value will be negative if potential energy is inward (mix2 -> mix1) + /// + [PublicAPI] + public float GetOverPressure(GasMixture mix1, GasMixture? mix2 = null) + { + return (mix1.Pressure - (mix2?.Pressure ?? 0)) * mix1.Volume; + } + } diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs index 99b2942f13..5433ec6570 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs @@ -19,7 +19,15 @@ public abstract partial class SharedAtmosphereSystem /// Cached array of molar heat capacities of the gases. /// public float[] GasMolarHeatCapacities => _gasMolarHeatCapacities; - private float[] _gasMolarHeatCapacities = new float[Atmospherics.TotalNumberOfGases]; + + private float[] _gasMolarHeatCapacities = new float[Atmospherics.AdjustedNumberOfGases]; + + /// + /// Cached array of gas specific mols + /// + public float[] GasMolarMasses => _gasMolarMasses; + + private float[] _gasMolarMasses = new float[Atmospherics.AdjustedNumberOfGases]; /// /// Mask used to determine if a gas is flammable or not. @@ -69,8 +77,6 @@ public abstract partial class SharedAtmosphereSystem GasReagents[idx] = gasPrototype.Reagent; } - Array.Resize(ref _gasMolarHeatCapacities, MathHelper.NextMultipleOf(Atmospherics.TotalNumberOfGases, 4)); - for (var i = 0; i < GasPrototypes.Length; i++) { /* @@ -82,6 +88,7 @@ public abstract partial class SharedAtmosphereSystem TODO ATMOS: please just make this 2 separate arrays instead of invoking multiplication every time. */ _gasMolarHeatCapacities[i] = GasPrototypes[i].MolarHeatCapacity / HeatScale; + _gasMolarMasses[i] = GasPrototypes[i].MolarMass; // """Mask""" built here. Used to determine if a gas is fuel/oxidizer or not decently quickly and clearly. GasFuelMask[i] = GasPrototypes[i].IsFuel ? 1 : 0; @@ -232,6 +239,244 @@ public abstract partial class SharedAtmosphereSystem return GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable); } + /// + /// Gets the mass of a given + /// + /// The in question + /// Returns the volume in kilograms. + [PublicAPI] + public abstract float GetMass(GasMixture mix); + + /// + [PublicAPI] + public abstract float GetMass(float[] moles); + + /// + /// Calculates the amount of volume transferred from one gas mixture to another over time based on flow rate. + /// + /// + /// A + /// Another + /// The area of transfer, in square meters. One tile of movement is about one square meter. + /// delta time, or how much time is passing/has passed. + /// Discharge coefficient. An abstract modifier for friction and turbulence. + /// + /// The volume of gas being moved over dt in Litres. + /// If the value is positive it's in the direction of mix1->mix2, + /// If it's negative it's in the direction of mix2 -> mix1 + /// + /// I'm assuming C is always 1 because I'm lazy, you can precalculate it and pass it with the area if you really care. + [PublicAPI] + public double GetFlowVolume(GasMixture mix1, GasMixture mix2, float area, float dt, float c = 1f) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dt); + return dt * GetFlowRate(mix1, mix2, area, c); + } + + /// + [PublicAPI] + public double GetFlowVolume(GasMixture mix1, float deltaP, float area, float dt, float c = 1f) + { + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(dt); + return dt * GetFlowRate(mix1, deltaP, area, c); + } + + /// + /// Calculates the volumetric flow rate between two gas mixtures. + /// + /// A + /// Another + /// The area of transfer, in square meters. One tile of movement is about one square meter. + /// Discharge coefficient. An abstract modifier for friction and turbulence. + /// + /// The volume of gas being moved in Litres / Second. + /// If the value is positive it's in the direction of mix1->mix2, + /// If it's negative it's in the direction of mix2 -> mix1 + /// + /// I'm assuming C is always 1 because I'm lazy, you can precalculate it and pass it with the area if you really care. + [PublicAPI] + public double GetFlowRate(GasMixture mix1, GasMixture mix2, float area, float c = 1f) + { + /* + Q = C × A × √(2 × ΔP / ρ) + Q is the volumetric airflow rate + C is the discharge coefficient + A is the cross-sectional area + ΔP is the measured pressure difference + ρ is the air density, adjusted for environmental conditions. + We can break this up into Q = A × V where V is the velocity of the gas. + */ + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(area); + return area * GetFlowVelocity(mix1, mix2, c); + } + + /// + [PublicAPI] + public double GetFlowRate(GasMixture mix1, float deltaP, float area, float c = 1f) + { + /* + Q = C × A × √(2 × ΔP / ρ) + Q is the volumetric airflow rate + C is the discharge coefficient + A is the cross-sectional area + ΔP is the measured pressure difference + ρ is the air density, adjusted for environmental conditions. + We can break this up into Q = A × V where V is the velocity of the gas. + */ + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(area); + return area * GetFlowVelocity(mix1, deltaP, c); + } + + /// + /// Calculates the flow velocity between two gas mixtures using Q = C × A × √(2 × ΔP / ρ) but without the A (area) + /// Useful for determining flow rate, or how fast a gas is moving. + /// + /// A + /// Another + /// Discharge coefficient. An abstract modifier for friction and turbulence. + /// + /// The velocity of gas movement between two mixtures in Meters / Second. + /// If the value is positive it's in the direction of mix1->mix2, + /// If it's negative it's in the direction of mix2 -> mix1 + /// + [PublicAPI] + public double GetFlowVelocity(GasMixture mix1, GasMixture mix2, float c = 1f) + { + if (mix1.Pressure > mix2.Pressure) + return GetFlowVelocity(mix1, mix1.Pressure - mix2.Pressure, c); + + return -GetFlowVelocity(mix2, mix2.Pressure - mix1.Pressure, c); + } + + /// + /// Calculates the flow velocity between a gas mixture given a pressure differential. + /// + /// The mixture which is being allowed to flow + /// The difference in pressure between this mixture and where it's flowing to + /// Discharge coefficient. An abstract modifier for friction and turbulence. + /// + /// The velocity of the gas leaving our mixture in Meters / Second. + /// + [PublicAPI] + public double GetFlowVelocity(GasMixture mix1, float deltaP, float c = 1f) + { + /* + V = C × √(2 × ΔP / ρ) + V is the velocity of our gas + C is the discharge coefficient + ΔP is the measured pressure difference + ρ is the air density, adjusted for environmental conditions. + Density is equivalent to Mass / Volume, so we invert that to divide by density. + */ + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(deltaP); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(c); + return c * Math.Sqrt(2 * deltaP * mix1.Volume / GetMass(mix1)); + } + + /// + /// Lets a volume of gas flow throw a constrained area into another volume of gas over a period of time. + /// + /// Gas volume that is discharging some of its gas. + /// Gas volume that is receiving the discharge. + /// Time that the discharge occurs in seconds, should be as small as possible since it doesn't use calculus + /// Area that our gas is traveling through in m^2, the larger the area the bigger the transfer. + /// Default of 2m^2 since that's the area of a single face of an atmos tile. + [PublicAPI] + public void FlowGas(GasMixture mixture, GasMixture? output, float dt, float area) + { + FlowGas(mixture, output, mixture.Pressure, dt, area); + } + + /// + [PublicAPI] + public void FlowGas(GasMixture mixture, GasMixture? output, float pressure, float dt, float area) + { + if (output == null) + { + FlowGas(mixture, pressure, dt, area); + return; + } + + pressure = Math.Min(pressure, mixture.Pressure - output.Pressure); + var removed = FlowGas(mixture, pressure, dt, area); + + if (removed == null) + return; + + Merge(output, removed); + } + + /// + /// Lets a volume of gas flow through constrained area at a constrained pressure delta. + /// + /// Mixture of gas that is currently flowing + /// Pressure our gas is able to flow at. + /// Time that the discharge occurs in seconds, should be as small as possible since it doesn't use calculus + /// Area that our gas is traveling through in m^2, the larger the area the bigger the transfer. + /// Default of 2m^2 since that's the area of a single face of an atmos tile. + /// + [PublicAPI] + public GasMixture? FlowGas(GasMixture mixture, float deltaP, float dt, float area = 2f) + { + if (deltaP <= 0) + return null; + + return ReleaseGasAt(mixture, (float)GetFlowVolume(mixture, deltaP, area, dt), deltaP); + } + + /// + /// Releases some volume of a gas mixture at a specified pressure. + /// + /// Mixture which is releasing gas. + /// Optional Mixture to receive gas + /// Volume we are releasing + /// Pressure of the released volume. + [PublicAPI] + public void ReleaseGasAt(GasMixture mixture, GasMixture? output, float volume, float targetPressure) + { + if (output == null) + { + ReleaseGasAt(mixture, volume, targetPressure); + return; + } + + targetPressure = Math.Min(targetPressure, mixture.Pressure - output.Pressure); + + if (targetPressure <= 0) + return; + + var molesNeeded = Math.Min(targetPressure * volume / (Atmospherics.R * mixture.Temperature), + MolesToEqualizePressure(mixture, output)); + + var removed = mixture.Remove(molesNeeded); + + Merge(mixture, removed); + } + + /// + [PublicAPI] + public GasMixture? ReleaseGasAt(GasMixture mixture, float volume, float targetPressure) + { + if (targetPressure <= 0) + return null; + + targetPressure = Math.Min(targetPressure, mixture.Pressure); + + return RemoveVolumeAtPressure(mixture, volume, targetPressure); + } + + /// + /// Removes a specified volume of gas from a mixture, at a specific pressure. + /// + /// mixture of gas + /// volume we're attempting to remove + /// pressure that volume will be removed at. + public GasMixture RemoveVolumeAtPressure(GasMixture mixture, float volume, float pressure) + { + var molesNeeded = pressure * volume / (Atmospherics.R * mixture.Temperature); + return mixture.Remove(molesNeeded); + } + /// /// Gets the heat capacity for a . /// @@ -242,4 +487,219 @@ public abstract partial class SharedAtmosphereSystem /// The heat capacity of the . [MethodImpl(MethodImplOptions.AggressiveInlining)] protected abstract float GetHeatCapacityCalculation(float[] moles, bool space); + + + /// + /// Calculates the moles that must be transferred from + /// to to equalize pressure. + /// + public float MolesToEqualizePressure(GasMixture gasMixture1, GasMixture gasMixture2) + { + return gasMixture1.TotalMoles * FractionToEqualizePressure(gasMixture1, gasMixture2); + } + + /// + /// Calculates the dimensionless fraction of gas required to equalize pressure between two gas mixtures. + /// + /// The first gas mixture involved in the pressure equalization. + /// This mixture should be the one you always expect to be the highest pressure. + /// The second gas mixture involved in the pressure equalization. + /// A float (from 0 to 1) representing the dimensionless fraction of gas that needs to be transferred from the + /// mixture of higher pressure to the mixture of lower pressure. + /// + /// + /// This properly takes into account the effect + /// of gas merging from inlet to outlet affecting the temperature + /// (and possibly increasing the pressure) in the outlet. + /// + /// + /// The gas is assumed to expand freely, + /// so the temperature of the gas with the greater pressure is not changing. + /// + /// + /// + /// If you want to calculate the moles required to equalize pressure between an inlet and an outlet, + /// multiply the fraction returned by the source moles. + /// + public float FractionToEqualizePressure(GasMixture gasMixture1, GasMixture gasMixture2) + { + /* + Problem: the gas being merged from the inlet to the outlet could affect the + temp. of the gas and cause a pressure rise. + We want the pressure to be equalized, so we have to account for this. + + For clarity, let's assume that gasMixture1 is the inlet and gasMixture2 is the outlet. + + We require mechanical equilibrium, so \( P_1' = P_2' \) + + Before the transfer, we have: + \( P_1 = \frac{n_1 R T_1}{V_1} \) + \( P_2 = \frac{n_2 R T_2}{V_2} \) + + After removing fraction \( x \) moles from the inlet, we have: + \( P_1' = \frac{(1 - x) n_1 R T_1}{V_1} \) + + The outlet will gain the same \( x n_1 \) moles of gas. + So \( n_2' = n_2 + x n_1 \) + + After mixing, the outlet temperature will be changed. + Denote the new mixture temperature as \( T_2' \). + Volume is constant. + So we have: + \( P_2' = \frac{(n_2 + x n_1) R T_2}{V_2} \) + + The total energy of the incoming inlet to outlet gas at \( T_1 \) plus the existing energy of the outlet gas at \( T_2 \) + will be equal to the energy of the new outlet gas at \( T_2' \). + This leads to the following derivation: + \( x n_1 C_1 T_1 + n_2 C_2 T_2 = (x n_1 C_1 + n_2 C_2) T_2' \) + + Where \( C_1 \) and \( C_2 \) are the heat capacities of the inlet and outlet gases, respectively. + + Solving for \( T_2' \) gives us: + \( T_2' = \frac{x n_1 C_1 T_1 + n_2 C_2 T_2}{x n_1 C_1 + n_2 C_2} \) + + Once again, we require mechanical equilibrium (\( P_1' = P_2' \)), + so we can substitute \( T_2' \) into the pressure equation: + + \( \frac{(1 - x) n_1 R T_1}{V_1} = + \frac{(n_2 + x n_1) R}{V_2} \cdot + \frac{x n_1 C_1 T_1 + n_2 C_2 T_2} + {x n_1 C_1 + n_2 C_2} \) + + Now it's a matter of solving for \( x \). + Not going to show the full derivation here, just steps. + 1. Cancel common factor \( R \). + 2. Multiply both sides by \( x n_1 C_1 + n_2 C_2 \), so that everything + becomes a polynomial in terms of \( x \). + 3. Expand both sides. + 4. Collect like powers of \( x \). + 5. After collecting, you should end up with a polynomial of the form: + + \( (-n_1 C_1 T_1 (1 + \frac{V_2}{V_1})) x^2 + + (n_1 T_1 \frac{V_2}{V_1} (C_1 - C_2) - n_2 C_1 T_1 - n_1 C_2 T_2) x + + (n_1 T_1 \frac{V_2}{V_1} C_2 - n_2 C_2 T_2) = 0 \) + + Divide through by \( n_1 C_1 T_1 \) and replace each ratio with a symbol for clarity: + \( k_V = \frac{V_2}{V_1} \) + \( k_n = \frac{n_2}{n_1} \) + \( k_T = \frac{T_2}{T_1} \) + \( k_C = \frac{C_2}{C_1} \) + */ + + // Ensure that P_1 > P_2 so the quadratic works out. + if (gasMixture1.Pressure < gasMixture2.Pressure) + { + (gasMixture1, gasMixture2) = (gasMixture2, gasMixture1); + } + + // Establish the dimensionless ratios. + var volumeRatio = gasMixture2.Volume / gasMixture1.Volume; + var molesRatio = gasMixture2.TotalMoles / gasMixture1.TotalMoles; + var temperatureRatio = gasMixture2.Temperature / gasMixture1.Temperature; + var heatCapacityRatio = GetHeatCapacity(gasMixture2) / GetHeatCapacity(gasMixture1); + + // The quadratic equation is solved for the transfer fraction. + var quadraticA = 1 + volumeRatio; + var quadraticB = molesRatio - volumeRatio + heatCapacityRatio * (temperatureRatio + volumeRatio); + var quadraticC = heatCapacityRatio * (molesRatio * temperatureRatio - volumeRatio); + + return (-quadraticB + MathF.Sqrt(quadraticB * quadraticB - 4 * quadraticA * quadraticC)) / (2 * quadraticA); + } + + /// + /// Determines the fraction of gas to be removed and transferred from a source + /// to a target to reach a target pressure + /// in the target . + /// + /// The source that gas will be removed from. + /// This should always be of higher pressure than the second . + /// The target that will increase in pressure + /// to the target pressure. + /// The target mixture's desired pressure to target. + /// A float representing the dimensionless fraction of gas to transfer from the source + /// to the target. This may return negative if you have your mixtures swapped. + /// Note that this method doesn't take into account the heat capacity of the + /// transferred volume causing a pressure rise in the target . + [PublicAPI] + public static float FractionToMaxPressure(GasMixture mix1, GasMixture mix2, float targetPressure) + { + var molesToTransfer = MolesToMaxPressure(mix1, mix2, targetPressure); + return molesToTransfer / mix1.TotalMoles; + } + + /// + /// Determines the number of moles to be removed and transferred from a source + /// to a target to reach a target pressure + /// in the target . + /// + /// The source that gas will be removed from. + /// This should always be of higher pressure than the second . + /// The target that will increase in pressure + /// to the target pressure. + /// The target mixture's desired pressure to target. + /// The difference in moles required to reach the target pressure. + /// Note that this method doesn't take into account the heat capacity of the + /// transferred volume causing a pressure rise in the target . + [PublicAPI] + public static float MolesToMaxPressure(GasMixture mix1, GasMixture mix2, float targetPressure) + { + /* + Calculate the moles required to reach the target pressure. + The formula is derived from the ideal gas law and the + general Richman's law, under the simplification that all the specific heat capacities are equal. + Derivation can also be seen at + https://github.com/space-wizards/space-station-14/pull/35211/files/a0ae787fe07a4e792570f55b49d9dd8038eb6e4d#r1961183456 + TODO ATMOS Make this properly obey the heat capacity change on the target mixture. + + Derivation is as follows. + Assume A is mix1, B is mix2, C is the combined mixture after transfer. + We can express the number of moles in C: + n_C = n_A + n_B + + We can then determine the temperature of C: + T_C = \frac{T_A n_A c_A + T_B n_B c_B}{n_A c_A + n_B c_B} + + Where c_A and c_B are the specific heats of mixtures A and B, respectively. + We can then express the pressure of C: + P_C = \frac{n_C R T_C}{V_C} + + Using the above equations, we can express P_C as follows: + P_C = \frac{(n_A + n_B) R (\frac{T_a n_A + T_B n_B}{n_A + n_B}}{V_C} + + Which can be reduced to: + P_C = \frac{R (T_A n_A + T_B n_B)}{V_C} + + Solving for n_A gives: + n_A = \frac{P_C V_C - R T_B n_B}{R T_A} + + Using the ideal gas law to substitute: + n_A = \frac{P_C V_C - P_B V_B}{R T_A} + + The output volume doesn't change: + V_B = V_C + + So: + n_A = \frac{(P_C - P_B) V_B}{R T_A} + */ + + var delta = targetPressure - mix2.Pressure; + var requiredMoles = (delta * mix2.Volume) / (mix1.Temperature * Atmospherics.R); + + // Return the fraction of moles to transfer. + return requiredMoles; + } + + /// + /// Determines the number of moles that need to be removed from a to reach a target pressure threshold. + /// + /// The gas mixture whose moles and properties will be used in the calculation. + /// The target pressure threshold to calculate against. + /// The difference in moles required to reach the target pressure threshold. + /// The temperature of the gas is assumed to be not changing due to a free expansion. + public static float MolesToPressureThreshold(GasMixture gasMixture, float targetPressure) + { + // Kid named PV = nRT. + return gasMixture.TotalMoles - + targetPressure * gasMixture.Volume / (Atmospherics.R * gasMixture.Temperature); + } } diff --git a/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs b/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs index 4158c687bd..ffb7d9f865 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedGasTankSystem.cs @@ -6,16 +6,14 @@ using Content.Shared.Timing; using Content.Shared.Toggleable; using Content.Shared.UserInterface; using Content.Shared.Verbs; -using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using InternalsComponent = Content.Shared.Body.Components.InternalsComponent; namespace Content.Shared.Atmos.EntitySystems; -public abstract class SharedGasTankSystem : EntitySystem +public abstract class SharedGasTankSystem : GasMaxPressureSystem { [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedContainerSystem _containers = default!; [Dependency] private readonly SharedInternalsSystem _internals = default!; [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; @@ -48,9 +46,9 @@ public abstract class SharedGasTankSystem : EntitySystem private void OnGasTankSetPressure(Entity ent, ref GasTankSetPressureMessage args) { - var pressure = Math.Clamp(args.Pressure, 0f, ent.Comp.MaxOutputPressure); + var pressure = Math.Clamp(args.Pressure, 0f, ent.Comp.MaxReleasePressure); - ent.Comp.OutputPressure = pressure; + ent.Comp.ReleasePressure = pressure; Dirty(ent); UpdateUserInterface(ent); } @@ -81,7 +79,7 @@ public abstract class SharedGasTankSystem : EntitySystem if (component.IsConnected) args.PushMarkup(Loc.GetString("comp-gas-tank-connected")); - args.PushMarkup(Loc.GetString(component.IsValveOpen ? "comp-gas-tank-examine-open-valve" : "comp-gas-tank-examine-closed-valve")); + args.PushMarkup(Loc.GetString(component.ReleaseValveOpen ? "comp-gas-tank-examine-open-valve" : "comp-gas-tank-examine-closed-valve")); } private void OnActionToggle(Entity gasTank, ref ToggleActionEvent args) @@ -93,28 +91,46 @@ public abstract class SharedGasTankSystem : EntitySystem args.Handled = true; } - private void OnGetAlternativeVerb(EntityUid uid, GasTankComponent component, GetVerbsEvent args) + private void OnGetAlternativeVerb(Entity entity, ref GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || args.Hands == null) return; + var user = args.User; args.Verbs.Add(new AlternativeVerb() { - Text = component.IsValveOpen ? Loc.GetString("comp-gas-tank-close-valve") : Loc.GetString("comp-gas-tank-open-valve"), + Text = entity.Comp.ReleaseValveOpen ? Loc.GetString("comp-gas-tank-close-valve") : Loc.GetString("comp-gas-tank-open-valve"), Act = () => { - component.IsValveOpen = !component.IsValveOpen; - _audio.PlayPredicted(component.ValveSound, uid, args.User); - Dirty(uid, component); + ToggleValve(entity, user: user); }, - Disabled = component.IsConnected, + Disabled = entity.Comp.IsConnected, }); } + /// > + public void ToggleValve(Entity entity, EntityUid? user = null) + { + ToggleValve(entity, !entity.Comp.ReleaseValveOpen, user); + } + + /// + /// Toggles the release valve for this open or closed + /// + /// Entity whose valve we're toggling + /// Whether we're opening or closing the valve + /// Optional user who is performing the action. + public void ToggleValve(Entity entity, bool open, EntityUid? user = null) + { + entity.Comp.ReleaseValveOpen = open; + Audio.PlayPredicted(entity.Comp.ValveSound, entity, user); + Dirty(entity); + } + public bool CanConnectToInternals(Entity ent) { TryGetInternalsComp(ent, out _, out var internalsComp, ent.Comp.User); - return internalsComp != null && internalsComp.BreathTools.Count != 0 && !ent.Comp.IsValveOpen; + return internalsComp != null && internalsComp.BreathTools.Count != 0 && !ent.Comp.ReleaseValveOpen; } public bool ConnectToInternals(Entity ent, EntityUid? user = null) @@ -141,8 +157,8 @@ public abstract class SharedGasTankSystem : EntitySystem if (!component.IsConnected) return false; - component.DisconnectStream = _audio.Stop(component.DisconnectStream); - component.ConnectStream = _audio.PlayPredicted(component.ConnectSound, owner, user)?.Entity; + component.DisconnectStream = Audio.Stop(component.DisconnectStream); + component.ConnectStream = Audio.PlayPredicted(component.ConnectSound, owner, user)?.Entity; UpdateUserInterface(ent); return true; } @@ -210,8 +226,8 @@ public abstract class SharedGasTankSystem : EntitySystem if (internalsUid != null && internalsComp != null) _internals.DisconnectTank((internalsUid.Value, internalsComp), forced: forced); - component.ConnectStream = _audio.Stop(component.ConnectStream); - component.DisconnectStream = _audio.PlayPredicted(component.DisconnectSound, owner, user)?.Entity; + component.ConnectStream = Audio.Stop(component.ConnectStream); + component.DisconnectStream = Audio.PlayPredicted(component.DisconnectSound, owner, user)?.Entity; UpdateUserInterface(ent); return true; } diff --git a/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs b/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs index 40d76684ee..aa1fcf6559 100644 --- a/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs +++ b/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs @@ -1,13 +1,12 @@ -using Content.Shared.Atmos.Piping.Binary.Components; +using Content.Shared.Atmos.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Guidebook; -using Robust.Shared.Audio; using Robust.Shared.GameStates; namespace Content.Shared.Atmos.Piping.Unary.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] -public sealed partial class GasCanisterComponent : Component, IGasMixtureHolder +public sealed partial class GasCanisterComponent : GasMaxPressureHolderComponent { [DataField("port")] public string PortName { get; set; } = "port"; @@ -21,38 +20,14 @@ public sealed partial class GasCanisterComponent : Component, IGasMixtureHolder [DataField] public ItemSlot GasTankSlot = new(); - [DataField("gasMixture")] - public GasMixture Air { get; set; } = new(); + [DataField] + public bool SafetyValveOpen; /// /// Last recorded pressure, for appearance-updating purposes. /// public float LastPressure = 0f; - /// - /// Minimum release pressure possible for the release valve. - /// - [DataField, AutoNetworkedField] - public float MinReleasePressure = Atmospherics.OneAtmosphere / 10; - - /// - /// Maximum release pressure possible for the release valve. - /// - [DataField, AutoNetworkedField] - public float MaxReleasePressure = Atmospherics.OneAtmosphere * 10; - - /// - /// Valve release pressure. - /// - [DataField, AutoNetworkedField] - public float ReleasePressure = Atmospherics.OneAtmosphere; - - /// - /// Whether the release valve is open on the canister. - /// - [DataField, AutoNetworkedField] - public bool ReleaseValve = false; - [GuidebookData] public float Volume => Air.Volume; } diff --git a/Content.Shared/Atmos/Piping/Unary/Systems/SharedGasCanisterSystem.cs b/Content.Shared/Atmos/Piping/Unary/Systems/SharedGasCanisterSystem.cs index a7562689ca..8fed3f0eb7 100644 --- a/Content.Shared/Atmos/Piping/Unary/Systems/SharedGasCanisterSystem.cs +++ b/Content.Shared/Atmos/Piping/Unary/Systems/SharedGasCanisterSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Administration.Logs; using Content.Shared.Atmos.Components; +using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; @@ -9,7 +10,7 @@ using GasCanisterComponent = Content.Shared.Atmos.Piping.Unary.Components.GasCan namespace Content.Shared.Atmos.Piping.Unary.Systems; -public abstract class SharedGasCanisterSystem : EntitySystem +public abstract class SharedGasCanisterSystem : GasMaxPressureSystem { [Dependency] protected readonly ISharedAdminLogManager AdminLogger = default!; [Dependency] private readonly ItemSlotsSystem _slots = default!; @@ -73,7 +74,7 @@ public abstract class SharedGasCanisterSystem : EntitySystem var item = canister.GasTankSlot.Item; _slots.TryEjectToHands(uid, canister.GasTankSlot, args.Actor, excludeUserAudio: true); - if (canister.ReleaseValve) + if (canister.ReleaseValveOpen) { AdminLogger.Add(LogType.CanisterTankEjected, LogImpact.High, $"Player {ToPrettyString(args.Actor):player} ejected tank {ToPrettyString(item):tank} from {ToPrettyString(uid):canister} while the valve was open, releasing [{GetContainedGasesString((uid, canister))}] to atmosphere"); } @@ -103,24 +104,35 @@ public abstract class SharedGasCanisterSystem : EntitySystem DirtyUI(uid, canister); } - private void OnCanisterChangeReleaseValve(EntityUid uid, GasCanisterComponent canister, GasCanisterChangeReleaseValveMessage args) + private void OnCanisterChangeReleaseValve(Entity entity, ref GasCanisterChangeReleaseValveMessage args) { // filling a jetpack with plasma is less important than filling a room with it - var impact = canister.GasTankSlot.HasItem ? LogImpact.Medium : LogImpact.High; + var impact = entity.Comp.GasTankSlot.HasItem ? LogImpact.Medium : LogImpact.High; var containedGasDict = new Dictionary(); var containedGasArray = Enum.GetValues(typeof(Gas)); for (var i = 0; i < containedGasArray.Length; i++) { - containedGasDict.Add((Gas)i, canister.Air[i]); + containedGasDict.Add((Gas)i, entity.Comp.Air[i]); } - AdminLogger.Add(LogType.CanisterValve, impact, $"{ToPrettyString(args.Actor):player} set the valve on {ToPrettyString(uid):canister} to {args.Valve:valveState} while it contained [{string.Join(", ", containedGasDict)}]"); + AdminLogger.Add(LogType.CanisterValve, impact, $"{ToPrettyString(args.Actor):player} set the valve on {ToPrettyString(entity):canister} to {args.Valve:valveState} while it contained [{string.Join(", ", containedGasDict)}]"); - canister.ReleaseValve = args.Valve; - Dirty(uid, canister); - DirtyUI(uid, canister); + ToggleValve(entity, args.Valve, args.Actor); + DirtyUI(entity); + } + + protected void ToggleValve(Entity entity, EntityUid? user = null) + { + ToggleValve(entity, !entity.Comp.ReleaseValveOpen, user); + } + + protected void ToggleValve(Entity entity, bool open, EntityUid? user = null) + { + entity.Comp.ReleaseValveOpen = open; + Audio.PlayPredicted(entity.Comp.ValveSound, entity, user); + Dirty(entity); } private void OnCanisterInsertAttempt(EntityUid uid, GasCanisterComponent component, ref ItemSlotInsertAttemptEvent args) @@ -129,7 +141,7 @@ public abstract class SharedGasCanisterSystem : EntitySystem return; // Could whitelist but we want to check if it's open so. - if (!TryComp(args.Item, out var gasTank) || gasTank.IsValveOpen) + if (!TryComp(args.Item, out var gasTank) || gasTank.ReleaseValveOpen) { args.Cancelled = true; } diff --git a/Content.Shared/CCVar/CCVars.Atmos.cs b/Content.Shared/CCVar/CCVars.Atmos.cs index 833938b765..ab4626cd57 100644 --- a/Content.Shared/CCVar/CCVars.Atmos.cs +++ b/Content.Shared/CCVar/CCVars.Atmos.cs @@ -158,7 +158,7 @@ public sealed partial class CCVars /// Setting this to zero disables the explosion but still allows the tank to burst and leak. /// 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/Destructible/SharedDestructibleSystem.cs b/Content.Shared/Destructible/SharedDestructibleSystem.cs index 4917ded326..076125bc8a 100644 --- a/Content.Shared/Destructible/SharedDestructibleSystem.cs +++ b/Content.Shared/Destructible/SharedDestructibleSystem.cs @@ -10,7 +10,7 @@ public abstract class SharedDestructibleSystem : EntitySystem /// /// Force entity to be destroyed and deleted. /// - public bool DestroyEntity(Entity owner) + public bool DestroyEntity(EntityUid owner) { var ev = new DestructionAttemptEvent(); RaiseLocalEvent(owner, ev); diff --git a/Resources/Locale/en-US/gases/gas-tank.ftl b/Resources/Locale/en-US/gases/gas-tank.ftl new file mode 100644 index 0000000000..03af8cb7a3 --- /dev/null +++ b/Resources/Locale/en-US/gases/gas-tank.ftl @@ -0,0 +1 @@ +gas-max-pressure-alert = The pressure relief valve bursts open! diff --git a/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml b/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml index 2bbcc3b13d..cfc35c1589 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,94 @@ 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.1805213564 + Tritium: 0.0902606782 + 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/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index d10c37392e..708cd59ce1 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -102,7 +102,7 @@ walkModifier: 0.9 sprintModifier: 0.9 - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 2 minutes of thrust volume: 0.75 diff --git a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml index 8ce9de0f3b..574e079fed 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml @@ -5,7 +5,17 @@ components: - type: Sprite sprite: Objects/Tanks/generic.rsi - state: icon + layers: + - state: icon + - state: integrity-unshaded-0 + map: [ "enum.MaxPressureVisualLayers.BaseUnshaded" ] + shader: unshaded + visible: false + - state: mask + map: [ "enum.MaxPressureVisualLayers.Base" ] + visible: false + - type: Appearance + - type: MaxPressureVisuals - type: Item size: Normal sprite: Objects/Tanks/generic.rsi @@ -25,16 +35,20 @@ interfaces: enum.SharedGasTankUiKey.Key: type: GasTankBoundUserInterface + - type: AtmosDevice + requireAnchored: false + joinSystem: true - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: # If gas tank volume is changed, adjust MinimumTritiumOxyburnEnergy in Atmospherics.cs by the same proportions volume: 5 temperature: 293.15 tankLowPressure: 30.0 - type: Explosive - explosionType: MicroBomb + explosionType: Minibomb # Mostly a pressure wave explosive so primarily blunt damage maxIntensity: 20 + tileBreakScale: 0.2 - type: MeleeWeapon wideAnimationRotation: 45 attackRate: 0.8 @@ -98,6 +112,7 @@ state: storage sprite: Objects/Tanks/emergency.rsi - type: GasTank + overpressure: 1519.875 # Poorly manufactured, they also lose pressure quickly so this makes exploding them easier, at the cost of the explosion being smaller. air: volume: 0.66 temperature: 293.15 @@ -231,7 +246,7 @@ description: Mixed anyone? It can hold 5 L of gas. components: - type: GasTank - outputPressure: 101.3 + releasePressure: 101.3 - type: entity parent: GasTankRoundBase @@ -244,7 +259,7 @@ - type: Item sprite: Objects/Tanks/anesthetic.rsi - type: GasTank - outputPressure: 30.4 + releasePressure: 30.4 - type: Clothing sprite: Objects/Tanks/anesthetic.rsi @@ -260,7 +275,7 @@ - type: Item sprite: Objects/Tanks/plasma.rsi - type: GasTank - outputPressure: 101.3 + releasePressure: 101.3 - type: Clothing sprite: Objects/Tanks/plasma.rsi slots: diff --git a/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml b/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml index 5a6bc352c4..1ba2d790fe 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 @@ -240,6 +241,11 @@ sprite: Objects/Tanks/Jetpacks/security.rsi - type: Clothing sprite: Objects/Tanks/Jetpacks/security.rsi + - type: GasTank + maxIntegrity: 5 # You may ask why this microbalance? The answer is: Sec jetpacks sprite is based off regular jetpacks, and I'm too lazy to make their sprite data different. + integrity: 5 + - type: MaxPressureVisuals + steps: 5 #Filled security - type: entity @@ -249,7 +255,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 4 minutes of thrust volume: 1.5 @@ -284,7 +290,7 @@ suffix: Filled components: - type: GasTank - outputPressure: 42.6 + releasePressure: 42.6 air: # 13 minutes of thrust volume: 5 @@ -301,7 +307,7 @@ suffix: Infinite components: - type: GasTank - outputPressure: 21.3 + releasePressure: 21.3 air: volume: 5 temperature: 293.15 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml index aba7531ef5..90c33838ca 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/teg.yml @@ -63,7 +63,7 @@ - type: AtmosDevice - type: TegGenerator thermalEfficiency: 0.1 - powerFactor: 1.75 + powerFactor: 2 - type: DeviceNetwork deviceNetId: AtmosDevices diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index 38ec6f8cc4..86b285c7a9 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: 25 # 5 times stronger than a gas tank + integrity: 25 + maxReleasePressure: 1013.25 + safetyPressure: 5066.25 + overpressure: 15198.75 # 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/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 0000000000000000000000000000000000000000..14c04d0ddde1af0c974c1830f4aaf824accbd69b GIT binary patch literal 5114 zcmeHKdsGu=77q^rgCJUH$yL%?v*F<XsiweD=i-BfXBcAM>-ix1e6>6K*?;81kZv3$5EHGK8qqx*(K zcdPb#t{oUwYy4gRXd{r9lUBU!Q4`Wj3isG&8~{f;x`w(gTb^*!^uKOwpWXN`rod*i zc~o?tKc`4Bc(T2Me_+t~3SMa2T$t6j>e6Q`J7(a!x&wV5hvgZxS1!xSv~Am(@`ytH zW0|;{qQV_9jm~#f-F)$&BmNNgJ4Z{mk*RBKGqgT?w3-1lK^bpvyy^=xr?h7eiW*;@ zid<-uYq{6I|?BA2+(ED85fVaBZ`(Ovbra)4hyL({8yuwf7 z4XQG~1cO*o1Nn?F*L+6-um~QP4T4XiMHej03KvYb`{$Z!+?vO8L`3*V;>yMb!aWH!`rm&kGy*INP@M?v3Qp=Eu{4jA1sNa((4mC z@7=t%Vf%@QO)cTUj3Y$}*Ai=HT%1+-*$ehbt4j^sR$i!UTWP(v(kV^Tr(Ig#_w%5g zr?4U;pC*2>*Xavh_T>wqaV4zu>$A*v9i3bA6Z#$88WG*^*PHX)cf>FOtvc%*vi;J| zz0lPsj+rUorH!+2UEfa4y_JJAXq!`a+9mGxdC7%>mh*VnZr_|b?>SUpoAV)(Cbsy6 z59&1ZgM4YxozAmO>!Y+6(`Ovp)R3N|y>Tme(To$d@`cZ>gbx4SW4iW9Tb~hcQR#~X zqqYNUrzTJ^MgZd@1NYgSi#D$DSmh0oYY_V%Q0 z;4>fZHLb(T)@n;)ic$8+$eSBmE9c&?KAS_kmfmUYHDa5QeQnCd9mdzoZ3?KIgTG`e z#08G&$*0P*>h?c4@Z*}8bHkZgtt+ksMu#}eUcY5qK$!UMjcx7UufK7kKQY#~Gb$}= zuW&T^Gi()r+pYpiUNzFlB@ z$_H^h{3uq@Lyo^(Sl!@(|9*<;I=5AyN0lWf%WaHLIRLK>?p1da=4Fsw4(ImYZJ7Rh zAE)oOflpmaSY4c9Ftatt`5vFtv|zCDmrYiNx%?ph?wuFhM4ez`UuW0j==`|U@)6~< z`BSzUcx_e}^yK*euPt+T=7Yub$C^pvWAvWmymBg7}_d~}p*GSFvOlOtK@ za4Y4p@zX^{5s~L&5)JFhtKHd=_sz^CMxVBS&bw_yQhn9w98H@W`0R@HdjGql`He2q zr`y&` zBqviz6tYA@p6sDS{ZlZK@qqr+Lm7tsOp-%jr7T$?fc;ZoDe5$tLMWK zGAxEA7*vVvN_}t3KrSzM!b2xP93qkHy)d!gL!yZ2omlV1rd!dcGdU2CxRf0PNY#FkVd2P1wxTfPbCzv17r#b zgq0JKKyffxE{)SK=mckb2Xi?D8j14G5-f&L5$1rM0Yoa4sg&=Q!Vn1@ib6U)sqXG{ z7M0}zGHFaEoym9yS_LbVSTE{WsT2}@EFWEA*jPH4w2-b-F#^2@mJQoi0Yj)v5hjy~ zIRsr$fX;JbnTH)HA%sGH5DH_Y6dH?7p|h#XFe-~pqq6A?BE_9enS_@K5mD;@LhFtX z;5k&svrgI9f#?N zX@xbYUMpb!c&S|8$E)IDT^%qgB85(TCmEAK9#596n;37i^(6n79-ex@q!EMpjoGl# zg-t^8#3&rs3ybxye2v%PuiOFvK5p_s`hJw_qg)@Pzz2apcGpL_K1hKN0)OnT|C?O+ zcMnsr6#ECH!XA|#F7d0!9+Peatdd9<)Y@!qw^F+jGtJ}rd55h&-P=O7z)_~1{PC%2@Nr@uXX}$&Q=V?!w?i8- c9f#Y?u76+>zPz4ciaFr8{>%No_leE?4-p@s;{X5v literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e00c0e7a558e110aa2547ee6a6cf11c2fdd70818 GIT binary patch literal 5237 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..aeae5c35d9ecb49aba5d075f805ed8e24752ca7e GIT binary patch literal 6270 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2e2d20d3975b15ac80cae0daca2c81e0b57b4ed8 GIT binary patch literal 5471 zcmeHKX;2f{77idF5fnwl1vCU1Ma-T+5(#VAWD5|Ype&uF1CeYdfdm}c6a)h(j-n#2 zAh^L(;)0GcDj>)>i0gBC0udEK1(y*;2Axho2kTAME34l8GgV15E7}F#}|MId5Q!?fJ#0O z228hs79aTMOyOMP=zEj9o(m%#% zFfQ~kPaZ1Fd!ozfCyR4}Db-D_PO}FESJw?pt#iv?H#82IpIPxt-5irXYlFhb3itba zo3iWg=tJbtH&0?`PZwUkOnUwCPJSt9huQQn=nqWnEcGS2?!+G;n7xyJCjl zHQV0Jtrs?JUhwwOdoaBV8AK{z!lZ7%)x5ql4dDV>DtW)9-|Wc&4q1^6#alaG_U4>$ zv1GfnJXq4EYTju^vZjcSndUZ}*x6FKM%>@kRr>>?xep8}f;XL1d3U{MnOrxKU-UFO zC&l*OG3`X=^5$Dun8Q{y7tDXfKo(3cTnKC4L){-}HlEI#$=D=atS^9{P7qvEqV0b$ zHO?>Frf1?)>~QqpWf-e9|NXV)=3bZb2!siaR@H({b0?^B*IRktD5uOnd|<`3L#(jN z*{Z3i{WsZLR(M`}v>+n!;E58CicB?d3doEsD~=Lbmk@>fZ}Dt2H)P%D=v6%@?(Zow z>TTM%w=AD3xRl2zNqN2bp!y0`XnT_dcf`ch$hSmneGq#8Xj*ElJ4$r(DZRz2*M^jT zsC_9NGw=B}JuIA}zcOjOdE29HHug^f3rji4Oivcp!$0|okGvrxZdZ}AaaM9Bnr?G? zj%FG${d_^4YPsZT484yay2_Z9X8P9RnB#;82Yo#UfV634?6fs5I4#R&M%LnACtG>t z?N>Hof)$5O=3L60dBl%Zx`&WY5iH$WY__-6vC>2R)+;Jtm;d}8-{>R4)R{rE7}YIg zUUB&F-B6!4llM05DFv-Ovl9m8)(y9#CT=|x_%YM59N}z|Tz{!0ZL&PE(91DUqnUUJ z)#Xl~CRRj&{p+%ZN(6nubrYf`xq_L2$yotE(C%oYjMRz@C4bvuRu25A`Ob3~`|8!% zYZs``Jak&i2@hqSdudMSEV!lIkxY2?o7DHMO+L=0^TVRv!rsPV!;o`ZEks$J%Z}ge z4z2&~X4C0qb7BI!miTn+oRzj|x^s7-sP@Bt{g?OLO?(YquQM-nFT8c;Q)Q%YmQNMX zWV?;!XVtOcVn@8qfp-3JSJGT2yFnUL+sWTT;*zc~&u0i!&|T!F0kR- zYnqQ=sytJhtz46*Y%F?~4MQ#x`!0G{$1Y_=beP>L^*ZR1L_0dEqpjOvm@q>V+kQY38-e!n zdAlg)EDWZX&G+(R_%Y0=sV^O5J<;u+qrd{yl zJ093qYV z^jwRSg*`dmXFXih{GQTxdG$vYIy7`QbQwi&d|&je=2vlPz{CkI1OfNPn+AY=>}I3X zN?o&eN{EH;)QGYlZM$nOrl{0WRq6rS;Dj}XLi_rpk}|(I6aR9Ar;m*#6R`C;?6W?P z_@asShh3qYH+uVWORRL^9wW<5UB;i*bUY|&Jm~N2Fx^-mb8;AG^n0IvRgN%y&i&+P z<6gQMEQxB0P1INKE_bFzJsLk=Xkhhko1k_Bv?4<@FB(7B_w|jb>%BXN^XnWYnYQZ> zCyK-`R+qZMB70X18j;E4_Sv_@yi+P-72;33`0w5s@SLRkTCNzyL8nFI5VL zrv@`Qsfip47wP5-cTrLy0wE{|5K5syB%>;6NF6T~y4RX9NQ4d|PoyE21~Cv`Vkw9q zqKRlM%3H}##v@(f2p1`rM-8L#D3WWl#AfUz4cnpq0pmg%8hd(9^l!=q19MC%%6v^$zQgAt6{3S_Jfi4{` z2LlQ~Aq15{v*Ny*($_zT@x?d}f!x3Feue&= zyABMYf`X`YF(*kIo2?)kUrKRFRRCs_yB!VOY3eUlEP(+YSLa}iK0t&!!oCpAg2vRtJj*82n z`iP}M09sDI5Qqmc5>dQvKr1-agW*p@;?X04>nscbAm>32&>rB6xMGFuFEEoY1jFTk zR!^L>Gr`DHi=+E83T4$uKP2$5p(PE;&`iX$;`&<%--rJ%6RRO}eMn9Jv- z{4cb2`ygCK7Tu38gXT}snMU?hIJk1;Y2;DB*KH;QLbolb0B0lx8ITNeb#X#mBSV}7 zKok!`{bRIXKim0#Qw(e}n@nIk6Hr7P1u6zSj)bB(6R{{K9*6~SI46)oB7V&-6Z7N> zKni-qLpnlQK?SPQ3Ne3lsT{t>D-uBMIzW=4pgJ8R84@#^EJoWgK4uJ3YvlLFra z{$5@GH@V<{y-a~3=pT>*dR1aRsyYw7W*M;qeCe=a?HgC6A^$3LF_HL1%3!bw7TT{K ztfX{0WHgrh2YDO!8ceb>G)aN$-G)rZ{plXerS&hi;NTarQ*%BY+K)Jk+hHEkcDl^7 zPZN6YXJtWF;ZEvVi#x{xCT`?PrcAZol+g3K&SCAOc3-8ryZJcV%z>Kz>KA*5wi?5{ zpT9g)9Rfz`!_K~odoqM+R|#7rNY99f@wV;`Nu}L`MP29Ad8_r1A5vT2ZZatL)Snw? zWjBK(3cBH*Xa?I>VN#FH*sik1Q1y&)IQQQW>K2uXI=pJpa$wI4j3-jszn(rO{h Wh-4*f_bG$ohxvO4)2lt>vi=04JT=k) literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/meta.json index 99437cf31d..ecd1f06fee 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/black.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455. Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -54,6 +54,24 @@ 0.2 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/blue.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..14c04d0ddde1af0c974c1830f4aaf824accbd69b GIT binary patch literal 5114 zcmeHKdsGu=77q^rgCJUH$yL%?v*F<XsiweD=i-BfXBcAM>-ix1e6>6K*?;81kZv3$5EHGK8qqx*(K zcdPb#t{oUwYy4gRXd{r9lUBU!Q4`Wj3isG&8~{f;x`w(gTb^*!^uKOwpWXN`rod*i zc~o?tKc`4Bc(T2Me_+t~3SMa2T$t6j>e6Q`J7(a!x&wV5hvgZxS1!xSv~Am(@`ytH zW0|;{qQV_9jm~#f-F)$&BmNNgJ4Z{mk*RBKGqgT?w3-1lK^bpvyy^=xr?h7eiW*;@ zid<-uYq{6I|?BA2+(ED85fVaBZ`(Ovbra)4hyL({8yuwf7 z4XQG~1cO*o1Nn?F*L+6-um~QP4T4XiMHej03KvYb`{$Z!+?vO8L`3*V;>yMb!aWH!`rm&kGy*INP@M?v3Qp=Eu{4jA1sNa((4mC z@7=t%Vf%@QO)cTUj3Y$}*Ai=HT%1+-*$ehbt4j^sR$i!UTWP(v(kV^Tr(Ig#_w%5g zr?4U;pC*2>*Xavh_T>wqaV4zu>$A*v9i3bA6Z#$88WG*^*PHX)cf>FOtvc%*vi;J| zz0lPsj+rUorH!+2UEfa4y_JJAXq!`a+9mGxdC7%>mh*VnZr_|b?>SUpoAV)(Cbsy6 z59&1ZgM4YxozAmO>!Y+6(`Ovp)R3N|y>Tme(To$d@`cZ>gbx4SW4iW9Tb~hcQR#~X zqqYNUrzTJ^MgZd@1NYgSi#D$DSmh0oYY_V%Q0 z;4>fZHLb(T)@n;)ic$8+$eSBmE9c&?KAS_kmfmUYHDa5QeQnCd9mdzoZ3?KIgTG`e z#08G&$*0P*>h?c4@Z*}8bHkZgtt+ksMu#}eUcY5qK$!UMjcx7UufK7kKQY#~Gb$}= zuW&T^Gi()r+pYpiUNzFlB@ z$_H^h{3uq@Lyo^(Sl!@(|9*<;I=5AyN0lWf%WaHLIRLK>?p1da=4Fsw4(ImYZJ7Rh zAE)oOflpmaSY4c9Ftatt`5vFtv|zCDmrYiNx%?ph?wuFhM4ez`UuW0j==`|U@)6~< z`BSzUcx_e}^yK*euPt+T=7Yub$C^pvWAvWmymBg7}_d~}p*GSFvOlOtK@ za4Y4p@zX^{5s~L&5)JFhtKHd=_sz^CMxVBS&bw_yQhn9w98H@W`0R@HdjGql`He2q zr`y&` zBqviz6tYA@p6sDS{ZlZK@qqr+Lm7tsOp-%jr7T$?fc;ZoDe5$tLMWK zGAxEA7*vVvN_}t3KrSzM!b2xP93qkHy)d!gL!yZ2omlV1rd!dcGdU2CxRf0PNY#FkVd2P1wxTfPbCzv17r#b zgq0JKKyffxE{)SK=mckb2Xi?D8j14G5-f&L5$1rM0Yoa4sg&=Q!Vn1@ib6U)sqXG{ z7M0}zGHFaEoym9yS_LbVSTE{WsT2}@EFWEA*jPH4w2-b-F#^2@mJQoi0Yj)v5hjy~ zIRsr$fX;JbnTH)HA%sGH5DH_Y6dH?7p|h#XFe-~pqq6A?BE_9enS_@K5mD;@LhFtX z;5k&svrgI9f#?N zX@xbYUMpb!c&S|8$E)IDT^%qgB85(TCmEAK9#596n;37i^(6n79-ex@q!EMpjoGl# zg-t^8#3&rs3ybxye2v%PuiOFvK5p_s`hJw_qg)@Pzz2apcGpL_K1hKN0)OnT|C?O+ zcMnsr6#ECH!XA|#F7d0!9+Peatdd9<)Y@!qw^F+jGtJ}rd55h&-P=O7z)_~1{PC%2@Nr@uXX}$&Q=V?!w?i8- c9f#Y?u76+>zPz4ciaFr8{>%No_leE?4-p@s;{X5v literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e00c0e7a558e110aa2547ee6a6cf11c2fdd70818 GIT binary patch literal 5237 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..aeae5c35d9ecb49aba5d075f805ed8e24752ca7e GIT binary patch literal 6270 zcmeHKc{Ei2`yWeWsgzKvk1-|dtd=p8-B>2Zz8AVPbBDn!W`-HsMQBm9Xi-U0J}SF} zP>GVINGhSClw@fkO8xF=KfmueztcIt?|;oX_s+e~@_Ikd>v`Vq^W0=N7l%3W>hdrc zY!2Cx*32N0JRp+2ykRo}QCwdW&aHWH*jqwQ+OeYT0On)<=ADyjC)V+eu?LQ< zX*uW`Q8?*x=HaXM*7gUY;{$yox|?bi<6Grg-7@ENwjLRIzbA6y(c>`U<#%DHh$G2k z(OK8mf0<&R`q-bjfVsJQN$L7ikMre(oZINq2zF_e4=|J$IFB;;p7U=0t2Kp|pHO}r ztroeSk;ie*7RS{{&z?F#c_EN=Ra>ACc;ifO%iF@mGLua%t**8RclW+CJ9{$U3|(q% zXkK5ERg)esv~Ou0Z;$M2t4tm>YpAgx+^ZbuO_|sjbF^^eT>ckCUgMp`$k?Vs#NO5B z=W~|bo0D?;RGxK7gu<6(WaFes^#T6M3k5?Z+eQSev97{-< zXiqfsvmI-0&HP|DO&pu0=zDgH*}Ng_hcgDlOS^`Q21*)l-YUGi%7kC_rZ0c;r2;}- zzU9GGi3%?!zusQa&gF!G)O_uW?(NvozK@?zmCGnOhkBLnXuPl``#-#vF1m7cdT;S z_(pL_y{6$_N0Pt9k}Smns`CfeWEG7;-Rwgijk(!J0tN~Fhdf&@(Y3Az*(B#=r#b1C zmb8VREzCI*o?BY>@F+*3%^7*IEiC-Juw`iIQA}lV=EM_+MUg8E-{_o5F`25{$jD$m z_h%eqjX(4C8*OOJJ6G{=u#2^p*>Ehn2kh*QR$EJnOVuG4EzE7pwsfxqrbnkG!iMde zHL?XjOjdoU?jB@D8`+}?)^zlRRh5pkX^C%BL=-mG&IMcCtgLc#Io+e_pl6*GhCQ&a zTsFEsI7yH9MTMPHhcBP(MT9J@&_GPgzu4-wrcwA79&LA0y+bBz&tza_e&D|D#CxBZ zPtUymf}hgHtLOE*UqAiu^qz#SbGn71!?xq0j0?!z{Hir4FTP#G?rYC|cX{>lO<_lJ z&kfB|TcO*LLGrjyUY(9xky2F@PrX-_(k()e-+n5n32mMT-W%_mNPaOlgMdbB86;Ba zsvgn3O3(ZbTfHQMtl0x?2DeUodory}^wJlM_c!Y~=Sc$lI-FE24W9Jxiz&2!`D(X$ zd*#Y6n9kfyVEbnorw88txd5uZY)}iO)LVHer2v=(Ecv3DwKE84tsPAP^I{TdG zZEQm6)S|+jltAjR2lmnMETwGFd8N#3Olx}JS>L~`UT-O z9%X?WS^`9^$?BsgGbXE5j-OY*bRm8dKYv(fg^tQD<*E#XjXhH@k$sI#c2be*UODcZWudLvr`oVxtKga98R?j!C4+lbzg~WOY)~OTEbqXv z)Ou-^#G0+O`>h@&z?)Lh7Awt!!RpC}&n~jKr>}U3<+V-Qs(ZHSB-x=ruotuWc$1O! zv118-?NT2t+$`l3`&VjlK1lU9;QWq~>>sEq>`JRhv-DZHu&&GqO$&-NkehVh`3bl) z#5)`*)ewZPjil&4*DB2>0j{dA?zhqGHOcXPNz%*XRGbqNZd%WCh`(Itz3Y~0y~b$4 z=j4ve!V2}U$f(Lsh`iwJKDE!|3aX#>52N7VCBDC4F6f& z>8NQ2x8hB~O{4qAGYPwJ$+vFoka=1<9MSQ9HKAVfNrFk+Kh@h@g4^O^3Np7p)0dCi z9Jr--Rl0$E{t~7}S)OO*hrJHVhxO(Qt%b^?HgV>6PXe(Xx=Au>VX7$4{W{rQ4{}5N zkcoyjiz5ax^6%SHzK_cSAu?r>yKKD*Rn=XX3h%qPnWN$*|hQVbmb@xQiXE2yV0@K#kjcjZCeZqj|ij0k^HBQ&8mR=9`+=)0a*GDPxhTAHCC6&ni zda74!nEEAy>v%8Gp>2D!%T47k%#z(~;InLPMpP76Mf!>c{IkT<>=y{FO=#Vcq_M~4 z^G1fvhkGP!iW|L(LnPgdZ#il|rJ`!|#;dRHQIbreI#P3vHyJRV5UPit^t}q)6%upe zBVSQx);sr*2Q;zH8$k}ik`(-L# zfTn$({f=a#*A3>MmZwfNY7CkX34?{4FUOe^6H*KlURGn1%sOZkjAlY3 zpC84UNaL_j0G&exQ6d%>8VO-AGfNQ{poN11I28$6>m={gm`@-okL^Ni8GG`hA|#s7}Ai21R4!##2^4j zDjsKyG{s_YSYwO{&D0qC3ly2n7XWM;D29T-QA`L2N5$hAbOsK|Ffqa*jc@=BX$pZN zsQ>^1R1BS9YDoJ9!j;E_x)O-^H7YR_9fC5X6QFeoG$a9s1Cd5ps*yMlBP3vq#WO%G z7Kg!^%s|m;M0*a81wiFwvVaf}&1HwoD8z<=-7x2pkIYQ{ol@2pEt9vC_~(gMqC&olnz8KAnsHM zU`7sQL$u|AfPlmE;&39&5#pfWV$UCH3bdi+4nrUWc%XF%#3RHN)Sxq5!F9fsO856@grT6g4iIHXj3M$D$`I&p$)d$4 z#`kQ^(Ep1Mvl)Y5ju^=As|-52ppy{&<0$;b7ZmG%^Y^U||IH=f@V|@v5x;-w`b*az zG4Mymf4l21U4O*D9~uAcuKzc>(h^t$l+U>?)LYBphtH>uC3R+g5|m&YNtuZ&9+LE7eLKN}ro~ zgWOZ%WJY>n@rcp^`m#5&Ou7%YcXITdJI?2NPFW5!ID$CfQ?vU`~2o-r|I%nZhol1gMLB`uajXdz6t z79L9rT9Kqk$dV*aNqVC99`&@G_jk^FI_LfU*PL_Dec#ve`CixOy1w6Y-AOJ^Hp``z zr6CZ=aywfqH}DP>UQ!a^KQ}u19Rwm(AMNhNb0bAU*&G&)5duJYQEUJT@EJ4+gx@=P zEI2|>Pj>HTOtPxQv|Rf0H-6{o_nW7jUEU|$45p?^qic9Ofh)4goC z*{bfD%4Ai+o|uJCxChrNJrBH#e!4m{1~%oCcul*6?(3;f*zwfjWU)$m&hT5-JN02t zf!PjcK1*tXKU!3?FxVLrDf8x|TIxkVzG>+i2_LoDaKh|VEBlsbTy(v(BHYk0mM|us zIhr~Du|CR1uQq1?PA(_5I-no#(NFxuRhbQYad1j+Hd?LlNyXTme?B&J-wK{O?%yRB z7Jd~!)6-T@UVm83pSW(z0nfpJW3b7{=Q*VCMCqZI7kX+D-6D@F8)bbbE4ej&5nE(J zm9uKuWt$pUv^>ACKD{{eXAyN``y3Tj#tc!;)~_&gm4^9hx4%&C08*KaV|vCtbYo5{b9nXk}N|j=`+>8Ir9V^&5@{;qAI(yde z^ZB9TNwd}SX4>1;uOH3I^z&pc=}oD(;GC~9^hNKutr_0py~-0)P1DXk>ON|__d{!k zX6l%tyoT-zo#3RrN;m(rrS}K-w`*fQdPg+1fW7_VB+-Y(0)&wzr>d%Q^E@vbf^qfhTUxm|k*+GU;r=x_T{(iWH#pv3xK*uOZ%B zZA3-jkl}yfnR2)w>#>)PtfRc?jH0J+slI%LZFkP?yUNwoY2`1r@AY|s*>nP7YsRaK|PMJ~U%UlqN9a-sQh4`0k` zW>T%$?={IldLd)qz=f3(@q{R9;+>2Br4iK+N2jXKBrL63n(lLNnN4MHTDE!3vuVHf z4)#booAzmAs@Jv~H!aFncC|lVzlq(0bKGFq&0708=E)tk)X!S-Ba{0c={$2aDu}Lk z*D3IrzI%|JoHJdq>V@2RFS8XhN{l8^ocmRKc479h;63`ZmB!*Zw*BhMa>p1 zZ&G`jyS6mp*3t@dbk<*diSd*68<#ISSo5MP17CXgb1GD~mM-ZA$jIe*zy((4y` zPFy5r5UI9I!M#TNsO{mEg^o~5318_k+@TmIDZz9#DR?YR;`uAe`2uL`#tiwtyV_N& zC-Xy!JnZ4)ANA3n_D9Ufiw4Vy*q0ooIjYn%AKa_i5|Tf%HZ``($72#7Tc6+EH(_Zc zo|%SPXZ`2uXhnG2;9aX;mm`O?UL~WSbx++00K~2sDg~5h?lQ7jSDpTjp1`W!I6#UC z$9S!Xh;z}`dXkLzY(x3x?dP= ztS?AjqTdmAZS%~gGWwNuvRO$fF*#iWJ{QE^C|XY#b-mKB-f`N1X?ILv_m#AcjF3-m ziPe+$FU=FW^5*#avE?EA;N~*p{H%IF$~*qB?8CP@hsY75=N)rOx(+$2%+k(eY}k-` zwr6+DPmQ7{Fy8lWmTq_5dUbFu&anM~JjS4P#IooO zF~PtrJaHmZwrjWUBy@yd-hOSSgpHJUBtXf zhqRBqBc9N@twFOyCik!OP2&(orQL@Ut?!eb78F++6hoV2OvO(Dx@q@W9IFor${hZ|b2}g%&Z$;AU(#iB zk#Hj@s!VD5Iqj!ySm_4^pS0s2XHHcvOFS=m+`P{JlfWft#_;23kKnSMS{-Ve5*IQS z0wmvtLgX6OSU?~mdl{CNE_Rld|4uUC{F1%nP=iDBAM39NY&`(Wl=YAcyXCUUTTUVD z$VSCR3x?jMmw}vdWKhS!!dfHgGI7bB`W`BN+2P@A1+hjo=z_>#;TTLa4yk)-|MWoZ zimBHoulq$T1a0mDx~Pj`yRG&h8PTxueSK59oalbCEx9Q7wm$7Sq5k#rp$Y#KdQ{#& zTp1nl!y=}K_-W(&tgo6I3mD_svq{Yt^mSWCTZSb34}Lf_QQ6MQc930Wj0vH3zHK3q zE4r?8)(9%|S2?e?k>6GjyJ57lDe8d0r&RFXWNul4I8(DZ{6v9m;8MFnsKsU#1$WYh z&F42KCt)XVntW1EGqXk<_ftS|^iN4BWDH4uPI%UL;`&)Tysna@D6)DUE%ENPXz4!Y zPPHcy6Jl@7mU#Ny3P0pf&q?} zUXELc6c!UfqO!;Ug3n}wqbCGnY{qAkD8T>^N(ShRP!rhHl?E7;K{bJS8akpJ*_J>M z!#0`&Y>jqur$h%+2vnGvskAYl2m&wx9tp~4hJ;L?cj0CKLIi2bX6Z0fKxD=s$XJ-N6YR=>~9F;T#HJ9RY;$)PIDa zQoi}K!#N>~=};+1AOv87rd)7U^iNCL*g3j<^AJ)%XE51|ULe^&S@IaP?_~WHn^3cu z&X0kB?%!~Kvi>*sMPty)(UEAyqJ#^>v$HaR3D+l5Sri79xcG=869@nfAj1h%8Uc>Q z8sXt2JPixS;3zl(3WuhkNJc+E*@bd>q)-YVgaW}43=jv0!&3+}3IR?sB;(*%6afz> z&Z`Y(=2-J6pO9+Wa10BFU zzzC(XBDvpH?hGcdl}8ftiN@nGI5ZBA#iFoA1On!}kq5xxg1sn2MWYZHgGG(7FhnpN zkXn+kQ$c`5IhYO6k^_) z#u3p3@EMN66H!0dv#1PO)c<8I+&)m_FGaUyaKZJX7DZq7)K(zu%iEXN5XRzWfTOz zR5(BeFh(>2iA+En{*2CL(Rh(04q#3Pc?7uv8+4H?sLt0?>HcgV83YLH08$1AY5GnX z7KZ$qEK+!4{F|*Y@_+GRylC*l5d->tk%31ScoHJN9fe={5*|AL#oyOD{1;aMp?@#( zOZ@((>o;A$#K12Z|L(5ebo~+ozhwNoyZ+zklK%cN1%!eVP$c-NLL}QXzjhElV1FJuV-6z>}zj+=*+C&#tRo zA0Ns=&WuSM5!X#$ep*bs^&+cL$-)tGcBxFU&9-!D(#z4QI@Jd8)Kkj$ukN2Nk&ykX zs3_~&(i7Blr>dQekrh>jAsO*Ip0O`!c9x*Un^6lVwI>m&$Bv8ysDX(`PD+OO|i*LQVY-}hg0UGKc_doRD|zJJgC-0$-w(_J0ZRhOzl zAP{vYM_Ui@4wqfZO5ndZDryV@f#pVd`AR$(k#M0%z~P1gaLINd00*R84g@0o>$UIZ z%l*b0GeJ5ug?O#FE{JvLxLU~M?+GW{40}nFn|~^O7mWATh$2qr3`5aVASxbu?+ibLr zB(O34V{$5qM^!FxTjjj(s=aqC?4$>EH#8(9n2`q)HlVg_Nb9z`IL}GrriX3rhJyk5 zFX~FY@=GuOwK-&W_8;d7+|tsAYhMhcwvBANv#Y~lDPgn|Q=wRAK?$c^;2CX~qHx8kUSw`Kv3@}0b>ftpXOnekZrv|c_jMX-%w0Mu zwr-l|vy00w5XK%4>FiACCH}@s3w(PD)uK>H&zMp+Q1Y>`8OV{*L71%-tk^Y0nDJ-`!=(mNBm=Xnlv=ImU}IEhe2Vp?^@?W z{*9M?hl(B}o{VbQ2cK+t7pG-*?6P~yQvD&-BRRqGDQdT0#;iEqp1!7r=$-Ye`teHm zlE+c-iI8JjEpFaLsrLxwm3XZSgUxr7L$6>N?Yy(Y8nKmFFO3T=PnmhbD}O&cM9%VZ zd#FC16oCBAWo`NWS4U^GdeA4^9-C%88A85Ub^qP%IKg7f^iQQv@r=piAxHLWaLNlG zYn#j?ky+`jd8L$oZm}?}uon7icgG#zMeaPe zZl;zSnWni57qAkB+=Bmoupx+uj{e*BK}E!9@6t_)Z;K!%u_Z=^kuiOqQHTDR&M=fWd8=(mZMWpZ4 zu)4-Y0qVrX;X+-iPuqocC3Mcf#XY2hRW+ zD^9Y^_F|(&msSY`{ug_C8uOD}96Rd~RWF8qQXCDwsf2@;H48J?Q~ji;qd9Y8;rfYSNW-fv&FHkt-9=@&tX~YnGCHU0?@rq7 zXw|=_ls&W{LGwh0cI_0@SpLGX<_lF>z8Hk2cd1>G?Ti$=?tc8poA{AxAJ<76y{dKN zwpJX(8Rq%|?%*X$X1*1*47wsNx!~QSCFkyIVPlU_4%%BBEm29N|MB}$(UAoeCE*a# z)3tbk!i}aD6*w7-*b{GN+m-c*#p zx{3~8Ucs@(?7rvvATBXe4jwSfs>~lamN``Qo2etp9J8oxz6zzCi!>bVzjSi@T@yq3Opla_AyA=8Cb)?BlazCh6{bZ~QOZkf4ip(1SO z8gIQV84(deU4^QZ@b_}vxi1lhKch`bl14hqG=~OhgB^0##dTiALGpBBy`xb#6IE&Y z_WI5JTJlLuM`m7T!x~N(<@#V(PrrXk(Dtmq#cIZi2j$kKN%y@z3w#*6{!)%>E`ym z#eV0D-_oY$#Vhg*t0U5L90OIHa^cqYdb(bWRrUq;dy)yS@6e|8_gUJZj&IS$h}L8) z=^p5T&Bi}%PrsG#L^fFrlSf~k!77an%AZfvv&XJ;Cgr+`rdo&mOekOQIwoTsWK&+%zoCE%eLYylHMNqItWg9U+DSW1Nq zRwy8WGl3v3pN1H^T8V&j*))U?(FNlov;l&-j!`1OGs@MA6&1>&uo0H)R4t@b5P%0r z7;q^sj4!52X^1&oD)=lDqY>~q6G~C3}9XyxKUwH2@;WZ^m4 z(h#!oscZp@%cjmf;#foihQcNyi6jOYNnm4GNOOQlMB*q+3K2_SGbjM(D<~(vSi<15 z02verj^ctiI5wWb!cvGxEQdrw5^xwIl0pH$KECgVw15}rsT5HRNE z_%BA@fJhA1q6`&_LE&+88rj59!E``s8L~o+;UIIZF4Iv8(F7y1X zb^#X@n;~J?G9&;9ioubn7(5kA^um&;SR55kKw`*L%vbgTHkY&gzgf$c58UFzq&srO z;P~6;L?70aClLPO?ZayrcWyDk;d9G^%3ys+LCn|&u;=0gu|B9+!3=&70QQfM1^Y?P z{fA;;kZ=SX5z9c5@Ej(RKqdl6CYFpvGFVI|mPjHJ$rQ@B=wbm!63Gw&RzV<-AXi|4 z&T$1d{y0@8-`Yn81G0GlDMMoL$S;%;5$KP}qGcW9Q??f9|KP)7&fu#S1NwcCfvpSd zgy_$$@FQQcrt^RNeVm8?;|L)1k3qhR-yd}SpzFIB_%7og)%Am}?_%J)jDJ+u|BNox zFNZ0B5AJ{>!K0G-&#Mx_W0n%r*})caM0VmjqsYAn-c*E+o5T>vJZ;$}2T9LZ3<_Zq zCl@pb^=8QoNTSUe5+Z3ixqPMbyDOJRkP0LXJ_3w37z2GWFrikQsZ}5 zctLJs)AiU`_oDQ)824|ZPKH7N literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..979cb6add836e3e27ff517466879dbeef388161d GIT binary patch literal 6114 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e00c0e7a558e110aa2547ee6a6cf11c2fdd70818 GIT binary patch literal 5237 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..aeae5c35d9ecb49aba5d075f805ed8e24752ca7e GIT binary patch literal 6270 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..908cd70ce97060c4893790348daa47446797dc4e GIT binary patch literal 6110 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/meta.json index bc260937b3..79fea60158 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/captain.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455. Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -80,6 +80,24 @@ 0.2 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..4171a36f1e3cc90fa11c60c0e0847f0eff552e64 GIT binary patch literal 5370 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..96fef17086357267d7967f6b10cd95d56abb5952 GIT binary patch literal 5578 zcmeHKX;2f{77n{cL{tzrAO=Mdvj<2r0f8hyga{%cA|Rcl6Cz|G2_!Hg;5N7og1E!5 zIJgnfC%CZ7Dx;`?IQmeKO+W<&S5(y33F=_Isd{DAn}4RN)7|HO=ey^8_uSLn+kKY1 zPlnHi!(gzlYyR1r!L0RLm1ZfCzbv1Vn&J0S^XK zwhpddo7!z}INE0;)t!o%@SLKLpE&gwZ~Zi-yL01aY)#nP(Y4R*KG|D5)4y>n zd!eP_zR0%zWQX;|0|yq;?pz(Ph=~u`_83fhR5}%-Gidmv{(S!x<9C#MF*|=xujn}7 zXVLcY<+{Xn6IC^Rr5u@!K(kWlMn2J}}m8HMD ze6`GMUGgvd3)6yEj!f*?AYS+DFN?e#M#_!ig2?;w+>^V@(oapgGX330!janOhNPTa z!xJw|TTp3j)uq2K*zjmtN1&6*(!_~v{Mt2HkF{(XICIVWlI*7SpF7yK3l$htVjg+9 zmTtQzhek|#6AI)m+niC+!zcP;$wdj*{w47CQmpF=xnN;6a2xLb+tigdRRJ|t;>^xA zg9fT8n|jsUc7)LrT0NP6hZh$M`xEKeFzkceJ=J-JaZ3j!GRH_iX9A1F+@8W%wsnJmniJ{0 zV4k7DuA>JNC%SC3x#W_cs7lj)*W{O#ZM}p^KX=09WHY`mZG~w853_@{W~EP)>J%gI zLe&COo1!qA-2MJ1@jH&S1fAa>7#e7{|S}M$QwL)-R020l$x&H zCC&?v-nfq$oa~Hva+|{`O4`!6?lvB=|~@wXk=UY1zIvv5SN6Z*Dm} zuf%`5-uu1FCl1`A@Q^1vp3dYe?~WFg>Z}?$aQ0&q&0%@f&6L#3p2?FKcS=qaX9kvr zC0?8W(^5@6lk?{kyR=!scPo<%eeW5mZg{S%?2T9jAIgK@*Y17lPgLaDtjQc|Xw8p3 zKiYV5h}1E#-=?u8pnzfGjES_4%YN+w4|XA+LinAxuZVkO)9PcM^_#EDhb_Nc*R3@P z?%zHm>do|@s&K_ch41$77YVL=29|GJ>*BsK+FsORT{X4aNRg0{yX8{ztNXi}9xr** zXPgvo{&0~kSQ-OM)=sn{pK3M9j|-0yot|CTx-(HK-e(Z-!8C-twtE!ZinxD{-qbkm9$sCx?Fqs^@uhzda5X8Tj7;voXgVJb82wLe;`CwTH3jV6|u21H?%@0r9 zMVT_(|NY=-GA;<-F+1?^$Ahwr*ovSdciXPqNMgnF64T?Zy>?8$h7);RzwX!WweOax z_wUMOHL!OV&9Td*C#s*4Dzw#S8A^uE)rC z4yq-`NQ<l-KolT>Zd zy}_6DFnR^IgD)%gC~?YVPjo^{mi{!dmPKps+ zzt_A;b=^60%G0M;WY1nm-dG@ri@oehthH7Z?DC^p^JIDK4%3Z>&RJ2e>|?)qV0azm z@~55M?}NASWA1z)ze_9q1D5GA7c2&*ij8lI0x5lQUBL?AN)`7+4WR~L++MV8Qry-Yh|vP-KC zbKwIHt{a`mYr9a9EG&u99?49!Jw15FKB36F`H2v{gpL+KPvQWU7lk7hq5-a$4WgAo3G}3f!Kh1>5`Ys9$`NdkFAzB*pOsV~5dy9w zaa9*&LMM=OP%0UB?Tf7f^q<%6h??-6s03l!%Km#)n*J5 zp@GQ59gzVn9|TP-1rbCv5sgJLl!9nH(g}{BO1V6WFWv1k1$5_#43oY5fSC{1mR3Bj8drmuxnHWJd&eD3TqQ zfFhE(Bov#&A)-iR0>B}0$XL7`c^nl}B$ERo4ydMr$k76bhsPrj$v7?tWse7WC?Xb* zMUk*PE(!+_01}ss2g!KiI0|p60IEtLVtiC;DlS9?f?RtZ7Mh+2knB-J9!NluNkle^ zO(OF^4#>tk5V0C6E{EbKmI?uAIR!$14`L)DzGgrzIK{<>>4?Om$DkUEPXr+6K@QL! z5Qw;9h3qe|pFjw%lmlu#aSjdy2ON=LPqZi5IXH|(t?~B zr2|O|s4EpB(Ac4DC^RVu$i-4Wu{gpJsir`vJ--gKpbfm@DAL{4cb6`yi-ei|!$iLG#CGOk;a$B^Wt&H+CB#&}=3ILbEL> z0B0-(84wL}HE}{*V?&%UK*R^3{_(kBzt{zTQw%r`nTThTuqXmZg6hc*i$}4sAb`T! zlZhk}4^Jim;J55DF;A`lq@W8Q(h<@MDo~A9h(({5%H~_VA`Dco10)#=OF)g242k)i zEJocizGO?q{Ffe74Pac0f&9j7Q0sy^A?9l<{H&L{>HLGg&+G6H&Hz#WnB=?k{UO&6 zxxPz*?*jj*t{-xJmjd4f{!v~3H@V<{eN2HO=nY5#eJU|dPV0s~v-H`X?sV9G^%vK1 zJ;6=rG*RNQMh1gTnyx;yV29IZLPi5Qlf^K2tTR=6wr=QEd=+H!WYS&y0;&fauTO>< z8*gdKUf8sE)64lgx_bIC9&^hT?HT`8>B27G%<&81-)+iy!FN}24>qy)?lqwl6g*$E kZB0y@=0Yr<8t^fc4 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..05118ceae9de2a3db0a019efe1ec7880e2fc9f6b GIT binary patch literal 6444 zcmeHKcT`i^)(;>cNG~d42|-axNTq zD8EJ?1Oh3N>!$;^w2?6u?TqY}s0Tzbw7+^*yiwOdS zJ{?aBOdTswJn|Wppm6B=UNc;0qXP2df!m^)sVu>Q-sj|ZBZsdbmX8-hTbQ3lv!8v8 zaek|lnRb3Kqx7YEU{Q68{_Milsqr9O-_XE>-N!{<-_7-*9O#ksI`Z(Xx4Bw6C{x ztkhrBLcP~M-`iGt>M?C?3Sn2pYRCIta}JI=Lj``)Udnm!@x6DQYOucBFsZ6Y$1OOh=HhTe zmA0H{O+mqkaH12Uv|U}Zfkt<0y69eu zhZ_$)7AcGF?TItse^%ve>zD2R*hhS@MQBBMW60^tF~KMvi+Q4XWW+-|&N8c{^#Chl ztorJ!lJy_2e)>%K+#J7fvirU$|7y=dtchT3OVsgd?-FLyHTTRg&z$T03C^w88`7@@sSk~d^D;-qs7j>~ zX;QXYwa zIc5n`_eNOXBrrYvafTntDoyW{_72kqo}u4AvZ&KeHWBTL4!AUbp~?#0`LVUP3 zGUI{&ri=H~b^56TB(PesQgmjj-@y6yb>VmS*lIbh4_=UoPySGQUdh5;OA%$%UAt22 z5?<&SJ(mHiNNDvmuGqIMpth`~J}eWx!lp%9Jxlh6u4=%3PQi-Zp+hrU$^r);>`AKq zNYeNiU(+1VI(9eK^hmQZDY$8Or{DR(%g#p}jE>vuq^qKh4c_1H@bc}XyqGnL~gSH8ALVLl=0 zsBXzYok*eRhRLotU-upA`OZOPq~LhVd68L}1W%=`LvdgJFIn2tJF2kZo~bivVFY8L zBRvMb8NZ@V`PLQZ+mQ}nyRDw=sa5&cO!>}1-nQG~(qO&$oq65$_*gUgKz&DDb&P04E7*9qHi4(O~S#ekk*X;JZ)v+!uABHi%mpxxRREG`C&>|7Ib zba+_S?qz%LmNoOYY+BDrb!odk{aq(MKLk-8-w{2h3-8pil6-VC(x`Hc#rl_gp-R}} zWr=N@Dit*muWkKeFDYIy>y0{{6{WqHTk?51!g+{R=w7ukuRe1bv~eW4*G$^_&s0HC z*ru?=*RQR6Iz)A%>VeeAs=LLSo-7XO2C=CtoP_ri=|-+wnlH*{UG?NWFt+AyYKt>U%#Q-7`2hI!hRz zB){-E4CdA51f_3ns%rAZQPyn#^r`B`zVz1T4zA{xe1`@Llnz0EeSNneNedqjS2F!g zQL|`!_s&Cclkrz#sbjZKyZkoch&#J4<6x4K1+#D&hPeG+LEdBf8ND;|ccXbzJK>U1 z<_2XJ503p3GUUPX{k?p?P1xb4Hc7w~7ibfLD^qT17H&*PsjI`8rMHhr>|-BnJX_vR z4tj6-vi#ga%%NXb8&rk4_Z@o|5MS=_=blre-sY8W61TO33uGd&-XUe%Ws^+L+u1MY zZ$6Qvj9e^Tv45Ga*)#pEY6VYbgg&G8YnGEyPBy2uuWPIQ&3~Ic4zr`Iw#*4}-)l#X z${%XU+h!~Mp`d79p=2J3e8X^`A>f_%A8&2uh+wJv!g_^DG z9L29wQ{}fw&%fIxm7suE?OK6Fy`44>4LIjIeAV(?{fQQX+~x2Seuw_0nkLaA+t~5Y zl~|Z4$jLtnZwh7D)#=NZTURC5N%*$Z-B5 zNuQH_nUle*3{R4)a5a@dziZ9BH1@H|E9!AZcJf!@XUWI1a zyZNn^?mQf`;i#wVD*s*U{G_elO4VB7PiyQtU;94U=ICnWH*e|$6H~nR>%nLPM@*%j zglB1@2TqSED5O5uj8ed#-ETv^m}L)RJ~yv^^J3t`u2}!D&QInGCvP@@vhCM0Dk!R4 zm4_T(>`-LCiXlmU(tPrU^{S0ggB4@%w1>{!k$kA2mEML6j`J}RUGBbBM9-^5)l!J< zTlUmf6!Cidi@f#~?RQ?hy?6IclHkm%<^4W3lBBpKS6eMjpSVYtPEaAlA%ybh%LC5I z4tm~NiECHn*&G?1yY=~b2)Ft6^vO((M2Q|)*(ZejGSMMsPg%`zc4e64T=pTu;_(ub zgXPYxqipDbWz$@#Rh?{duat4f!JN&zkQ?pZJ=Tzye*P)3T3=S7SF$RQ)8{*6770SO z6~5Gd{#<3Xq1$XlU1bFJ!z`Cv7;QyX;-^8`;4z?$6|sPJ)raherE%HD6grp6Fb-w& zfOZxH!dZp#D6~L^5KLwGvp5!ziOL!Xm_@gMcw@+LGS8L~z_JhLGu*?SJZRy8G;=z{ z%2FN|iUk1J3?T&^$`0ZPu%Q-^C0s1+KnsWu*#&IN5At52gAt+-m-yeoBH#djDkuW3@3P?Z&VH_bP z6v`3kiXpyW5Eud)pT!fhxE!z;lS1Y07g|6dz&iLZ``A1(`5Qb(@RbFC4_GLL2SXUc zVQe<+dkuk*C;~vf8uV{91Rg-w4|8J(xcm7u22sS|2z9@QpwqtT^Y-(DmfWG!V2mIJ z8xR!$yCQzr(vC!S`KBSJz@NqDEolK{{~#%3G5;p(2iwFeOYVGc2%!EA_Xp{}d|wg< zq{w6}flJ#jHjhNGfQa|U(z!Gi9lP|&Fr!j27$gdcHbY~eXoLwGYKEYop(r@boQgy< zC>Rv_J17!IAf#|;3^5b{Zp;F3FjP1SO*1utqRilED4L3bLn$<>DbyTIhf_?@CMIwU z<2wjvJ`2c7O3?RKiJ|BK6rG7QMVcc_p-dALV3G+9K!GDop>PHjgP7A$Ar1@{a0j54BF|-JT@?_77W{`f{e+8NpvhUxq=frNaaUFC7akh4#e-0Y${1FWCuT zeOaOfP&ob!pniN!*uUmk|1KHObP5UyH-*xuW=J5ZkW{D{ok@iv(PjuBwr~>$-0Vkm z0hcKZq3{{j{s4~vS3rU;aRuJ;HB|aP%7+9n#Bl&9gThhJzbP|=z`i;Q6IYDCyv4!( zA0N0Sf$vHTp!a1CC|y7$gncW8U-=Rjo&VwQYaITEI{?r>H~B~W{-o?nk7SZup@v{#W${8Y1TvFCC9Vh zApn6^sEZ#7P{!%iz@)5@L?+6PNU2I{VH18iPykHDkqFivK6l1XTFV~+Jp5;tHpuEYGyzN_Jk?j-q6jgkD0_is%StR_ECefPV^ nVBUg4pF$30u1C!#>Y+rrooA0W;Z!AH6Np4~B3!cBd-(qV#wt;B literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b6f305736acfe738c3118a1de515c8c502b87d5a GIT binary patch literal 6749 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 TE#F%}svtUAhML71=R*Dm0?yCL literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d8f296bd8bc8dcac4414bfb66c68d805aada6370 GIT binary patch literal 5742 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/meta.json index b2f8b619d5..756d7257dd 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/mini.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, inhand-left and inhand-right by SlamBamActionman (Github).", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, inhand-left and inhand-right by SlamBamActionman (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -89,6 +89,24 @@ 0.1 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..14c04d0ddde1af0c974c1830f4aaf824accbd69b GIT binary patch literal 5114 zcmeHKdsGu=77q^rgCJUH$yL%?v*F<XsiweD=i-BfXBcAM>-ix1e6>6K*?;81kZv3$5EHGK8qqx*(K zcdPb#t{oUwYy4gRXd{r9lUBU!Q4`Wj3isG&8~{f;x`w(gTb^*!^uKOwpWXN`rod*i zc~o?tKc`4Bc(T2Me_+t~3SMa2T$t6j>e6Q`J7(a!x&wV5hvgZxS1!xSv~Am(@`ytH zW0|;{qQV_9jm~#f-F)$&BmNNgJ4Z{mk*RBKGqgT?w3-1lK^bpvyy^=xr?h7eiW*;@ zid<-uYq{6I|?BA2+(ED85fVaBZ`(Ovbra)4hyL({8yuwf7 z4XQG~1cO*o1Nn?F*L+6-um~QP4T4XiMHej03KvYb`{$Z!+?vO8L`3*V;>yMb!aWH!`rm&kGy*INP@M?v3Qp=Eu{4jA1sNa((4mC z@7=t%Vf%@QO)cTUj3Y$}*Ai=HT%1+-*$ehbt4j^sR$i!UTWP(v(kV^Tr(Ig#_w%5g zr?4U;pC*2>*Xavh_T>wqaV4zu>$A*v9i3bA6Z#$88WG*^*PHX)cf>FOtvc%*vi;J| zz0lPsj+rUorH!+2UEfa4y_JJAXq!`a+9mGxdC7%>mh*VnZr_|b?>SUpoAV)(Cbsy6 z59&1ZgM4YxozAmO>!Y+6(`Ovp)R3N|y>Tme(To$d@`cZ>gbx4SW4iW9Tb~hcQR#~X zqqYNUrzTJ^MgZd@1NYgSi#D$DSmh0oYY_V%Q0 z;4>fZHLb(T)@n;)ic$8+$eSBmE9c&?KAS_kmfmUYHDa5QeQnCd9mdzoZ3?KIgTG`e z#08G&$*0P*>h?c4@Z*}8bHkZgtt+ksMu#}eUcY5qK$!UMjcx7UufK7kKQY#~Gb$}= zuW&T^Gi()r+pYpiUNzFlB@ z$_H^h{3uq@Lyo^(Sl!@(|9*<;I=5AyN0lWf%WaHLIRLK>?p1da=4Fsw4(ImYZJ7Rh zAE)oOflpmaSY4c9Ftatt`5vFtv|zCDmrYiNx%?ph?wuFhM4ez`UuW0j==`|U@)6~< z`BSzUcx_e}^yK*euPt+T=7Yub$C^pvWAvWmymBg7}_d~}p*GSFvOlOtK@ za4Y4p@zX^{5s~L&5)JFhtKHd=_sz^CMxVBS&bw_yQhn9w98H@W`0R@HdjGql`He2q zr`y&` zBqviz6tYA@p6sDS{ZlZK@qqr+Lm7tsOp-%jr7T$?fc;ZoDe5$tLMWK zGAxEA7*vVvN_}t3KrSzM!b2xP93qkHy)d!gL!yZ2omlV1rd!dcGdU2CxRf0PNY#FkVd2P1wxTfPbCzv17r#b zgq0JKKyffxE{)SK=mckb2Xi?D8j14G5-f&L5$1rM0Yoa4sg&=Q!Vn1@ib6U)sqXG{ z7M0}zGHFaEoym9yS_LbVSTE{WsT2}@EFWEA*jPH4w2-b-F#^2@mJQoi0Yj)v5hjy~ zIRsr$fX;JbnTH)HA%sGH5DH_Y6dH?7p|h#XFe-~pqq6A?BE_9enS_@K5mD;@LhFtX z;5k&svrgI9f#?N zX@xbYUMpb!c&S|8$E)IDT^%qgB85(TCmEAK9#596n;37i^(6n79-ex@q!EMpjoGl# zg-t^8#3&rs3ybxye2v%PuiOFvK5p_s`hJw_qg)@Pzz2apcGpL_K1hKN0)OnT|C?O+ zcMnsr6#ECH!XA|#F7d0!9+Peatdd9<)Y@!qw^F+jGtJ}rd55h&-P=O7z)_~1{PC%2@Nr@uXX}$&Q=V?!w?i8- c9f#Y?u76+>zPz4ciaFr8{>%No_leE?4-p@s;{X5v literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e00c0e7a558e110aa2547ee6a6cf11c2fdd70818 GIT binary patch literal 5237 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..aeae5c35d9ecb49aba5d075f805ed8e24752ca7e GIT binary patch literal 6270 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..64e78630870a10556723b8b219136f6e13f99b11 GIT binary patch literal 5903 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/meta.json b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/meta.json index bc260937b3..79fea60158 100644 --- a/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/Jetpacks/security.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455. Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -80,6 +80,24 @@ 0.2 ] ] + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/Jetpacks/void.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..c81e4b9868e701cca975de8873381b8d75b81617 GIT binary patch literal 4928 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..52220c89543a5f907023de65483d64efc0e1e856 GIT binary patch literal 5118 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 K1eFCQZuk#93fTn! literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..cd0d10099f3048d708b5a24948407d6a177af966 GIT binary patch literal 5473 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 F{sWZPEZqPA literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..63df9585b96698a3df70e8c056a089d2b3191fc8 GIT binary patch literal 4984 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 H6q@uOZ6Apk literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b8d7b7f80935c5fb8d545c65de0c3c1c9c22490c GIT binary patch literal 6002 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 E8=2y5lK=n! literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json b/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json index a9dff4eaf1..d47e1e011d 100644 --- a/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/anesthetic.rsi/meta.json @@ -1,75 +1,93 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Integrity stages by Princess-Cheeseballs (Github)", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-BACKPACK", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE-dog", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-puppy", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-fox", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-cat", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-sloth", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-hamster", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-kangaroo", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-possum", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "equipped-SUITSTORAGE-pig", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE-dog", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-puppy", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-fox", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-cat", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-sloth", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-hamster", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-kangaroo", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-possum", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-pig", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" + } + ] } diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..f40c14ac47c53a967c0bbe75f9beefdd9457e118 GIT binary patch literal 4974 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..278f80945f9a3daba3caa3c205495d2ecea1244e GIT binary patch literal 5113 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 a04Mpa9+CIQublA!k09}#FFNcUlkpz`l%H4t literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c06374e80f11f92b4ad3717961e5d59dc17b96ef GIT binary patch literal 5318 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 aFWq9=oOhVTE!c<0K@bFl^7r%Nmi`68(+3&= literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..54c1753e487c9300692d0a29e2b8174a42c131cd GIT binary patch literal 5705 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


RFjwq!CPk&RO$~uQ0M{FW*N-m^`&G{V zhh`ud14I^%iABT3XQGKj5RW!t;mBw*iGVZ4f_RXHWBiORi|=R#u8vR-zY<2z9fqguNYsmrDFbz52{4rhb0EKo07px7rYW; zzAc4c_!2KV|KjIM9sWfRF!XOHzr^ovx_;C3OAP#y^6&2YP1i3m@Jq_SyX*f(m%{hM z6vTz!fP&zol7I5YweT@ZmSJmUjz|=rxH6`*Tj0wKzV#*{0x?rn{FOo^r)t1LIgt(B zLhgl(inOdjp?n<#iwbSb&756p1`ky#R%4a-=~^$?Z93=T87R^K;nW%|y?B_piMVCY z!Uc3|ogHUplBCkdwQjnMZClkL6?IBKYJ_J#a|iuG^d)}(bGVG5wdi4@aNG}vwMigLwv%QT|lJF&o`aD zFuA##q#B)_B3FMi$u8C|c7az;aZ2&@ka1hR@W7qp9(hY#)OweeNsoXwz5^vO;y;TK NHWus6bJu$9{tt=D;_d(d literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a65b568dccfa23c2afc1de4885cdb31083fc8db3 GIT binary patch literal 5826 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 b^dSwHpLgcrozs9eC?y!pFPK{48=LVz*%R?o literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1062dad6d5f2a3972e2be1af292b48491f9b8a65 GIT binary patch literal 5815 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 E0I}!ZoB#j- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/emergency.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency.rsi/meta.json index 8c67ffd7c9..53f656df6a 100644 --- a/Resources/Textures/Objects/Tanks/emergency.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage and icon edits by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -22,11 +22,6 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, - { - "name": "equipped-SUITSTORAGE-kangaroo", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, { "name": "equipped-SUITSTORAGE-puppy", "directions": 4, @@ -47,6 +42,16 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, + { + "name": "equipped-SUITSTORAGE-hamster", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, + { + "name": "equipped-SUITSTORAGE-kangaroo", + "directions": 4, + "delays": [[1],[1],[1],[1]] + }, { "name": "equipped-SUITSTORAGE-possum", "directions": 4, @@ -57,11 +62,6 @@ "directions": 4, "delays": [[1],[1],[1],[1]] }, - { - "name": "equipped-SUITSTORAGE-hamster", - "directions": 4, - "delays": [[1],[1],[1],[1]] - }, { "name": "inhand-left", "directions": 4 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_clown.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..8b05d65cb1193ad7c743a29611b51ef1dbdd016c GIT binary patch literal 4976 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 SyNMY9Mi2>OBTM=7m;DEG$9O*g literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c675dfeb0e3f1a08715a54bb4b09dfcac4ef8f00 GIT binary patch literal 5304 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 MX8L&k>>jcCKPfj7n*aa+ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5425aea0d97b8ee58df469500c63b3298e01c432 GIT binary patch literal 5691 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1a64b21a6394cbdefb66fb365d85700323d488f3 GIT binary patch literal 5905 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 F{sk%cCCmT- literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d7fd9a572e9a3e92911230ed929ff6f29d24c06c GIT binary patch literal 6411 zcmeHKdo+~m_n%UTQgTgX8o6ffV{S}x%S;&|QgrJ|EgD!D7dD90&<_`Rb}m-YLu^;>7H-}k>}t#{t{efHj;XYbG6&wiihkcYeL zGBpD=5D2u4LU#57?qJ2Gsto*#qoV!>fmGt6ynQ8JG$~jp60kX37FZG~WPw>S4jTlL zJ(Xt$ob1PIIL}oc_KSZ9a)2Fx=b~-1p11BFmH4n(r>vxLe{T)QK4i^0`3c1?mn<*1fi62!i{b>EaYKX(nTPyEBU@1yO}ZN{01)-#>P z>p7`&J%MLqG^UHX)uIBj%!?$VyL#P(B3ICpxzJk)$L~t-HrjR#T8_k!NGeLlUJ0XO zh??04{A=ZZA3D+QH!~acu6YNg74}~J)|)?Lqe;&1MJ9DGDE2KopH5iE)b7rN-!++v zubul*&YsNiYqZcQ9ua#QZ>ek@!e!qq;_tgZe0E0NX(&dLl5|1O>v4OPS<_*X+0bR* zz7!LGJL$DPNJF|ixtX$g{qt%1ot!9tgmQAM^Ol_GuF4zFt2l5%4gJvMl@I>Cc_qk+ z6bUu$1|+pQCQ)jPnD9P09s<8~aJ{L@kBD5t+_}|9oTKBdaMz8S+EXWTXdZ^Ft!Wy+ zOduOF-W|{y_;JY1wIF6-&%+Ofpo3Zds%C8cAIh^#AIUhI4NNv-RSgpJZe=e~@?6J@ zcQYMS!$IFav^zaMma{I5AT&ZJEV+GRX($2%zNqbB?{8pWkbd?cS?7e=u1)vKz0a42 zgl{vha^dNfk>JV`W+hl&$s26c@T#_q-PhheiZ)IlsF*CzTO-kDoXZZ%UdjH6 z*fA=$9w=*0-=0}oF=b#YuO3g`b#<&_dX4#-LP>Mlw$!|csZ~i2tyR{W?Vy(UT^*;( zDI;5(R$po~4}HmQwjEBkDZLQ8RI}5)fspWv_U6Q}aJ3Z@QA?@r7D`L5I3j$KNLggr z@+752f9XlBP1~Sxjb`N<7CTQpOW_+mIWroKc=J3x^FIGkSwqum1b+PqY8!i;K_w>qxXA765BA%?S z@Evwcw{_por7VrE0^?*1hP_K{CCD&tY`I5Nx zo1W>jcW&|P%Nblzp64gkpVt17s-17q^=sualIzm`W3h;=1=_=Gr*Oh?>yyv!YTmDm zDSu=;v8ZHqve)aC>&sG2L~jdIkO+($^k>#P|BA4^!bWncdq?zDjL&K@<0@nE(K6H3 z0S-T%Z%oR1apkRBXG`rtpC!NgbaXLR%I?k1Wb+<3GKs1oN!vpTA9U!$_F~-LdSon@ z*_zeP?)F=<@m_Y)(Oar6O@~)?!LsYK$7j?g{aUxmdMW%vHJwT7SqJ(ZhktoiPRYnP zQlqS|`m(t5&-Amz5P7oAX&bfk5ovC^l<=m#No}S-rRxoKyWP7uR#4_ESA7`Pu7n*7 zPQ7*-SMi%W(HmQ2GCkJQr(t~8W9N&AgG_mVVcz-cp6UgVy|-3I{9ZDU@}f;J9uOh# zC#}#dKCsv&O~8%3hO#J3_paTw%$Ry0|4qcUtszF)RlJ`IV{Tv)tly;DJ@dVt<~}xS zr1Fl-wZ7@#I_QckYcHz(!^@GG?`!?*xW?v&lqp%JmQm?GtzxarwQJl8IK|ioK2Ti8sXx{Et~9*4 z121JeVzq?SfI=@+|}YB@aRp|)m?^GxHJ z!SXkWE+Kb#D-yW-G!Zs_51v|8Au$UJ@x-oBh<=<> zeL=CQU~oe9@}4f4K{YKSQs$*Ye;y`&e9KWe&VRRwrXACS9>?f%NYj;wn3iKoD^Cy2>TJpB0r+NRz zWw))STOP|-=`>tNq^om}sk@n@w^?j+MBO;oywfZ`@sB{G!qW!p^2sw^bU0Cv*kat_ zs18<@5A~-BW$FR0(cNbCaLuQA7dD8R0>)~phIP9yPL@>A^;mCI9}X3ZOE=4DGLIS^ zI%{e5efIgc6ms>MJG_<6>ZiP(Fha5*l@;zgs|`lW9@I`AjTSU$QEjydXIfIvLmF2uajp4Kz7SD80YdiHjBOwtcW-NSYoYFT3Cfz(NcjW8xVB&K zY?|Hsf~t6%-j-cRYuop~+K@&;N{ix`2dUPDuB{81Ga52By!8xpb8f+OJ-?;;9o`-U zTCktvKvUwjJR%i9L_Gapeg$L7U`InTvCB0WqSj;o656?M6-m`>K2zK0Lq~lFnyXoSl!-p{P zCen;aioFXgd#4UUWSy_9lisf~m(bmr(|DeOv(#5v2(S2nRQ`KxVM!7%&bT9DWYKGT zB_IE$z|e(drwVaI{{dB1o|0a>5w%MRCjFt;d>3k+>tAi!hh2I;9I3TjzO-xMhfuyi z9+SHP6}*aV95~*jUl&=DW&!fIU+L2iP0=Qr(CQ0S`uF z3g|4Dj3)&4P9Tt-y-Y}Bgt8=HIxC37CqZ6atAl_!OcKP$hKisHomjyfa+HX*Ey~@S z5f#cHFd_CE)a+zL0D#Am(7-YtmoFyDNRWA4BJivjhC{&fDw0qV#Fy#;b`pqKUmZFe~z#DP1X4kIxng3W7L1;k*?<_BWam4*LsP-}t7G%!l)}BY^p5+;6o1 ziG5xf(4ta_&H_fb!aa&J38I*v$P_R*Oyc|_j)lUYZRm6;0)xdvF$_E&O1EL!K(Tlh zlg?nGa0nXmD<}$IETQokECmz*4&wkgOcVlRgJK||G&}(T#n{-Op?Ew21Emw#bOf4> z2fpLzuOPOHI6zg>xL0iHndNH!K~!$4!{NCFOvWnkx_ zm<-}3frv)~(#he`f>>}NKWJW{Ae`vnK_NjA-#)%_IrEDN44z*WL>l8`2x3|Ui#hKn zfb~(t2&VCaSU~^yRIvYybN)jyurVwe1IMC484Lmzib3L0Py!l3hoVsoEQWz50F^@c z7F{f0OQbXr%OMEh5#S0a(0Q)F7N1gO`K`V*n5D=AKp7N)hJK+84S|0O7OvVgS33V?gTyIwAaXEBwTlqUrn>f1mR3Uz`De{yxdS;`cjU-|6~S4E!tQ z@7494u7AbAzf%5QUH@-%seL(2vG~9aND3U4w0-tu0>><6`W9Db&`HIKt7tK&6}T-C zlKsRWkh+%QS^&z))dvPuBowNP%CORM<<&|aQENPa!}Juz*}>blUY=netmSFgWKTF_ z2lE{8#A7_K_|7dmd*5{2aQA+<_=sOT$U|0Sy|x=QKjk*u_#y7xO0;j{ogRn_dL94t zP(Ikw5ea*8q)9WrwKr!)W)!DILlh{h@1N}7aH{E5O@f8z=@~)Vg9l#z#NawjWz6w= z%koP*dPgfc@tgH&*Nb2~8^UeL98R0tU$dR7-6n+KB_>PXZF;^$ORGuPso^uIhrG8= z{&w>EO2)l_gNg1%t-+Si5{{K6wA1*uP<~2PHSIpSs!zuq^3jd2wO==P zn_uPsAyW_CWnBB-&VA(ft{#v|#QKx_JzQHS@sW#y>kBgB!J`%(WqY{S`_BfJhr!;I zE<}&F712-aiY?rG=yzO?*5k#+{F^h>kp9{Wbt&Lik$b}vm#d6w+tolF_o*~r$nr9n yDUY`b)GC-|(j81eW>3!bL{5wU0+KQ66RYF{t50*dbhAxSH53H#XKym>6;_>sdt2GnCwx-`#C({@wKRsPK=@ z>P$5SS)25oku9JizYAC^gFuZGt#a(fv(BN0Y~$ zKfY?TK_zfaDHv(~rDs=4YNf-Oz|oU?9kR#Pn6``4)`6(nqt>8V-PNd-{=s(@-9E*at`qAqnyHG=hFJ0GxJZ4{U&ey z28KWV(?zcD%J=*1tDWbR6c*O?*~HTUyC)UNIpyCjZYY{qS|L!Kn-ZEhZdPs6{;v*^ z%c)zEMb<;v9rLT*b4xZRS6IS!CDvE8CRZNL-PU|cHvgk{=4+adY)G6^6&IJ1-QIlk z%LQ&@d{<8h8sj(hY}psVRgJUfsJ3vIZk*>`b+K??W8;P|p@rB_e&3I5j=M=}x*ffC z%ueq1D`jxg{o33Ne#2T{;QHt8t1qnr{EB$WhOmiO>(&2Kz{MU|=(m0cqWl=(rr=6y z)*|m7i7}`fx}3W!Fg=iYd~DWoS(m5;qNrPFKg)IIAwyTH7^w`tWv z*^x`n=}+O#y4(L~=r4Z1-2Kpqx2!7X>nnREOrL*-a_z@zZEt-WsUthbbNLeTWp2j7 zV2G=~S5sK=wmLB3($1s7C3%5vdk>sp_wO-XTkA4zCZ+#LvezGu!s$NZ>It+>V=do1 ze3&RIOi15(yyR$TU|{0r`sw8U>KkjFuZ3$Mnj=VrE8(;nq z3)7^uw$z-B=&jD{-%UCe)T7S!_19U;>#LkfqC)O6&J}&%$+)#CJTI+@Gj8%x^&S)a zgE8{THzacP;#IDCzGJ4f`$A<;FZeLer{-Ac$3A^61#fE-CO#wamObroYW9B)01hj0 zu{d5T7Qg6hqMwVGt~)xbF4E`J){>bqlPDRU>GkoOCx7w@f8(ULYoqb-gLfC{?|8HE z*o0(%mzLbT3Hfu~yj3aVlU<^IcRD#Ove+wS=gIEE=zO0s?d_)x4nq*&J)I z!`uS}@`zxipT|%Ypg$Z7=BCZ zd0sPLcx!2!XSmd(L(owj<^Arm8;9>d^1HR8&hh*x->;e?(pM)<&e(UkwgN3X$vO$g z{1m#d{&0ovYFkC(M-`^H=O-2|_)u!t{F{4QQnaIV_2=)4eaT6y|FdVEf)aC!F{Xdt z^|-=W!5{b*_>U}$UU{RZw!hV^{r8E^^*D4<=%xL8C&l0gcY2F_{pd0U0tiM%V&r z3_&)C0SpOdK=ioIh-)>V4HH&qO-2!gLac)?{8Q^>vLSelVUPua51Iwm(HK-ZO|7O4 zw=fuEG6;}ChyK#SASZsYXbG4>YtkcFOa`Vg1`UTmks*7XNuOp92SsRD8m1;x4aBaD zSGJrXmBkNP*eFopYMtGRAo~?fBd&Z&)+@f*R_x&ncZ4t>!hJ>iMeKHELQ5tSO0oc-FFqpd3d2SWVK*QnMi(&&X+xuMkT1g5zwiWOY~iu zqEM+MN}g2rpn*XG0C(rgZW>^1%~`u*`v%>Y0C0RqVd6E}h$%VYPUN4QkFRR~LqymB N(wO%od!iTR{s~IQV+#NP literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..dd289f8c5203b60c24bbae110c7cb5b4b3352041 GIT binary patch literal 5169 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c406019ccb727e181fa5c936422988f1985c1ae7 GIT binary patch literal 5484 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c97800296bd79cef89e52e6c5b787c3f8b1e350c GIT binary patch literal 5891 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 O4{>$$a45Cgl<+^`g1Z+0 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..247117f21e87889c37891e9a6ec68993285d3c87 GIT binary patch literal 6359 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json index f4db0728d2..e32c18d479 100644 --- a/Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_double.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..4a3a086d629c2f610c68f93890a200a2a30cc062 GIT binary patch literal 5000 zcmeHKc~leU79Vy*qf|+Of;fcAB2M;QqCkQYHHBiNXhEIKOu|&Mkc9+Li=aL&*jB36 zQe3M*g{r9Kp-~jEiuzuCAcfYgh+20niaH#XKym>6;_>sdt2GnCwx-`#C({@wKRsPK=@ z>P$5SS)25oku9JizYAC^gFuZGt#a(fv(BN0Y~$ zKfY?TK_zfaDHv(~rDs=4YNf-Oz|oU?9kR#Pn6``4)`6(nqt>8V-PNd-{=s(@-9E*at`qAqnyHG=hFJ0GxJZ4{U&ey z28KWV(?zcD%J=*1tDWbR6c*O?*~HTUyC)UNIpyCjZYY{qS|L!Kn-ZEhZdPs6{;v*^ z%c)zEMb<;v9rLT*b4xZRS6IS!CDvE8CRZNL-PU|cHvgk{=4+adY)G6^6&IJ1-QIlk z%LQ&@d{<8h8sj(hY}psVRgJUfsJ3vIZk*>`b+K??W8;P|p@rB_e&3I5j=M=}x*ffC z%ueq1D`jxg{o33Ne#2T{;QHt8t1qnr{EB$WhOmiO>(&2Kz{MU|=(m0cqWl=(rr=6y z)*|m7i7}`fx}3W!Fg=iYd~DWoS(m5;qNrPFKg)IIAwyTH7^w`tWv z*^x`n=}+O#y4(L~=r4Z1-2Kpqx2!7X>nnREOrL*-a_z@zZEt-WsUthbbNLeTWp2j7 zV2G=~S5sK=wmLB3($1s7C3%5vdk>sp_wO-XTkA4zCZ+#LvezGu!s$NZ>It+>V=do1 ze3&RIOi15(yyR$TU|{0r`sw8U>KkjFuZ3$Mnj=VrE8(;nq z3)7^uw$z-B=&jD{-%UCe)T7S!_19U;>#LkfqC)O6&J}&%$+)#CJTI+@Gj8%x^&S)a zgE8{THzacP;#IDCzGJ4f`$A<;FZeLer{-Ac$3A^61#fE-CO#wamObroYW9B)01hj0 zu{d5T7Qg6hqMwVGt~)xbF4E`J){>bqlPDRU>GkoOCx7w@f8(ULYoqb-gLfC{?|8HE z*o0(%mzLbT3Hfu~yj3aVlU<^IcRD#Ove+wS=gIEE=zO0s?d_)x4nq*&J)I z!`uS}@`zxipT|%Ypg$Z7=BCZ zd0sPLcx!2!XSmd(L(owj<^Arm8;9>d^1HR8&hh*x->;e?(pM)<&e(UkwgN3X$vO$g z{1m#d{&0ovYFkC(M-`^H=O-2|_)u!t{F{4QQnaIV_2=)4eaT6y|FdVEf)aC!F{Xdt z^|-=W!5{b*_>U}$UU{RZw!hV^{r8E^^*D4<=%xL8C&l0gcY2F_{pd0U0tiM%V&r z3_&)C0SpOdK=ioIh-)>V4HH&qO-2!gLac)?{8Q^>vLSelVUPua51Iwm(HK-ZO|7O4 zw=fuEG6;}ChyK#SASZsYXbG4>YtkcFOa`Vg1`UTmks*7XNuOp92SsRD8m1;x4aBaD zSGJrXmBkNP*eFopYMtGRAo~?fBd&Z&)+@f*R_x&ncZ4t>!hJ>iMeKHELQ5tSO0oc-FFqpd3d2SWVK*QnMi(&&X+xuMkT1g5zwiWOY~iu zqEM+MN}g2rpn*XG0C(rgZW>^1%~`u*`v%>Y0C0RqVd6E}h$%VYPUN4QkFRR~LqymB N(wO%od!iTR{s~IQV+#NP literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..dd289f8c5203b60c24bbae110c7cb5b4b3352041 GIT binary patch literal 5169 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c406019ccb727e181fa5c936422988f1985c1ae7 GIT binary patch literal 5484 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c97800296bd79cef89e52e6c5b787c3f8b1e350c GIT binary patch literal 5891 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 O4{>$$a45Cgl<+^`g1Z+0 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ce27f54c82ef537a56838f38af0f675ddeaaf035 GIT binary patch literal 6301 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/meta.json b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/meta.json index f8ac073188..e32c18d479 100644 --- a/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/emergency_double_red.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Adapted by DangerRevolution from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage and icon edits by TiniestShark (Github)", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Storage by TiniestShark (Github). Integrity stages by Princess-Cheeseballs (Github)", "size": { "x": 32, "y": 32 @@ -73,6 +73,24 @@ { "name": "equipped-SUITSTORAGE", "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } diff --git a/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/emergency_extended.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..8b05d65cb1193ad7c743a29611b51ef1dbdd016c GIT binary patch literal 4976 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 SyNMY9Mi2>OBTM=7m;DEG$9O*g literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c675dfeb0e3f1a08715a54bb4b09dfcac4ef8f00 GIT binary patch literal 5304 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 MX8L&k>>jcCKPfj7n*aa+ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5425aea0d97b8ee58df469500c63b3298e01c432 GIT binary patch literal 5691 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1a64b21a6394cbdefb66fb365d85700323d488f3 GIT binary patch literal 5905 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 F{sk%cCCmT- literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b528543af3103c5c681940472e87b8b6e95a46e0 GIT binary patch literal 5903 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 SyNMY9Mi2>OBTM=7m;DEG$9O*g literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c675dfeb0e3f1a08715a54bb4b09dfcac4ef8f00 GIT binary patch literal 5304 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 MX8L&k>>jcCKPfj7n*aa+ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5425aea0d97b8ee58df469500c63b3298e01c432 GIT binary patch literal 5691 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1a64b21a6394cbdefb66fb365d85700323d488f3 GIT binary patch literal 5905 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 F{sk%cCCmT- literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..afc9525d813322de7fb8181a4660ba89bf2f2d2d GIT binary patch literal 5840 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..278f80945f9a3daba3caa3c205495d2ecea1244e GIT binary patch literal 5113 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 a04Mpa9+CIQublA!k09}#FFNcUlkpz`l%H4t literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c06374e80f11f92b4ad3717961e5d59dc17b96ef GIT binary patch literal 5318 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 aFWq9=oOhVTE!c<0K@bFl^7r%Nmi`68(+3&= literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..54c1753e487c9300692d0a29e2b8174a42c131cd GIT binary patch literal 5705 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
RFjwq!CPk&RO$~uQ0M{FW*N-m^`&G{V zhh`ud14I^%iABT3XQGKj5RW!t;mBw*iGVZ4f_RXHWBiORi|=R#u8vR-zY<2z9fqguNYsmrDFbz52{4rhb0EKo07px7rYW; zzAc4c_!2KV|KjIM9sWfRF!XOHzr^ovx_;C3OAP#y^6&2YP1i3m@Jq_SyX*f(m%{hM z6vTz!fP&zol7I5YweT@ZmSJmUjz|=rxH6`*Tj0wKzV#*{0x?rn{FOo^r)t1LIgt(B zLhgl(inOdjp?n<#iwbSb&756p1`ky#R%4a-=~^$?Z93=T87R^K;nW%|y?B_piMVCY z!Uc3|ogHUplBCkdwQjnMZClkL6?IBKYJ_J#a|iuG^d)}(bGVG5wdi4@aNG}vwMigLwv%QT|lJF&o`aD zFuA##q#B)_B3FMi$u8C|c7az;aZ2&@ka1hR@W7qp9(hY#)OweeNsoXwz5^vO;y;TK NHWus6bJu$9{tt=D;_d(d literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a65b568dccfa23c2afc1de4885cdb31083fc8db3 GIT binary patch literal 5826 zcmeHKX;c&0whr^GAkZR$#(;oG$Q(#6gOD)9CWsJZP?SnifdCmuA_=H1K}8T~P!R=! zL$`Vz5K%-BL~rdz z?2zC96GLl57z}1Y3#5iZcev`(*M0>qKgjFwJ(QZ_WMR{kvQ9iF`6b>k|cBZGy8FC8mdu29Ge(=scd-wXr zxlxafydRE9f5?8eJ!{#Pzb@eBlPv>3MEE8Q*n6;?Bdx-^>~xj{PX}PVc8cMnzINj9 zzc=SRtQ5U@-14NS_*l@LZDZP^GY_V>PNk27qDnUluSX~?okI@ELZoq6kpz*9zyhcNHq{?=4_Y=JE(-D(&+vEVoH+H7sE zd0Az_&D~yB;7U!H*=dfGxw#cA5tHmPYKq7t5KAuF6m z39#k02bqtuiWY>$mGLg=ghi*0I{#c$QCHHabkS+Oyeuf_Saf|_%gn#LR zRZeb+R+gz*S#yH#lC%H|k@l0v78myZLHZZ|X;7O&oWjV6K>N^A+BlNS7h+d#z594W z{UXQT+d6AxM%x~Z_@DoCg>dlPmCiK}y_mc-YwOYzw~TC+c8wG0^QX2Fm!!P7KXR`* z;=!Yg4|6ldqmSm@c0c~?z#qogJ9-2#0+%m2COQ&uN505!Uw(EmLB8C%%%>~2L3uNv1~0#0jcquI{`Iu+f}}Af zEQG}Q)!5>9sn#0Ls|T)U1%{8}_WqiLyES&|&EP_6!joxmQ*HUW`;jDJg$-FF9d8+| zWhP#!B=1_~DS8`aEGcctgw5h+L=JEod>glrU+Tw7GaU0WuU5|5&=u=zVA|cRbB|S2 z)B(FzJ#E_w8=Iien^&&g*buPsS-axU^I4XXbVuv8M_*rx>8{x@-pB4pKA2=#oUH5|vfOoW z=HD;q4r^P^2fsPlS$ezRYDl3XK*XCnNSr()QaUXe=O06tX6`kvTeNifzT4rQVJ4W{ zOPfY|03LDJiI1nq?-UWdPLx-U-8=QmwUbTx^yg=~p7zZyvz6U#Z`Ud~GRK?P7-5uW zYtwA6l_b0#B`+ARds^84YI1R0;7hbUp)<3@X2lw-!JINgrN;SlCL`g^o0*&2kX6Ej z8JURSyvwW=4(0D^-Nt=qd+olvV-9A>zWF?@198Md$&0 zZ9TGm;Bl(1UTma60yXx^(7=f&h1J*Z^_S>hP6K_lrDyfL*O;HdTJ0Se8O+n~;4`&nE_Vy;DfQtat)oW&$`)h?b`Av4l5!ZX1p`Z++s6-!fLq7J^yJ&!b8JB!|V<~ zQCcWggfO4o>X62nj5kzuJs0>)7Q;7g`)d+4FY~UkNF5*|NpG$oZ#6U+%(FgR!E&X5zVlp(|{3?qqD3 zn-i`#GcLw1a|dYvCl?qB!V zbRo@flr&oFGt>T;-nwTmXZQbhQS-X-tYW1{a#o~EN@ZtbIr~sEt{L#V?X>DjUAge# zU6))J-0y5_@QP)BL7izzxz5ER(Z5C;5JkUaP^Xm^mpg?0gv^dK@zz;gxI0w(Ax;|ZZ%0u1Ki zEfWH)L{I`}f^l5FC*no@c?6ux_C!Pw=xDl-0>*O#(?nocS}=o^mdGNp5#C;g9x^gS zzyl=!T*gb{i^(!igqoKOJ*&(p1Y8Y~Bzhtu=^=26Km@|^u6S28(oe=s!6Lj2;T|G3 zha5`v|40G7c_QK^5+NCdl1innQk<(m6o*i^5`&5CSPq##ubg?@laoTh$Vh0 z5Xr}Y{-cMO0d1yGp`ci>TEqhVQb4}M?rRD*>$AUbwJ1rQ4x5DnlRzE>6+^vZzUeZ6 zMi2Syp^_ku%M+@-AhEweO1PXaVto^vszsg7*MUIppLxGQf684AhEQ}mnJQqdR)t5S zdLmT)li30mmrYhb;@AW%3s1x$nOGbai6^-ek?t%k0SU731UwqURNcO!qVdHNfX@O| zR1mo<7vcfkS#B5>o{c2oaZDs02Y^U-CW(jyNGuS*5wJLSkn|PB5)l`wN+9X$s8m#J zh>Gn-#1lah2FW4e2}nH1K_i(2Jcx8d69Ak$2hGHh&}u3+i|j8D@c?KzxjY~aL<#wE z>IRkIWSt_G1OgI)h#?Rpc_LIHz*U}~ zo9WPmVgnL@3P?bR6pba4(Ks@O$iR@uXd;<_Mxx!w=&$esHkXt7f1y>=2ktR7=z&}@ z)PJhlG&QHfz~rg7sn;Z~dNRS`>S;j+SW_v8ffSIfjuYaVYGTC${5TL=KRy=hCp-5) z6a$Xrh9wb*EU2E)BqSb-$06O>Ab{i$IUF_#O=4pRz_;vT0Y@SQM4(R`q$8viRG@0D z;7%WhYQeX7X*{SJ2S_p`8i)KM83uv+m@G=QVtmTh1NFc3@K6K3T4Er-DI2tOK`SBZ z^HTUxFV&*+U;KR>hyP*^5cQ8vzDwU9a{Z9&yA=2?@Q>>HA=h^)@Lk{^)%E`-m*JPg z6v&5mKvL+aWHxVm4|L4ZW&RvMg%znzT>sYLUWaZ5!oVmo3}$4mx-?)V3Mwl zIbJezVVvkZr!lX#DZ@Sh9eP`2RWJr8lXt~4Nuh-yT^b|Wn_FM@|0@uiU+l8SsCALc zgh=n!%MB*R#%7LLZ6-ksqZF)*BiXD8neQ&QZobj%U(rVeDHg5Cm)wer%#Ae^MHko7 z_w^N8QY)YN%lp_nwJK-ihqh)1zn^rh`Y<^;?xIO~Cpl_zbD>SbxMFMXjDlQeDHQTj b^dSwHpLgcrozs9eC?y!pFPK{48=LVz*%R?o literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1532b8a6bad7b495f9bc0a44c94349ffc9d6863a GIT binary patch literal 5766 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..278f80945f9a3daba3caa3c205495d2ecea1244e GIT binary patch literal 5113 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 a04Mpa9+CIQublA!k09}#FFNcUlkpz`l%H4t literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c06374e80f11f92b4ad3717961e5d59dc17b96ef GIT binary patch literal 5318 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 aFWq9=oOhVTE!c<0K@bFl^7r%Nmi`68(+3&= literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..54c1753e487c9300692d0a29e2b8174a42c131cd GIT binary patch literal 5705 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
RFjwq!CPk&RO$~uQ0M{FW*N-m^`&G{V zhh`ud14I^%iABT3XQGKj5RW!t;mBw*iGVZ4f_RXHWBiORi|=R#u8vR-zY<2z9fqguNYsmrDFbz52{4rhb0EKo07px7rYW; zzAc4c_!2KV|KjIM9sWfRF!XOHzr^ovx_;C3OAP#y^6&2YP1i3m@Jq_SyX*f(m%{hM z6vTz!fP&zol7I5YweT@ZmSJmUjz|=rxH6`*Tj0wKzV#*{0x?rn{FOo^r)t1LIgt(B zLhgl(inOdjp?n<#iwbSb&756p1`ky#R%4a-=~^$?Z93=T87R^K;nW%|y?B_piMVCY z!Uc3|ogHUplBCkdwQjnMZClkL6?IBKYJ_J#a|iuG^d)}(bGVG5wdi4@aNG}vwMigLwv%QT|lJF&o`aD zFuA##q#B)_B3FMi$u8C|c7az;aZ2&@ka1hR@W7qp9(hY#)OweeNsoXwz5^vO;y;TK NHWus6bJu$9{tt=D;_d(d literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a65b568dccfa23c2afc1de4885cdb31083fc8db3 GIT binary patch literal 5826 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 b^dSwHpLgcrozs9eC?y!pFPK{48=LVz*%R?o literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0d1e43896f85d394fcfb4e8a59d093aa7067c41b GIT binary patch literal 5806 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 F{sWZPEZqPA literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..63df9585b96698a3df70e8c056a089d2b3191fc8 GIT binary patch literal 4984 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 H6q@uOZ6Apk literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b8d7b7f80935c5fb8d545c65de0c3c1c9c22490c GIT binary patch literal 6002 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 F{sWZPEZqPA literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..63df9585b96698a3df70e8c056a089d2b3191fc8 GIT binary patch literal 4984 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 H6q@uOZ6Apk literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b8d7b7f80935c5fb8d545c65de0c3c1c9c22490c GIT binary patch literal 6002 zcmeHKcT`i^)(=Pr1u23$4k0`ch4es3q$nhmK@b5cjtDo&4MYeDNgxF2O+k?&HbB4u zJEDj>IcJ}}e`oLC+2`DIlAWDw zmn*DMfWctP?d>Qo&=)R#w zdpPFl9s1H>MWmlbnN++=+zVLdA1Btme>kWItI|XzGZ$WkA9`EU_*&S0Z-m;Q+h{l4-j_GSX@BQX zZ@#s6LKrt2wYzv4Z4>!?S~)K7oy@3( z(-rss-aV!rrP$&6n}u7-_A`nezwitS(p@ znjI<<7T1iwebs)?_WB;tnGV_XM*$z+i`qUsmA)~x;mPY=b9@gAvtt+|!fZ^`hsuCy zI==XLxy104)+Dp7vZ+RTduhlv=S$vkmPvA?SM`5B?OIz>&|SOgkCV2jg4_H%*?v=j ze!U{=n>&p(8XPxNcZhoI+YEBz>el#8AqaNou+To6e=rufOL{&oW)$vkHEC6Dgh@Yf!0MJu@|ubv zz3acJ-tyb?G_FOyJ>&O!Dcv@yUZrgKMfaoyyRkQ?ZZ#h{u~t#|quJW>hZW`e$joQz znb`@alkJY_l(}9$xp$;GRY)&9~cwHNQcI{ziSGOm> zDKhkN&7-4_r+ZuvWFn&dUGJ7yp9SL$y^OCjX^)c}_gy6EUH+qQSIyj;8QR#X$$jq> zi(l+3=N(T;eoI@vjoL?fZnyr2+}JCbdt?Sw)$30=a6>0|1<1dQD+^GkNNXD0YB=%I zS4qkJcA|M*@UgZ#{+Q{sFb9_>6Yfz*D%I~tW>jj6<_;RC;;t#vh3YcrG<9a*^vxPY z+LX=#TP^oabWJS8{hb(h^rxSz+fNVm25Q6`R@+{>IxbCBUn!B+HGI1Hf~U2<<&hIn z@jEP6zCXjxx5>%y34D=c7f>WZ+jiwO*{^DJ?RINEuZ(k8%^o8hiLJRWD)Toy)L~Ql zIfo>^@vxg#yE}D7p<~P0Z?VA*coo3{oMWM zwvrT$rd}${peiBbqOW|XGW|x?c=rX1TN%ph%6~baTUgENAH>@i5z=;r&aV^B3>}ZM zXmD>jJ*)K74b|B#WYkFOv5s>EQ&&#uOA(UN*7W0JR`hNODgC$OgCUW*(IaTQE z=XFjp01hlu4j!~Ty5^@TxB9F1SL%(0_g9l@ZUl!{9LlYqxN+-2&z=HGj>P6skM1^^ z2g855z2gctzOs!-j}UFmuiHPY_I4jH=TK%M`D`IIIJl%mw|_`AZ$+A1wrX#U(S0xK z_RKTKJR&mdo5w2{`D)Rbr zGmN3fDNAc2Th~JxKXmbrBwkmHLKN^Gl7lV#JjkpS>wNMX{hB(m!{oVUM=@3{*CCtOUV(|;DyS~ zE1aX^)z|&Fi!FIdlKVq)a-Nj$8hPK7QqDf|u-X^$?9R)Z+6N4M>-tKV8tyXCLxm0K4A8gJ>uY@ger#(l3Kb7^{PWRLC7mmjO0 zLsod4*yLC!4y`OEWi3)<=%=(>IUg z#7gv8k(J_)O`Hn%cANoV8RcI%(fP<0!pY8%s3B=^(oXlL?%H)1{|)s81maxbOJ2aF;t=VE$#v00HV8L&9bYfRJWX|`TdNOqx}ue^N` z+{$L1h8v(`bI~RxnK0I3{z3PUB^7mImj;GsR3M{~IUu_b|KxG@&GYue4O+62=^CwEZsQ z3vEzBS00DyOO z`pP$P%VIc79f8ch;C`k3Irc?mNXyZYOyST&#O~Qs%n{=L$qWvi#UL*hF-#f;hoRw+ zrcA6cl3>EXB25`M0usbB0U~J105Qh&B~bQ3d;t(d2gOhjIEn?~;AwOc))+%aVlj9k zl0X1WkN}7UkytE=$e=M8pfNyN01bKtO5RoK0 z9giennHUoi}S!}=$M011u78}HbleakAn2%s&$6KtRBR z44^r{3Sw}C{C}F$gT)m6U)tj712_9P=yohV)W2v^^l?tPg25kaAFF|^#mNMRFHQ?G zK>rv59|#2*i+(~_ADig@K#(5@tskEf_OqPzUy^|a;6Oad7(g;129X53DIRG`GbJJM zpa~Wv((pLC$w%_Opz}FQfe_$_U9~6%RL>Ur;M=qfZ zf&LUMTD)R>j@AtQAAFcCDlA!IAiIw;Xz7AhLiCrV@DpEVMuyMV*Lck)gA zey8g@UEjpOHz|M5uJ3ex69eC*{5`w=-{?~K=P(5ZK|3HJbW~coE+-l~X35YTY$>p# z;uF_dX;wS*k>}cZ@nJAUHSr?>%g)t;gt7vAN2=`bGG$F!+2RBzS4i~Up0dTwvuSKc z>GDR5N)3e}rSKAVTMub{1!gz8`LKL?uC!-DCywA1ztSZ_LD7n_EI8}{!eVIcTw(P> zZ(VExbje{<2?XN8uRRfO>vRq{q69yz;3V3_St+#+$I1r#-)xXn->fjdF;yy+J!zDc zTR6O3KSEDa6;oCHNH-IvyT#_`#=go>eoIPgUB+vtn~%~GuQl{ZXwOXcR)}Vayvs4` zMc$$Njuq<0#eaCGg)%XL`Ld;puS*}8Xq<|%O=wRDluWSE|CjTfV~uEykqEo=S0|CzPUoO7PN_vhLBv-h*l^K7JhyQ!-g zsKH<`bq{yvWzZcVzf`9}|I+yQ*D#n0BHo`NTL#1+#1avQ#|IIzI5CI-V|g4HEcWhj zKv0gQ8GM2}D^L-U(kO`?%o{?q`fL<_*fOwf`>uzW$S(D%21gcFQ->T9%SH_C10&$6 zE)fOReSRx><%bWy9DgnKDbD-w{^1(6&!J5XQY0(w+VwX9&#FJ+_U$uV<>i+7fPduG zKK;|$C+~fDR=N54_R9-KKUI)czje(%y-_B*hACwV7K)bZe^`Bsrd^#_CvpkB_-Q0G zshgQcqllL1M4Gkiggv}qkbjtV-b~9XrTN%{ws$lw*ywEUcAxdmj}PVe=v5bu4*k=9 zsbS5zB{e&*E!z6*%tx86Ng~OyT{_yeeN%V!1hwXzrCheHJ2NpQ(q)oVX>qwODoA=vtwTT(JU2x_bYLZ&8yNDp8t2qt$(Hmm-jfM zHz{%o0bA$qQLA1BXYZ&lGPY1NeCoL>x4fiC(jRg>r=@hoaU-3|t=R~x<<9=QWeZR( zE0_D1@_#Xf)b9Y``ra!oeDbl{9I*SM@I52R)Oi!&O(R4O5R6t!unF#nH&wj8v4FM z!2e;f%c!`%ZMJIbK@%+6TLDg8#P#*FxU);z#%vu?YtziyduB%-4$Fvc;SbgYH|rg4 zyXVk{a*yjpea`U9ba#|U)L`w@qvmhxNyvS0_e``;wDH}Mn~gDDpGo6B zeD?acx1aF$8DVcRrtax1qS|)%5T>t~oseJ@Y<6g<~VP_jE4N2M^od z9Vws2HB@ram!_H!%FttzB!g7nv@$wb7 zf$=S*Ey@a!O1q_M9mBAS7r6czH_N&ftGK}(^-AW^y}S8M$?rmxIgSaNagc^7QP$xtg&to)r^^cJ-(yZ++Nc7PcU$ z)wJ^toAz`A*6JindZ|#DLN~BcP6h7fYeeAO*&(#uyrvSv+?; z{%KQ_`rRBBxeHE}bZ&e}wMpdlM;4vq2G6~&P!SJn4Aw01c{1e4@YUu{@>}MxdSTN&r~9_62xSK0ru!bJ z(JekJ7wzvgNK_o+p8xxFlJW_+zj#M^uI(+ktCZe%uM8?w`r9=kyGwO_Wmtl%((cZQ zJ#QrAQ*7Zbo8aVG_kEF>xAmAIT>kpisVlF{50juXt<5*Xve&>S*xUE6>{_C$Z#Goj zWs*zCe*$kW%1=|v_chYo5Rh>z2A6oQ*O)nxLY~m+ce8S}v#vHiMYlF~jMCQC&Dy8A z!guXn`u*6PVT+7vE5B#hD^BK>Lq~QMmz?3O>>-#x=$w=MGy>W3(zP*MHEKCsO_7}Y z)>k#z@WM=vZ3E|BK%Z4T|J|NQ-RM;Ik)nb`#KjmH#(rHMSzNc`D)ywkhHp`p`QZGk zH@(6}6=N!n)l%=->!!D%QI2Uk=JnYZw?#Fh8%JE>OY2L3_8S@z(zVkVv5rFXXCTpa!g?BXCbNvIQxwGI}9z^jEg}Z)&`8GGCyAF8mT@KS!Ev?^iL~gH&?~4e$kx3`xe{=%RR~FxkFBe zM&cC}K$i}UbWMDf&Y@J?$cuvy8>6bO9_QXqiA|Z(ub_HyX2}aoyHN<7WAIQjaKm)` z#L&qd*=4R;-3*WMH6d@}4Ex$QH&+W+Ju-iDExo+1ZJl?(&hc!l;l;YzJ?O$vx-X-V ze^y=aU}|62lww=u4nxn-l&xt2H&PTO=K=NSMJ=rpboZb{>&qiAIzE2Bvl$*0GW{>b z33N@wh%oX545pCEqtWOdG}^asCG>^NOW0Puv|+J+)BdANToxjuH6xqpd#%#b$vF#k z>zsI&XP<^i2K9(M_hqZ*DBnt6Z@g)_iXK$etCSr_lo}lt=jj9=X?&aIv`Js%`SVNC z7q7%`jS$3zp4OG73d_%A_}iLtqy@~LS&8t)2cn#qNB6m7I6ah_=e_+Of>XG0w?0zF zv#Tz{_PfskD}b3I&355R>OexDrlp74OUldhj(R4E4=aX74F?W2D7M3A@9MCPOk-F@ z7u~BXWal>$8UdG^=Aq3Mh2rkM!WF9vqkKNqhlK=rNcTQg?PEA8dZcajrOlqjNV|4w z8x!d=fYbO~)bEqE)HGmr#+>Q-PN@&y)qU=X5nZi+oxMjVL*YK=+((?+l<0Mw`{yoX z3M%6i-|b&#d2;x)O;Wkf^=AUq+9|I^N_w{hUfoLQn52Vd!FUt5(HlBy$~_-5YAjUX z-HO!_!Urn{$O$lfN5Lb*o*qqo%VlE~mnzp#Ka7dI3jT8Nnjy(W2@pfjrY4#PZC)5& zo>Z1dfCktiCWwv|h@s653})*PD+X8*pbWtTxjdmA@0qyB8%u=%1fUE+#0vOADK*v(If+Y!p5?$rE>9qyw1UX~MN`J({2=QW-{g|XaDH|K zGXIYIi}ttJCzT;BFE6UIh!rJw&%@acDW9Lp7O{A2>f|F12eMf#4uGOCNmvvS%f_Q9 z9Huo2g#JJ*n@ymA#Gjx%gi;wGWPx%h2pr9WaBw()NCs`#D3Cw`P((5rgdlJLiey8u zCX$G34vs?p3BpIhgQ^nX|Lm0D?o`9szo^}4vQw>CnfU4P@!-jY5{qrLI9KF zP&8DU1O#LviN8q1w?oRELdY$@i@l%)#Rg=6Gav&YP%PetiX~8SB!4W9ilb1mL==`p z#r~u(V)Hn0|ChFW`5@f)t1b z*^_=kSYJe}a6rffq5ko;V84y?{zEa4@GKS?1Xxf#;qXuf$v70nhC@UV04B+r2okMr zNX%c+r6P_j29SV`T!=@AE2uyxxk8wKO_jy3`Z3|4JP#0MC@cZ>gEDI*=4-GRdB^w` ztu5w%@nJiu@KcL{?7oaatqbaenD4FdD_`=a^I!aZ&BK3j1_=7kB)`S)AG-d~^;-=5 zmhzwK`a{=mG4NZ;f2!;MjV`qxhbd49?SNvSqtcqO@FmbOYbtZ8n=>p&e&RZ&%xi~k z)5Pw9QW$Kyw)|3n?atGO232GpUM?z6m1e4HE4Sv_wnB$#n1{2YKcj&armuWBOeaIp zAnEhz_D^OjP}&!Bf>(dc+?fIg&@sN>_w?>`#tXTE$ zVy6AEd|!ov%RRY^uez3M)&`E2JE3}!Ji3<^bxNR&>Llu|0u zu~bK)vQ?Jqq(ibr$!g4>-t^ix_;mPnz?4?eedP--1q0cpXYs^Npksh zow}-yDg**icXXg{1b^Z3&jKj;Es2VnfIy(Hqujlu8<{e=NG#;?LIAilQUt((2p$&# ziFiDa8T{Mx^Xdt+_`Pa3EqD2o`!0EK(|QM$u8rixWE*x_%nHgI|G+?bV@UlgM`zy; zif5lJrYAdRi<{r|9Sk~u^N-bSGsTigPs-qHm3zpT>m#y@D|Qw+tQb3PM0?uvekM5X z8n&qaaFSE3plpg16!i?Pw=&8jwc}(oH798p@2WPXKD*xk%4W|ZyV$VY=4&uvPR8`e zr17CCZtVNQ6Qo^XtOfY@5uvHrPH62+GfHP^dg*?iM*r`bZ-FgC_~kpcC$x;X6$kR& zS`No|7r(mt%5Uo0{*coz)spnA?p~a1j2RrL+4t6G)rYHMlY5&YdU~D~Qe@Fqv9WDB z0POg9^+xFM2lAU|4dtvY?4(L*Xy$L|0n_!lgJ*S>H_7raMKQ$G7X=#kFKvHSbJQ;; z=kj!FlJWYb8Oe*H67x&x>O}uBZm3?2@;+j!mw&O|V>dPQ<`3SNw{{)-OVw24-u)+8 zO?sN!UyFL8{R?5L-F^cJ+TjVm->gq*?p%vkv;Pofbx7%D$M|?@YM6Y>LH~|d_vA81()+o3(ZGv z=jWrSveDCR0)rItZ9M}G81RtUvLTPql982v7dKLt@X$kQiu8^gGATf9E1tane&{VU z_#+dkduGxqy4e2WaEDe*{^Is*YcJ8Anm+`pi=>aTee3eI%q2%l3!o=$6zvKYSlzcRC#@leOJ+-X(O-sa^#t|l76G1Bpkhf|CLog0^J{ktXD(W%I>>iPPz zQuhPC^bdJq*RSPXB^fknUeeD>J#s~VYYOcUr(Uq9@{Nx79!kMeHDg%lr9}Gx+w#K% znuNO8X@3Udug4uu1NU}T?wnU+pO`05E`{Z`o}8dm6Db9(X3Pl7*_Bpah4G&VD{O~c zEn1>-)@NJoqXsj#3*#p?+bu1&Oguow8J|#Jkqj}r)mVSKRtuQWER>BjK8sr(HxA?H zX^z}_?VHv$;Y;Inc{q*Nm)Sq}A+gH6yKcg!|Bge(&bmA)S|3jMVfY1Jj|K3k>|D5I5)tL9P_ZHz_} z28bl_bE91?lhpkPn6#*49Ccdep6%GcCTQUvgf>4tcMW#WZ%xcP?49UmIpV~ApN+5K z3sOv&BlOf?Uq+g4dsp?q>cXtP;>X~Tt?leu$dk4_it7cRb2C2f3_&bPKw zs5$QK@fa9LB3cp}YK>iuohE>>?XVEK`9*x&+AUeY$oImD7}p8w(|yRe=Dyrj z=I2hWigYeas(;2QIXC6uw4tH3vA%Qo(Mx?&|G_ISU&

@4Sx=w`H|QW2nWpJKK<*-J=w7Fr+-B5y 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..69ab788a113fcad2438242435f3a48c84dcec9c3 GIT binary patch literal 5813 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..866db3b674b92a111fc7a3741de43b922990d461 GIT binary patch literal 6297 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..77d9a4a379c45454bc5d592deaa55f3e0193dd8a GIT binary patch literal 6292 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tanks/plasma.rsi/meta.json b/Resources/Textures/Objects/Tanks/plasma.rsi/meta.json index 2d3199f75c..f7ddcc6235 100644 --- a/Resources/Textures/Objects/Tanks/plasma.rsi/meta.json +++ b/Resources/Textures/Objects/Tanks/plasma.rsi/meta.json @@ -1,30 +1,48 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432. Integrity stages by Princess-Cheeseballs (Github)", + "size": { + "x": 32, + "y": 32 }, - { - "name": "equipped-BELT", - "directions": 4 - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - }, - { - "name": "equipped-SUITSTORAGE", - "directions": 4 - } - ] + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BELT", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "equipped-SUITSTORAGE", + "directions": 4 + }, + { + "name": "mask" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" + } + ] } diff --git a/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-0.png b/Resources/Textures/Objects/Tanks/red.rsi/integrity-unshaded-0.png new file mode 100644 index 0000000000000000000000000000000000000000..ff4e662e733c7fe09a0da0b91f92c92c82ad8f74 GIT binary patch literal 4893 zcmeHKdsGu=79W&{paHFll!^{Pd_j^)$b-!C5D7@60feBSXqn7R!YFx=3?xu1q7@ZA zTkNhc#0pkLtGM6;6sY*1qN~#CE=8!;H?0;BS|~m_`%OT^vpr{z=WPEoIg`xX@7~|N z_xIg&rg zzj;!^$;j?g!2w6TmmjDrflEGqUJ;xh#Hy?I=%W%}eEy`oc1g?~OI}~rPi;ZH1?72c z$Kt4_-MYx;_qtblT{`A)d+)i@~1NYFRV<0C>0qwATXCjcym_Vm!PCT=aAd#FKYv+A*j9WoI{x9V9k6b< z*SPWRnf4c-*{vLX=GfWy^OB)M^OSRGx1(b`^PN{YCH*u#=?rwhxb|eel#7CNoxerrc_Wb4EuK#(Zd^f{mZLyl^ub-f|O(**8AwSvO8X%3CbJQqvG~82(`~HAH9)So$|?y$z|aa zmep)un3thpeXW*^SlG0l+c3+`!4uJK7?zr{q|hUZU9)Xbp~v>LnSZ~thueU1&YvzC zrP@i~<91&BS=CW#89CLZrYoD|`aTLGIyP6D5A|%$ZBe%ctnt}Dr|#joC!J@P%s=um zb*`4Ryk$kssaYQt@xGhzb7hXbz}7fFGBK@Q^P+0IO89cyUbk=Es~UqYMNMhmER*}N zrZjo8tNWII5VW)NTETp6V%O@M)vg{FKJxZbc)q-RK#*VDxu7`rQ}NU3Nh6H~u=iIB zdFX=_`<{8mgSfrNzrv%w?%q@}CQNs^eOy!a;@a!E(bJaa^ku{69&ef0XCIE21##+l z4NwD6g`aGyjyxAL?tJaOxnYlnx50Pwi_yf<8}5lxz8WPWU?=wsV-gA>LtvX0b<-T}Y?R-N|rN9yj*xNqKSD#tGcsdh_Gnv<~UY z@Ae~&Z3VWqEG`ORGiqc83l z)w#ZI-o6i9yX?E#_U$$OIL*2I^F5I)b4w5|`WQrz_5Q-s+2^b9r?bB&*Zk(HIUb9bqEB5`XKKB#q_=#z4tI2%;yF8|bz6a{7!XvSd$N`e)g_^zZGrB&B^Xp(oMCj2)#1bznATP+$fo zC_Y4Sy_Sm+dKJmFXboUSLQp`k#em_BE+yE(K~4b{6aZ*R8iOs`RGkU61hTETD7a_DJT`1qp_c`+6BIFUsNP7z zLavaDaKbE9hJYR91P2%iH5w}mAAkT)f$S8THlRG7*=**T{kVFghR2smr94Ey69_m! zgJa6n(U^s!GkGx({TMRRgc~UXP3d(ogNdp1>2x5Q4c6gT@o5bT#UQ-SG{6GjgJ;1E zJU$oUX|=qe7A86@1Aq(!^oJHECHN`f#gZm{x)CSCGDscmH57ut2ki~%##C!M1kNK< zNi9${0k8ZwT+WdzVg@Z33N)0~V6_5dzoAJ}>epnw5gW5&O=oBzz zC*fY1fYmOj3Ry@eI;LliiD_0#6ct|GDKfbQ0mP8rOmVt9MHe%5tIq|&$NpA zdn%4h>woHhOr@;N1jE+0Kry^O1rwG*64p2YR{s*7g6T9Qm>&ZL`$|szK{4P`KQSSc z;2a5#F!dzBKs|{tj+&GZxJt;!RH}aR2GLD=HEqU>WQYdv2)F_TYUK)^KCo3Y-i*ea zLNfaRDB~c0oFSC4c>~Gvn2GT!+W_8w@eyEE7&2mjUB3*BE-(psgQIYOFA(dW`5M@V zKhp&azjN|d{Jx{>9bIq5z*{N5tFCu+y%hs*rTnhC{%>?Sy*^BlI`9w3430`=JznnM zm}ReuoFjuc%=>sk^C%#3G(;qtAZX-R=CXkb3f+LvftD-693BmKhQyNg#)CJ3Xo_4G zqD(l~-eALmpb^81dR>wimv%`bW(|X&V+Dlw()T)BY>GD8o_@ZYzYXX^@~|k`;n~Tn F{sWZPEZqPA literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..63df9585b96698a3df70e8c056a089d2b3191fc8 GIT binary patch literal 4984 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 H6q@uOZ6Apk literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b8d7b7f80935c5fb8d545c65de0c3c1c9c22490c GIT binary patch literal 6002 zcmeHKcT`i^)(=Pr1u23$4k0`ch4es3q$nhmK@b5cjtDo&4MYeDNgxF2O+k?&HbB4u zJEDj>IcJ}}e`oLC+2`DIlAWDw zmn*DMfWctP?d>Qo&=)R#w zdpPFl9s1H>MWmlbnN++=+zVLdA1Btme>kWItI|XzGZ$WkA9`EU_*&S0Z-m;Q+h{l4-j_GSX@BQX zZ@#s6LKrt2wYzv4Z4>!?S~)K7oy@3( z(-rss-aV!rrP$&6n}u7-_A`nezwitS(p@ znjI<<7T1iwebs)?_WB;tnGV_XM*$z+i`qUsmA)~x;mPY=b9@gAvtt+|!fZ^`hsuCy zI==XLxy104)+Dp7vZ+RTduhlv=S$vkmPvA?SM`5B?OIz>&|SOgkCV2jg4_H%*?v=j ze!U{=n>&p(8XPxNcZhoI+YEBz>el#8AqaNou+To6e=rufOL{&oW)$vkHEC6Dgh@Yf!0MJu@|ubv zz3acJ-tyb?G_FOyJ>&O!Dcv@yUZrgKMfaoyyRkQ?ZZ#h{u~t#|quJW>hZW`e$joQz znb`@alkJY_l(}9$xp$;GRY)&9~cwHNQcI{ziSGOm> zDKhkN&7-4_r+ZuvWFn&dUGJ7yp9SL$y^OCjX^)c}_gy6EUH+qQSIyj;8QR#X$$jq> zi(l+3=N(T;eoI@vjoL?fZnyr2+}JCbdt?Sw)$30=a6>0|1<1dQD+^GkNNXD0YB=%I zS4qkJcA|M*@UgZ#{+Q{sFb9_>6Yfz*D%I~tW>jj6<_;RC;;t#vh3YcrG<9a*^vxPY z+LX=#TP^oabWJS8{hb(h^rxSz+fNVm25Q6`R@+{>IxbCBUn!B+HGI1Hf~U2<<&hIn z@jEP6zCXjxx5>%y34D=c7f>WZ+jiwO*{^DJ?RINEuZ(k8%^o8hiLJRWD)Toy)L~Ql zIfo>^@vxg#yE}D7p<~P0Z?VA*coo3{oMWM zwvrT$rd}${peiBbqOW|XGW|x?c=rX1TN%ph%6~baTUgENAH>@i5z=;r&aV^B3>}ZM zXmD>jJ*)K74b|B#WYkFOv5s>EQ&&#uOA(UN*7W0JR`hNODgC$OgCUW*(IaTQE z=XFjp01hlu4j!~Ty5^@TxB9F1SL%(0_g9l@ZUl!{9LlYqxN+-2&z=HGj>P6skM1^^ z2g855z2gctzOs!-j}UFmuiHPY_I4jH=TK%M`D`IIIJl%mw|_`AZ$+A1wrX#U(S0xK z_RKTKJR&mdo5w2{`D)Rbr zGmN3fDNAc2Th~JxKXmbrBwkmHLKN^Gl7lV#JjkpS>wNMX{hB(m!{oVUM=@3{*CCtOUV(|;DyS~ zE1aX^)z|&Fi!FIdlKVq)a-Nj$8hPK7QqDf|u-X^$?9R)Z+6N4M>-tKV8tyXCLxm0K4A8gJ>uY@ger#(l3Kb7^{PWRLC7mmjO0 zLsod4*yLC!4y`OEWi3)<=%=(>IUg z#7gv8k(J_)O`Hn%cANoV8RcI%(fP<0!pY8%s3B=^(oXlL?%H)1{|)s81maxbOJ2aF;t=VE$#v00HV8L&9bYfRJWX|`TdNOqx}ue^N` z+{$L1h8v(`bI~RxnK0I3{z3PUB^7mImj;GsR3M{~IUu_b|KxG@&GYue4O+62=^CwEZsQ z3vEzBS00DyOO z`pP$P%VIc79f8ch;C`k3Irc?mNXyZYOyST&#O~Qs%n{=L$qWvi#UL*hF-#f;hoRw+ zrcA6cl3>EXB25`M0usbB0U~J105Qh&B~bQ3d;t(d2gOhjIEn?~;AwOc))+%aVlj9k zl0X1WkN}7UkytE=$e=M8pfNyN01bKtO5RoK0 z9giennHUoi}S!}=$M011u78}HbleakAn2%s&$6KtRBR z44^r{3Sw}C{C}F$gT)m6U)tj712_9P=yohV)W2v^^l?tPg25kaAFF|^#mNMRFHQ?G zK>rv59|#2*i+(~_ADig@K#(5@tskEf_OqPzUy^|a;6Oad7(g;129X53DIRG`GbJJM zpa~Wv((pLC$w%_Opz}FQfe_$_U9~6%RL>Ur;M=qfZ zf&LUMTD)R>j@AtQAAFcCDlA!IAiIw;Xz7AhLiCrV@DpEVMuyMV*Lck)gA zey8g@UEjpOHz|M5uJ3ex69eC*{5`w=-{?~K=P(5ZK|3HJbW~coE+-l~X35YTY$>p# z;uF_dX;wS*k>}cZ@nJAUHSr?>%g)t;gt7vAN2=`bGG$F!+2RBzS4i~Up0dTwvuSKc z>GDR5N)3e}rSKAVTMub{1!gz8`LKL?uC!-DCywA1ztSZ_LD7n_EI8}{!eVIcTw(P> zZ(VExbje{<2?XN8uRRfO>vRq{q69yz;3V3_St+#+$I1r#-)xXn->fjdF;yy+J!zDc zTR6O3KSEDa6;oCHNH-IvyT#_`#=go>eoIPgUB+vtn~%~GuQl{ZXwOXcR)}Vayvs4` zMc$$Njuq<0#eaCGg)%XL`Ld;puS*}8Xq<|%O=wRDluWSE|CjTfV~uEykqEo=S0|CzPUoO7PN_vhLBv-h*l^K7JhyQ!-g zsKH<`bq{yvWzZcVzf`9}|I+yQ*D#n0BHo`NTL#1+#1avQ#|IIzI5CI-V|g4HEcWhj zKv0gQ8GM2}D^L-U(kO`?%o{?q`fL<_*fOwf`>uzW$S(D%21gcFQ->T9%SH_C10&$6 zE)fOReSRx><%bWy9DgnKDbD-w{^1(6&!J5XQY0(w+VwX9&#FJ+_U$uV<>i+7fPduG zKK;|$C+~fDR=N54_R9-KKUI)czje(%y-_B*hACwV7K)bZe^`Bsrd^#_CvpkB_-Q0G zshgQcqllL1M4Gkiggv}qkbjtV-b~9XrTN%{ws$lw*ywEUcAxdmj}PVe=v5bu4*k=9 zsbS5zB{e&*E!z6*%tx86Ng~OyT{_yeeN%V!1hwXzrCheHJ2NpQ(q)oVX>qwODoA=vtwTT(JU2x_bYLZ&8yNDp8t2qt$(Hmm-jfM zHz{%o0bA$qQLA1BXYZ&lGPY1NeCoL>x4fiC(jRg>r=@hoaU-3|t=R~x<<9=QWeZR( zE0_D1@_#Xf)b9Y``ra!oeDbl{9I*SM@I52R)Oi!&O(R4O5R6t!unF#nH&wj8v4FM z!2e;f%c!`%ZMJIbK@%+6TLDg8#P#*FxU);z#%vu?YtziyduB%-4$Fvc;SbgYH|rg4 zyXVk{a*yjpea`U9ba#|U)L`w@qvmhxNyvS0_e``;wDH}Mn~gDDpGo6B zeD?acx1aF$8DVcRrtax1qS|)%5T>t~oseJ@Y<6g<~VP_jE4N2M^od z9Vws2HB@ram!_H!%FttzB!g7nv@$wb7 zf$=S*Ey@a!O1q_M9mBAS7r6czH_N&ftGK}(^-AW^y}S8M$?rmxIgSaNagc^7QP$xtg&to)r^^cJ-(yZ++Nc7PcU$ z)wJ^toAz`A*6JindZ|#DLN~BcP6h7fYeeAO*&(#uyrvSv+?; z{%KQ_`rRBBxeHE}bZ&e}wMpdlM;4vq2G6~&P!SJn4Aw01c{1e4@YUu{@>}MxdSTN&r~9_62xSK0ru!bJ z(JekJ7wzvgNK_o+p8xxFlJW_+zj#M^uI(+ktCZe%uM8?w`r9=kyGwO_Wmtl%((cZQ zJ#QrAQ*7Zbo8aVG_kEF>xAmAIT>kpisVlF{50juXt<5*Xve&>S*xUE6>{_C$Z#Goj zWs*zCe*$kW%1=|v_chYo5Rh>z2A6oQ*O)nxLY~m+ce8S}v#vHiMYlF~jMCQC&Dy8A z!guXn`u*6PVT+7vE5B#hD^BK>Lq~QMmz?3O>>-#x=$w=MGy>W3(zP*MHEKCsO_7}Y z)>k#z@WM=vZ3E|BK%Z4T|J|NQ-RM;Ik)nb`#KjmH#(rHMSzNc`D)ywkhHp`p`QZGk zH@(6}6=N!n)l%=->!!D%QI2Uk=JnYZw?#Fh8%JE>OY2L3_8S@z(zVkVv5rFXXCTpa!g?BXCbNvIQxwGI}9z^jEg}Z)&`8GGCyAF8mT@KS!Ev?^iL~gH&?~4e$kx3`xe{=%RR~FxkFBe zM&cC}K$i}UbWMDf&Y@J?$cuvy8>6bO9_QXqiA|Z(ub_HyX2}aoyHN<7WAIQjaKm)` z#L&qd*=4R;-3*WMH6d@}4Ex$QH&+W+Ju-iDExo+1ZJl?(&hc!l;l;YzJ?O$vx-X-V ze^y=aU}|62lww=u4nxn-l&xt2H&PTO=K=NSMJ=rpboZb{>&qiAIzE2Bvl$*0GW{>b z33N@wh%oX545pCEqtWOdG}^asCG>^NOW0Puv|+J+)BdANToxjuH6xqpd#%#b$vF#k z>zsI&XP<^i2K9(M_hqZ*DBnt6Z@g)_iXK$etCSr_lo}lt=jj9=X?&aIv`Js%`SVNC z7q7%`jS$3zp4OG73d_%A_}iLtqy@~LS&8t)2cn#qNB6m7I6ah_=e_+Of>XG0w?0zF zv#Tz{_PfskD}b3I&355R>OexDrlp74OUldhj(R4E4=aX74F?W2D7M3A@9MCPOk-F@ z7u~BXWal>$8UdG^=Aq3Mh2rkM!WF9vqkKNqhlK=rNcTQg?PEA8dZcajrOlqjNV|4w z8x!d=fYbO~)bEqE)HGmr#+>Q-PN@&y)qU=X5nZi+oxMjVL*YK=+((?+l<0Mw`{yoX z3M%6i-|b&#d2;x)O;Wkf^=AUq+9|I^N_w{hUfoLQn52Vd!FUt5(HlBy$~_-5YAjUX z-HO!_!Urn{$O$lfN5Lb*o*qqo%VlE~mnzp#Ka7dI3jT8Nnjy(W2@pfjrY4#PZC)5& zo>Z1dfCktiCWwv|h@s653})*PD+X8*pbWtTxjdmA@0qyB8%u=%1fUE+#0vOADK*v(If+Y!p5?$rE>9qyw1UX~MN`J({2=QW-{g|XaDH|K zGXIYIi}ttJCzT;BFE6UIh!rJw&%@acDW9Lp7O{A2>f|F12eMf#4uGOCNmvvS%f_Q9 z9Huo2g#JJ*n@ymA#Gjx%gi;wGWPx%h2pr9WaBw()NCs`#D3Cw`P((5rgdlJLiey8u zCX$G34vs?p3BpIhgQ^nX|Lm0D?o`9szo^}4vQw>CnfU4P@!-jY5{qrLI9KF zP&8DU1O#LviN8q1w?oRELdY$@i@l%)#Rg=6Gav&YP%PetiX~8SB!4W9ilb1mL==`p z#r~u(V)Hn0|ChFW`5@f)t1b z*^_=kSYJe}a6rffq5ko;V84y?{zEa4@GKS?1Xxf#;qXuf$v70nhC@UV04B+r2okMr zNX%c+r6P_j29SV`T!=@AE2uyxxk8wKO_jy3`Z3|4JP#0MC@cZ>gEDI*=4-GRdB^w` ztu5w%@nJiu@KcL{?7oaatqbaenD4FdD_`=a^I!aZ&BK3j1_=7kB)`S)AG-d~^;-=5 zmhzwK`a{=mG4NZ;f2!;MjV`qxhbd49?SNvSqtcqO@FmbOYbtZ8n=>p&e&RZ&%xi~k z)5Pw9QW$Kyw)|3n?atGO232GpUM?z6m1e4HE4Sv_wnB$#n1{2YKcj&armuWBOeaIp zAnEhz_D^OjP}&!Bf>(dc+?fIg&@sN>_w?>`#tXTE$ zVy6AEd|!ov%RRY^uez3M)&`EB>-oLs{7&b*?|;plx#zyW%jf!jKi}_l-Pg5)>fx-UprrtT zK$Kiv96Z5)nD{3r16~DjaT5@TOmm#Kuh5eg1LN_zOjZN{6UOoY7$9OXArMj9;CjDH zD-0HV3RU-$$Wk?Rh6z2Q(+6MdG`9S-O|JvBWdooI+_Y=TLBd~cnY}cwdB(jkZFNU` z7^3e~;a*eG(3`6RR$gT1AgcCJm@}zo_;)Bo0*u9VzX&W=~P?l7P_Z#-RYwqaVhoh5D8t& zx(e65BcG@e@HbyNI+f-yCQ~oS|L1l6#cu9<_Khi%VY~Pj37-$KD(Tp=^tPViXzfY# zU=^+F)}re|73yinI|pFNf0KDJ4wH6x6VQ#pQQtQMkw|-RKgBJIt zozPD(EX?+Vx(uOE?&hf+RhvM|5SJ3F{&6Swd>L1h(}Yc@BL$v|k%5wGMCPcKZ`{yD zFK+(kvBHMB!XzB)LUrLjGlz48r!O&^sx zRxfMIzBodC##)gb-Fcz?dFOO%h#cOe`cwv8Z3^x;m3yd~cy-a@1e$`}JB=BJdxUSNZRbG0i4+6er?Z+bRSzI48z)I`18?< z&W!P1hn+3@x|?K++DtEOdtRbgO33YC-g^5)7;fuarPuXCm-BYQ+*VHN zTaGWeV3~qiQX`bf`S<{|cQU_HX%*%Pa_IGKZo4Q8C8oE0A6bM}Ugp}Ioi=5e{^qR4x~H|!8^NJ7VKaKL zPOFk|k4Sq;se*pqO@-Au0(w{>GVPNU5vHY>d^z`Xzy({chsSC=S9NH1(1-6hqefKK z`)UF*{^@$v6TPe(2A6Vtt0M=k41x*9)SgfmUmzuhMTn$dt2l9Ay79}DW8Lz7;}xJP z0rCL{gEzk!ml}9it#rZ~oiE$`xJyeDNi8f1sy-il#6b3<#NFjaTebSOvvIeZAM=j9 z9lvy5YxfP|9kUUCvss_;sJfdnSN&J%@9tDDN=axi0!}u0W^z(js99ae2n+bR)P1CP}zO18P2Ej z)MYyEKWxrVL2ai_w`^1%8#nIT_8}N~H!mZwV1xEcx4J}cm|t17QoB%ljHXfAE5}bY z-T9~YxxXcYeADDy?W6xZ+_0)qIo(zY>z^dmSr-tlefx<;MB7^Vs%f>#syC}=k^_Ps z%#u}#3*PPxMO@PsJ)#mR=|_cj{x0LXvJvv?k1IC5HmF3bEMs!v{34&HYzIe?es znVr3vqH{1$bHQV^#@tX#yUWhgXDd(|X`)>ZWLlxsRpT$l7d209x|C6T=~@2!;G0VI zP>tRUlZ|fbf3SBO(8?|}pFx-}eUcoJTmLLxyJ!0zRMg_5YT9ZcM~&-tc}PDccuMIm zu49(>wVv7&zDCCEjFWExz9>%M(KDQ5Vw_zi>fhReu)KS!-B)G#)-1P2NsyYJj{zUo zw2v3c-({k0$_ps>&zbgzook<{S#nsl%BF$~%xt|^;BfX}N3rz2f>wkx5FDZX*VKSf zN~<#rx&6c|J$gqbUoBfv$3trMZai78=3Cub7Y>0yQ(5-*R9AcZudg@orIweFal@^4 zmCo&rUc2Bq%08-*byQ0~RrSbhV~uJ%mdSdd)sniR~dS zA;m`;O_{wG*GGH%M>gyZjXnBNu)t6{3u>Jq+B>w(vC7t}f;E&ky`#3w)acfWTm3Q{ z(q}S8${VfW^FPNhyeci>*%CI7QxuZ<%zH&7kTH!YA+&+M1|oYLKS z==MohqR|pr3Dl)eW-=3_5~sJZw=8Rq9+4WikzDV8cVoCj#nB=n$$vmjjxDMARF~2v ziHu3=HQ0dp!})FV;+>8?pLSn2Qd!U?@hP0ceVveJ4e=Y^^j^l?T6^^>_-J!m|g$ST1pVz$?zfn;sWVw_w0+tQD+8BoKfN2x%}8JAxx1 ziOBFdToQOM7Ng*>ITK+x8SYD=!tA+x0ER>2kZ6RXh!u^2TPwh<_zWh=)4}Nr1o%XT zhY5u|5(*U)6N8MwBDwrfl$nKv1qzKpVK4~L0wIXy2x%e&N3cQ+F^}N@2}z~%9)}qSMB|~%kZ2T}jr!3;AasldLB0g^A3X%# z;I<9r2?)4Rd^+G54RC}jeuQAqzxnf`_z`pIFz6^C0$_ut0&rBbpN4dHrBJ_lh$#qV zv3YY|AlW}z3R%qWWc?JISTmQ-kAZ;h-*A7j{+j!oF=$1hkQ})5C~0K$UKz#(u90E@9;5HS`s`VUa99D$I=p#x$l5FE(@ zaV*S2Xf!O7hA_wCF`y#|X3ivF5r8?CNg&|RShNN31H>9W3+zf-#E(&lp%@?(1A`-C z@a8xKEd)(K;0O#5iViRkbS!~sj$;x-Flfvi6oXE3;_}%vu$(M5EfhfUIH7Y2G2tXz zsw)|eL88A)s1YW`-l0g8k$ z=H6?9I}nHj!PUXm+qd?0wyld-q|RNNu`2-U3I$WMbiYehb&<;g&D!a_hs*cby8r7{ zTWhsjrLyjE52rdqQf#IjaNxLC!7H@fu8y$uC5CL;(%0y(Y)_uzcBmc0AIjeZ`S@rfUi<9n8s%%@iRbO>O-e1?AI*vMLg!{C-?6b1J zPK$_6J0`zehVQ?Bc{jSP@~nG|uXoapGkW+O_nxVV1C)PC)ylWe44w!m%9Pg=MwLJk zuSqAJ?^nj_1+4rFa4k#7?mIu2@YxN%b6VI7#PXBYw|TsO@b0Kz$l3M#w;n*3h;lU7 zTb)|ivAf8>R|R#|x4olHFiWgzo51U1%&~5M?V}pT^Ue3icdijf zaJ0ie=p$`#Scem*Ql^;* hl}jVGJlt~3Mypnn+!M{b$y?(>{BDXYX(CvrkSg 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

6Clqyl7esX!=#0cE@l24J;N zfI_L;dPCOidQ3Ii*y=1FZ@gt^W})X%`Q{m(vrha#q^42`x{x#iWJli z_bfHt^Z>Kgp5GiA>TI=Aa%NdWetl1O=YH<|Yl`N|EpPS>^o0S72~H6|pSnAkHSlV( zEITv%oK;X~B|dy=sOCwlDn-)t$CLr}y_7aEtt-^;bkas!m%YJ%^;*?yo+acKg`a*lyUCV4V`HrU?CY04v3 z=D(a#e66!~{Ym0I*TJiop0GU+TCm%}zCer0Te&@qg4Tidg77Jo{hIfU!KW00+=5Q@ z%6;zwvI=vp-Wmi|blXIRCO4)`JDR_dcGr3t!^*_vs+;Uu%S1h_zx(l)-NlK|7Ef{z zv?Wg-SHRxj;Nn6#umGn=xqHd)%%AAYy8fy|`wx{D6_#I^o>^|v z*qkk0N%50OfL&NfGUt)`FY+mMSx>i3>#o_wDoR_F<4`HP#9HJWdd?{hZd%CNel%Fz z=F%Egy!Ys-+rjG#>zxkVSadUZbxb8QYU|_7d$toQEo?kq?!;fdzRdSuiI@$7L&PgB ztD?S3Z0`8x-=&vttXR1^@kmBMTl^L5YSlfliuuEI!z&KKi;stv4SV;d{#84q-*O|v z#6v&@LzVAcz3IY2}#Q?8mIllOiFTSaJ{{UJ(XtsyRo@vtV`re(-6c0p2&#WtPRUYy2c?~7DE;;qC;rTc- z&(ODXhrnm2^COr$FL`AyyFWZoTv}UYm>T>r=jp3!d2gor)s!k-dmQub{5B7+_G~$x zK~#lzxR||glpIaOv65%97c28qcccbQjah5jR28z`FXGCIwy|6gaq^&Z)u-a553Z_yAy3*Tk&^E`O3W=_a`dH3u3>eiala~3}}Tk0QkG^tNV z_dB76iLrXY&(WL8^^Y76d1^jT(@1VR`C{v9qs%l@#k-4r8KJ+QsBy@>b7u!3lhgCt zo<8u;$lYdHdp59sd_UBo7jH4Yn0UwfptCB{{PC|>(XG`hWCjng=Csr)+nTLc6b^bX zsNa3^e%o6YBb%uuvtzAT7|hiGxze}2l2fs&yi4-^F-_bVnOEoarxtdDDw_@(4MRP* z(C!uIo8puC7cHd9iu&{e_TgnyF5gDqvHb3qzjcAt!D%MOHv3Zd9^4&2F?Q(4oQlf9T$u-zE zEH^e*W<9=o7BH;WQS=OBzY*trGVRX?6&5d^GoH8Wd6iubDvLq~P_Fnnc5t!h=f2j| zWLcuqxPIKjhZpJTm8LZj)HoND<6=|-&xv2+9#J|~J2XRYVF36{ro%nv@(n-FPR}^lr>pW2tzr8H< zc-d>lfazv^v3+gqfkMAXV|EeX-vv4Xk%P zGJL=J{=EYieq_^}rx~Jg@NXN_yzHhB(Lf<;}_4SPzDFXq+u3w4;Y&B+}l*-1(?g-D?L7+}y?&*k2BR zqgF+zr0)+2-@F+opvns_HyT7DyH1=C*GTo|hs%aGj%h4S!J%OGA1 ztOU4llu*LJy!iDz1`zTYn3WU`o+D$z(L%ojIUJaT>a=w7R+}mdq z0%D-`Dy)ixmCB=V1b26L9G-|H5N3rcx;QMAm1%X?t|xjCBMt|A_mU_J`PY%7_++L-&^QVzutEy%`wo_;kLM zC*;$0uVfO1OQh29AeT>a1Ia`Z5p?5n@gSd1Ci4h93XMi1je%lI6iP_KgSAiyI97<@ zPz78bi4Vgdk4WHwWFAofa!KwKkU}89GQVNFU3M3b` zs02KgG~$GIV(3UX2(^$lQxO221riONDTg7YR30RiiWnHJQ-Id;V>bs`P<%)Uc|%GV z0mT!kbUcYppayC0ZgesU#M9{bG5S)zP!RvWw6)6z@EDnNKcNB{KVByqSyO@Vx{=mM zQzXyCxK%q!{A1P#c3EKMf5oD!EUK}5l9 z?}b4jwY|+(jH>`kRT9tP+*3tI===^wvHg|u+;t;iDI*sc$auZ Gru+v$l={p7 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e3ec17679cf6b9962aaee054cd35ebd40a102bc8 GIT binary patch literal 5630 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 bGEZ#;y@rOQS?*a1hmP=ao5w10;jH}+9|xfW literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..bdadc8c0bd5a4b301e12a3f1dafef5d45e0fa0ee GIT binary patch literal 6042 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5c7ec87a25180910cfcbced1ab91c05bc7d92a68 GIT binary patch literal 5792 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 rJmyobu)}IVUG~xdMqt511 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..cd496dfc27bb143e429ef92d8dfe919951f3df5c GIT binary patch literal 6943 zcmeHLc{J4R+aKH5Wr?zsX@o3eRx?cYePW{QQkj*(%rGS-Zb$WEb6$or+9>N)T4ocBEEyubgNbLP8T%jdqX&wbtZx$bYG(^gwaF(ok&2qa0c zv)T^aAZ;lEhY!P7Ekv*XY{0$!G&gEW^^@Ol(yr^5V*(Ce|8)AfwWO%~xd7+t zvWd+TYd4NOeqLV16Nx#pff^(JQlfOT%UWCCwzlXiQZ>gVL!bF)<?t65+@Racm`Va{ja=c0R^1|1Ez4Ogp zN@7pe&rRt(d7FbjMcc%QY?ZK@sLN5Rj$^-kn)YVQ9C0B_-%2c%IV=t9S*8A7*)NS$ zWK4O`Q3Jkd?Xb=><1&k8H-Lk5le8*MBpB@(v z?}}W<`VzZ01w$R&W|JFgQLISMVRUp>cGl(f%+L?YyL(m4XqWbHtM^$Pm=}LMaWK2r zt|b4(wnFzo>;YH9#3aRihH|dHcc0w`WVQ662e?v@$CVf;-6 zZX#u~jO*YxIhIYc9BYbw1TEGFKQb>@9d=%lXc?^*YLk!u_})sj%lDF=J25n-YUnD3 z-tfV7T)Vhh{x0XVmUpSJO}*(|J6(@8yq6^S5O! zH@AsQb*~#@FHGN;9GYp-Eotg5^&|woMjcyyW?-YBLh;UWua}4A2WIvbxz1}}yPRK& z=>{uqi!`mCosRGO)PC$^GIA#?A+2XEF zg|RC|uq2TS!kRD}<$Ks`j@R0w9$kjr>mtJMzTQ|-Zt$_XU#RHH{vKrM;XHnqdaZ47 zF4}L}E|77Eo@9w=8TjUs=SM6GUI|7&*<=4)_1X}(BNBCg{oaZs)SgOh zHNPDOy)DBpo>1kY%X~yk@0f3XP}ua%GeV7UY?6GrK7Y%zlzmGR}uJ=KXynHv#u&N@_=>x>X26L4DeyEb2Dhk2b0I0KS>Qr zXND%b)+@D0>^)o9uiluo<%+pbH}p`VQej5PGe7S(Z~xRNi2s$okFHUVz<-TaJc^B5 z8XgL%qBUojaIbI7nZE4T-;~p4uRGvYg|_ZpwV!djs-r%xqTS(L+ZIx7#iGsIjqrT0 z1xNm#>&brk4=WqTMa9<*-#mWo5vsF4a~sY`RC#S)UBXkzIX1gu`(Gt}ac=Y|o3^y7 zhYQ=4yIzAo9=VK*B23)M>75d~Nu;T8T;G;kMxMvt=qCR#D?o!9Kn+O{~4 zHNKy)mgYFw9Lpkts<>8i=$68Mse9fhts^v6FL7>GMxIr$f0puGQR99@e!SgZu#t|V zQN2kga;Jql2BPwHH7>qB8~^D}w*}p)WTwa?!yckG2p#chr3YCml}vXQKA$FE9g>Rf zK{3@Z*7|Oh7zsPIA!Dh?a0%ax(=w4qgBR44)2fn>PCvTE-0NVovzZhAcCTDXuedx} z^`U@RM4#l_wU0EhHZBaGEt@mkEwze`@Py)}Lo#3pE7R3|;gqX)-JC=$?16@6S1=#Z z33^fQV$*xPGapnHtlth+y)@Ib1*W;pY+U932Tm{)7<6G;+mOzAUS*RHl?nt&Z%;-+s{R+J}l(r>joaltb_7jLp)Hns_t(nRRk=1ZLJ4 z?%av2Cwmg0mt$OzS~dxEq8q=Y&QK>SNFuR?tI^LA!fw|w#or7x5eTy!o+^165$K$ z$0qh)N59RauB~_OF8MOa}bE{Aj8tqiC}5@cYg+SWjWDDZrHb&skH8PPKIVmxk(4# zcG~PIEgzhrtHdG5{}VzTj!k<5)vdY*rW>i#`mUZ1ggFl zu2q#V{ie62uHcq`B z7~h%V8*yfyE3rxF7@x_J@YK+9x5m5kAx&xtJ;b+v8{-x>h#O#gCZyy7l9$6iX@ z2pvd$G<%CgzVf)0v!=2lU)E91cC~Bao(;nl%@N6!US*Y^jOWDTgqZ3rA=!m?KB9yo zh=t8Mc^A?Kn^K#kM9gHn@q${askPooZ+QeqHxDd-bU=71?qy$gYY73TwN{uPer*v6 z{xHQ~7SD`P?F}6ld~Yh??$y3KfWPug5f1M)C?diXSo3rpv0p$hY~O3mov4krGmlm$ zS-)CLY1CRJ(a*maz+z8E=a_&z$NcBO1_pu!>K(g21MONEd+9Xe^Jf7%mQ&Et3HAXR z=}-pHFnbUk@Dw&vk3?mYX?o$zAfTZJfs9PUgGiJB8V^FI`7&6>(0BDWpb!St80xN1 zL=b~4X?_g5NDj?8a;pm^GJs-8g_@d(8HM8k049w`f`l^zSzLU$F?0nN4?Hi6;ZVqm z2`|7H>OpjZSh6`Z2u2U1hk#j!GeS{N6ETPphf2q9x3c*G0lXPQ{dl|}JRBYt7N!@5 z)?;&g;YdS6LpTBjN1rN;*^uoEAu90;XJGROCN~v?UOoetIlZ;LBhJt#|=s|HG2U zp#LK4AF(ZKR?_)B5WxK>?mw*m&V9ugup$!iR%}Yha(DzQW9ahucq*I1pyF2^=~RpX zhE6er>C>=u7)Bq5fRX7WDoh`XF~pJ)22>K3{u>m5#pRJ$6xuQr0ItUXaBxU0ieiXC zz$j>a3JgQR&|xGx1p_lcQUN>+LLWh={s!U5VE|Q03j95)Whg2DMMGi@G5Q7uFcJ=J z07Qe;hZ*RTNiZCVf<~iIG#rhDTY;id@HT7?lLSmBgGutG!Gl=7D~e^p@#anhV<<`w z@k`2%Br3qR4hY&tKD#G#q{0z3j-0R_6k6=c(osnYtV zeV89@c^&}DUnXmiw~m}gWt9o!0)>Z*t&q75dL#3{J|Fx z>wok2V;=sSLqH&Z2KiU~{-Ns+UH^)Ke`Wlqy8h7huNe4O#(%2o|BWuOUxz6g3+RBt zfTNP^`T#lLm<1-=+ggE+FQ2&12{F2Wn`n@oCl>?~2ejgHe4y-{wSZ8VM<7}Yj|oVN z$rv){jLrU-yim_k1r-r zbY_dSDj{NTgT0loS+}iQnU?&S=U`DQ=T;EU30O#4s$7H5ad06`jW6fO?WCvdCQZ(F6iH>H%sLsi@xdJ=d&Y|2D;fk z^<|M0yihvS-z0@9zx0*5TYOu!+sRiL$xU?w4`U=CRf2tm@*eKdh~D#c_imre<+Bb; zhX;o#yJ65+8FnS61omm)RnrUO*s;mv_w-;}avg*7Y& zC@(ujeCcYmg8w~ol>|>I`^&}blv0g>_35lW|56Q|y1b-LY)|t^vs219TcVSrAh#}R zdN3SOO@WqCHSFoz7(`9~?C4Uycq|{t;5ni7T3Bzaqk0ykA;ywk!r7d1Ns!#thZWSvhdmeKf2FE;s5{u literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..69549c4060f84e4c52c4794ed6f1c5d4986fdf4f GIT binary patch literal 6849 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..532f2d278568d512cbc38fccf13f6c18d4559372 GIT binary patch literal 7186 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 AWdHyG literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8a4d83323b9b31b2c0ca5ef1064d586cac0ce349 GIT binary patch literal 7691 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..841d29e1728554add170c84de9e969dd2806b426 GIT binary patch literal 7956 zcmeHLc{r5q_a9lx9ukEbV_(LMF~-bf$i8Gs3`$wXEKFw6%vh2(v{)jvP!b_amaHj7 zN|7zQ60$^O3CWW18|r=E-s|_huHW@u*YEq^JJ3wc|X2>(>@O&Ycw)I0pa-O$Oo)vF-5wKp!T9Ores1>=QmDASr-C1^@!O z(-XX7htPSUYb?nko*8~7D#*M+WNP*kx6*y;jq(E>7{9Ao*B5!j!$P|o)|S7()&?YU ze3eaKXQi>H`IWXgJ*ur)%3AK)gJ>He}|%F5E4#Jtt$<@uz3s=Ct* z@=E-kH{9QkmXm`Cy`FmQ&k7IFpVNdq#e(N0^xNF=}ss5{2xBmMXq?N&1r`%kp4Mt(?HJv#PY+RnnO;Coofy87udxtVSx!jW6IfKSnyWk8?K!ed zSJCEno(U$ZFn_e`knxSC3%ThgixD>R%SRT;w!P<`BfqyS`W?x6;Zo1HhgwqK)azG& z{ishHa^U%+sUH;yPs;|;eMJVNU0Ny?ivv0p6YnRh%1=W4AV;F;^T!gdT};1UWJBID zJD$HZdA0<;EeA78)0iJl+@YFOgGG~$YUhom|*;Slv%`l z1pg1e;tWC>hR|GBru@uQQ|?{lw*Wx?;oY>ti%6ADkJ`u(^&|cUWfwM6Bk2#Ur1TFe zYVC2kDevL$BqjGyctKuAUn~q#rT5Y)QPP;lP#h+m-BPoaU~|wTN~!asy`^@#qLo!a z_zibOYw;LOvt#q9kMZ``6IriI$_C{d581`U?o%r%B$Gz!My)S8-}0_Fza6^|>r7AY z5D5w#Ey&QqEVPdMybxU^q{%Bp!#)>Fs5U}}Z0yNNq)y)U>v|eFWv#K-oDP4R?;h@C zd~geBbaiFDx9>hk&p+<;QTx0DI_$HKtoinWfTQiafbOg}QB)J3&!}(j4$2-Ble-o9 zxd`7u2!Ps*4;G4k*x?_D!z|?gAS*suY+N~m@oe|4oa^zgJT=?h3~zr})1-<^-IX*t zwA9csoc((0h<6_&2bt}CnC$H>bo7zPVX#p=DQE0P)2OTq-$>T#6#2)LudLh3YIED# zDfHK#@eekh6${^?aw-fkgvIqXbvpZEQU(xh>E(c9A42F0`V(cq^ErMKlfL}fA# zeYBgA5SnwC$Uc0%*sSmMR8~Z<+z-Ws$VZP=SduzlTdOeLz{x|3iTN#Q_s{G+BdWLi z)mqzVgQZM*)A3S;@(!D$`*SCsJ68jwQ`hGhp~CY|qHbWstf^VFSG z$rzHkN4=A~@S6q2rD(&gg0FT`%0b|ZY8mKrHPfvEPqji5O35c$j=r2<_KJ&l(+C)+ zH+UWCGID>o14=z!7|1Ofyz)-sq{N;s{NQTW@gI*@X1j3;J0L}ne*a61$79d)R*W0n zcZW@+yMlwtZ}z?!8BNDhgL{J8_u}rijclBd;fu>JjAIGJzlc&xUB3K9YlaY6U{v zFU3g(db|5v!6*bZ&tH1hH0Q}a zp9kr*CZR8upbN{5WfI^go4N&^b`(qU#BypV`plgJ@5OX6UVhRkQg6>axohMXWc^fm z4chU!xm)97x%Ft2y{6(V4BDOc`^y!87b z5SkQpr&j+E`;+>Jm8*6s_D<}P?TT@@a5&S(=x(sPiSDC&vr#fUPn9R_bQ2VBFI9bU z-rlKg;p}BQ-&NSwo^a)M$UBWLK}8=_0(X0s$ajI)~BP27(pVcEwN)4E`F zK~Z$ev4rx%51G{iLNpEVshNIMllK1b)OH@5eUcIPcr$hCQs$q(6|3k@cMM*_Tj_I| zSPD9e*z#}cNpU<_j=gK1JEF|@!QQ?pZ{T(D4ZXXw>P#JdyqByJqEqZz|hOB3y0#7GiZ+Bq%dNd%g`bxNS;QtKe%Oaij4925+o2PnkE&fqFc%e#@w9GBF0y8n(77C%DCzxBjz_2lXH~CK0^bQwF z_G*(ktr5fGOP#HdH zFY(GoU~Lz0mz`)ruw2RQ1Dzsm5y@T3UfOld*3w?Zd1$2vJW3ZXBvtTA58|r9jV6bR zUgJDuB>0AYG+b6OgFFzk#|ioxkdE2WTS9{;%(^TgRxhg64T_K>$5J78?)Yj7?VzXV zM14?u_o=g|&LKoZ(CdADr%c@uCe6OM-%iS)dyrKvhdGdbQ&%i?=PsMU0eS3|VxOwR zud;$x#6_E%Uhj>7s(tM^F*4hCCozLRJWWTz2Fa_?`!+?qdZ*}9)K=$E%r>i0HK$As z&0FcC1vb+kjdDA(lN4a*>KLH;n7BDCk8O_2i)g47tPZ@p2ylR^N4 zMd8a9DYXOrRA|c0GY_-&P*WWI?agLQ8ychM^b8OTUBtoEs=|aN=bQ0!pIW~UE}`JX z!!xe$dCpIjEGCVJoh-f@ziKKy{8hPA&?B_QZ2L6ROVfY!N2-%%8${GgAM4wAEM&g-AmNWCSIP@TF})ovu=mIGrG?rsJlEIzu}0Y6A$m^_mioz@>*4Ybh-d*P*YPg8Wy!WkY^&@7U62e@`^l zL@D$h{fIS=Wr$Pn*2F&#|JG}E}nj8CyF{t4?R|wbhJYl zPB_`sWtsL^Qo`z?-YMP1bX9LjZY~tkx-(( z6Z3jxpeqXsPu~6w`gppzD=3$}4VuVT>&GX6YZ7Y2Wg?yLZ{}@9wzLY_RoqoIN)qAa zibqd=Eb*H?HoBG~uuF@2WG%HnE3ox--;47Sej^vnK73lpvt)o}=4h9=#)D$BE3@)j zVghs+A$M&p;oqAQ?XES8HKg&zIfl3HU;XOZc3yzpw(T{xq;p4uJHB51> z)p;J{UOqEG=hGjRZqJmZNm=h3(~ry~mrEJT-P?QnqA26Fd28gI8(iYRHsEf3vGyB= zZYrrexr#+-Vy%v^_+7`PW}~;uIO}dpOFI_vD%oWS;e%Tc#C2}se7Tlod*p`Y_Y@K; zx}-CsVmgwBGdk?9$@0t4>Z9Gs%S#cxl_et|_D|zMkvwrX*SOwQwCBhgg5CiDT<0k! zCbm{4CVwI>6RT|LPTuu^)mp8v}9v%jttik+Hh%z+O zN;oPBWojn#NyZLsUJLoqd-G+Mm7bdP=1q{&6>Yw`sZF^TXlHi6_Z#P#Hsp44u5t6; zRCFgz5AFPM%N829RF@pKk6X+C?0||3OwnStZCkim|4L+qny66UrWJ2GWAap@0pRfG zV@rHUB+p%?I@g5&e>Z=|S`uYpft!qA-mPokbLVuZeiTk8dI)QSCNOAPcp}4tq!mE( z;dH71fWBdX51!ynVgo%$o)kI;^rgHK1f&o#ASYcc6zgL`@}gJze3=B2nIDPHR`?x)NcdCV$CpXnaEC~M zkf`Ar+0f#U{WnQAh5Q#;f3t0UWy77{4dJN&iTgL{ zKYZU1=15_&Xj2BkcilWIQw(T*e>9Onpb*g;Lk}d9L_)%}!DMZs9vH5TM1qmJC=zEx z(k3FIx-cRF{u`7PoyEq}38Zx>4!9PDg9Fnh!-+5i91O!F31B!83In4M9(rIBN{~oMBF^MIg1|I(kT5B*s||+hAhp3r6cG+a zl87FhQ0n0caGk%SvlwKyKb}c4^5pQy;fj->8(aZZe}ziz@ACd$r1dy(C<8-vz<*H& z2SI*03t6uie|W18`CokKZwUNWVmNv~=QyQ{Qwbq|mcn0rtrwmD;^$W!{);&Ma5GVwGr5^89_xc!tvc{wvW zu7L2X&*I}ia#a}#yimlD;U+U{Anf(N6#i!|g8pl%7U7HX(Y`(&yQ^upi#RYoSX<@f9Yc-C`T!b7kgNnq|gU770dS$lX)i= z@n3rPRpF*rWm14<0>(xZdCRb=3$jqn&078Sx1F{K3#Q=B1ZsWw?$x-X_`OAs0_`@1 z-xB~j!X88Xmhr4NYnTtHdXZTEl^vOn`M6Yz9SUMwURs+!j96BGmftec_YQ9$=;Igv zrs4qD^{iT#UWYgLR*fAin@po&n$|9S!8TXjDjis(dxhTc=pFWTWA|_ zd|sZhM7?ruX+L;R;t3#eWq9fAkoCk<_hw0v{sq+OVmHaqQ{RKFJD%{JeSALVI{SLu z_jI5>a;!^&odFsvs6N}GZ$U|zo@y~$jo5P_P#prv9cqu&E*%<9Bd3)o%_UySi;oSx zn{KCDf;4D^15%a8or1Rj#KNzCb~p&GV3dfpXVFzwsOTEYIx$0Av%szmNT~?-AOHkgNT zp_DS`YG;Kd8SQKvzP#1NOH=8(_eFygo1m8BleX*k{e#Z3oTJvaRhc%CFJ~APD5v|z z4fc1kPC^l~aOSoe#cgVtg_>)#VDSPKr|AoFohbPw)vHn)J?2!OuOuhTP1RahW_!3# zy-Lj{$y#rw@MPQpIKrKt!a~;F&{|S`vMzz)ey-wF0R3UMkMkSkVj7n7Co(sMvPs+Z zCN^*-rQ4&ea6@<0dL0wP9N}9tda#-2*;6lk^&aGIUy34fPjz`xCC}Wg!~34Bt}WBh zbIO?!yNfK&PF``y>B7qvjn(XQYWtB0=@HC-fZuszue@ia*HK z+T{l~%v&lqGtIRS<*piZ>kxYlJ6#Qk)UH3X7pbgN3Exl44Na6dxR!j%K3qSTIq%ac z;l1n$w-W!Bb*CEpu?_nf6G(ywX;1x*cgbqG49(JIy?F`-D*|_WTLqT(R!J5_6zbT| zp3ktx$K_~L;oO<^uPw)3c;vaM)ZD#tqpo&zM*g+B6QIwI>h1<%e{|q+fXH2d8k^Ut zd2?&z=C@u+cR~VCjdPJ#`bu>6)~xHel;|rd7`iBy=8xZB0hC(#Pbjl*N- z)iWlm&98JAnvx$__w*wR!cAkI0auwV#pg9`l>e?ulJ7oPTzx;*sLR|adf}FfR3&Ru z9=+kLyCe6)v^iCcuL271a?rw@t_R0k*4#9TQ+X)#OxqfI(%db_Ki0^=yhuJEO|T>F zKu8*knvixHd3M&im-`%V?e%HcnQ2>*8KZupr)YpWxbvZ#mveR1fx4_Z_t=Ly58Cg) zkE`4N4q8Jf- z%J^DOdgX=gfy*A27YPmOeP!$4AL4kAEq&wPwk()IFMctqdik70ofY1?;*UUMBM{gp z3>K+0Op+2-Wlm#B+@f-ambda*M`Bb&cl}OTEmo>Jr**Wm@qGS%Ft+tV$+1#;{er$f ztZ*N@46dkp!=JP<9S59UaKu|=Qp?amtnJfU$~a5&@g3cGU_0WU{v@+7hnMZR+qxSY zr5eYd8R~DD>r975zn#{D-MUcdFjLq!yQLw1|KtNV{J;=ci6Fl-DLB3)0&ajaV@or&g(x8W;s=s zdZzL>lokunO^=%&CvJJYB$?=G)Nyt?Z|!)Y{d;QG4Vqwb0M1lJmSZBEt97a;r6O<6 z9)kzgb|bso?A=RS)_Lq7cP8W)UC2P4*LauE-n_u*7`gJ|$pc0zS-p=1Bwux|Tt5H$`qeuT`=)zp z1=qNl`)JJ$PBofSVaqWtdbL6L5*_d$Df<#xEl)`$!o*X5ZF)$EV76k#Lik6y=h;1o zC0o(RqPU^9OPa3-EC*WUYzuGE3;pHY%x*g^d(JSuYBXAYZI70G9K(rmBIAw;yPZ-# z(B9GOx6425=!8haP-(B6RkAekWvs&$8;e5D%k;7M$}=X&s_v=|Wxu5HK6DQR{d}lz{qd7F4QS3$eOuo)(Y*vj&EMnU2rXIXQA)e!coh2>d{KvC6ys*$DKAz zqh-Tw9rb6^fklp|9TVaS{WmN>7$jOdm>ya?8zVHyRGyvEq4FvESxegWQ#2A%S4AFu zaT2RMG9Z68mK(9KIkZ=C&|1OE_s04_`NE^wB&u(xsw!7O=aIf^tAeQ{vVEB!Zt;pY z_1XyzZIipok(wH<@{@r)e*czqE11u#jqjAnWW{4kZmk=WO4dvGpE5XOV+w4d@Yt<8 z${U~$X($K!V0yc{P?>zLDZt`0KvOAK0DVwlFbiv`0AL1!VmJfz=kP2MuS>5Y;2f4E z!pqDR<0`NN12|6MLU48XN;)$pe4fF)eUaP7lLqtDZvzja*%RDaR@6lxP_3#rh3>reu04AED-@>v4Dz3OC%Cg z3Eq@1^haYU6bc%HL*sBLNCPDb5@)s|PC?o(!0T~z)$o>I}#uJGF9ut&7LExqw2#1Kl6G0{gk75y7 zcocz108tD8%RrGyBo>ZDB2frt#2+A52{}+z0zp4|C4*u?Py`&oj0CU=DA0_}KoQss z3<|)ruqcp-X8~jej={oFrl43%sv}>>1)y|txPU*17V!M124sX&ZQN*<2%IVAyTmOB z5VIizXbo_9EWSkaeTdHCf~&=Vj880ygeMZQI1442g>QL7&g!u*3eFwru&pEj}mRi6esM51SHwUQ?^V;LmTLUxPSP ziwO>&S{77*`8fm;5DK!U{DiPR4>1D(o<9ink1qxLRnGYj#Q*|k7#0b`MzJXXltB`i zjbe}p3@D9k5`#!0Uq(ErAV#gxJiEe5jtEQ4AX)CtkwTHzPIAYcEV$Co_(Kc|4h ze^2sD{QjovH(kHPz%MEPuCCv7{SpJer2MTqRB|bekq{@)5yc>a%3094swe7ZR$7X|4_`uN1V@k+U)vCO(6~~xba2UUQY}4Ku3?UU3aG1RCzgVbM`s8OAgl4Ow@Lu^fm_& z61)CRmE%QMwDpm5=mBNdrj2mtS!ZOZRp`Y4K)`lwL*FP(cX;5(7JXP-@zDDZy}iBN zAA%Fvh4FAzwR0hUA-5A0(bKPgBLyRv|{_X3SGKAp`{wIwU4ed@@w86by?bH2k(qef2CMA z{gUc65%<#LpqN=&yvmii$IFjMR~$m!PDok2Z87cO%3R+ZwW{DVZ(er{dFQ0!{zOiU z4rab-%jo97+QKp#H(o#tSnKU>crfkYiJQ`yX(!5}jcGG=V`n_<4S}vpXhIEM+S1dh z+YA$0`<+r2kC&h84>gCbv=q8!%*5o!&V$`yX`s&xw2FUZZAp0vQnNj@)YAOkT&+U} z+5d!>&EcV~x&`D^=g8s~r0VdC6CB?>#jdq@`Su+_>M zPv1~-z|p}y{frudgz$UxuuJ$dwS2a5!gJQ`K(abE8NLMUqIC(nZ!1 zO18KW5h=Ue$X+Q_mfwfE-R}8)&-tBu&hPu*{hagJp7-;--p})Tp7;B_pEzsFLp#8` z!5|Q5hp7q926#g@ZUH{v$qo(u2m*meLhX)nY=}V+KNgcp^QAyIA$}AHC74D9fr9&T zQ)wwGDuO51%i@n4p};8(!ItyaFArxzW?5&KUW5g{B2!QTF=Alf@%6Rpwy5Bc#MYBa zI?0z1$FokpR+jjTy;D^?E55iCdH<`(dTli`LrD6bUZKJIIA|MgW$!Muwcr+y1T~?@)ta``UYoUCs%Lak9GbMfZ- zmieD{PEGZwub4u^6rsAum2sDQ{is+mzHdGBQd@F^V6P%iM>M_)wkqE zr#Q^1lH>?{?c<$Q9Iez8#Hi>Mqs8iX?YIC%vBy1k9#{8!$oRqc%oO(^bc6co&5a{@ z{`E2J!OsyIVg5}4_%{BC{x^*h29G*FKO_nb>C;QXUahUt6|u(H9_mD$O&gE15mqc_V_vU3+4Y zlTfb6UziZa?b6sKLMq_rvMxK&M@ITv7BnSF{i2+wE9=6;wI_RzUZU5WXEHl;`(+>-&{gkmtnja z^0_i^;&unYB$vb-CKZ&apz*`S5CoWuYgVjGrC&H5dNb9vDN($WNVaQ>wQoGAc6j=A zsnASR`?eHu@IKl%nofh8n zMSM=OuNKq&>a-k3d9lB7I|d!EH+Lqmsb-*p+Tl}#U$m;Oj)@y<*NfEB%_={#EG)6N z4;~@7=X04M;e-Cy9%)&btCRqeet+N&p{kmvm4DD0=RQ6V>5ndZ*vhvj+{$sVx_T|d z=cT!=Qn_5s;zff8@00M#1W7wedAe&_Y#eOg^93)v$T zlrxOg=eS^7>VLEl%|(9Fk}lQvEXG5;mAOkdhjdCw=-nOv3Pj7%!dr z*fP+`O!nozlX&elTC&sSo>#;8mmRaDm_d$B*T}DyBMCkI@sZkEC~8=Fj=2xJB~rWp z0})-lvb)#0J5i$XTAjUq>+s|GrBRm-TX|RVD$rexEBna3{;uH{OJ(K1B|OrbwlRlVMKdbQj)hfc!1AOP z2JDQCWS7;)^%XKjI@W?+bcbCrvw&BVZ1uR2{Jp^G;|<#u?UC!k)8k>% zk-cg2{@Rbrjjk1)jXip=TlMhcDy=WKyM++mzMY=guPls&wS0Svbb3GP2Huh59Shw` zI9pP~b0p$Z-<1pKsNqi&8RN}G^s?Pyw=0J)+MR4m&kjtwFt~Kz`$Ri54xf9r&t@g* zjh08HX3w-!qv|R;WEQtR8&$+~UZ9_92KN=Dd&XLC%O7w^G#yzk11mguozPlCLCqF0 zKN_cf@l0zsQ$96l;<>|sWUM@2BTK$+o`=^2jXGu%9N!wZd+CSS*F0klKkd1g@Y<99 z>PgyG))f+a#VbV4XX^Il6wc~APA9$HN#M+_j|dC%rBr`05!c^fd~KtAGN--Yr|~z{ z9I=}9+jCOc_k-arMaFtwY}y^!w|_iLd3$D0k4N}h!6%pKR^57O8Wr2$R9=C(aNYbx`7QbS!D}>u%F$OW$1b-3=lcSbwuc3XcT zc^`Lil;zs1%fUzUJ z*6|9m!cjs_{`=~((TKJKc)vV+k{xRwl)#VuRe1K~H04M(tf)85xm%^|CH&~|s@uh3 zv$UZq4>IpQg;m5mA%tQ&__oSjj4|X~AysmJ@fo(Q3xw57_CptR`_K3phP>%b&bbzm&I$2b*RVqiCFmqun9lwhhWl~k+W_(#G$YXTm zU5j_4pz6e^_RSru)y|!d0*b2yPco0^XB#R-xgoB8R?m~+6?PO0P7G2R&K z*w~P|H8j|6*l}_MOm}=lmQ(&#;hqu5+p6z7$Md zzQtRI3*1eMH!8v|ehoBHQt_p}aPGMKI0<%%YOVpxNMo@F_EtoP&LKYMD8ILwwAk8w z{EsY7!ALO@)~2UD(^W_hu{t)C@Vj@4amwunS5#*Rxk)}a zj}rRS%Zy)_Z#|KhKmSRyIOg6|ZGq=Y$I6Nwv;HFr+R}1g1kR1Qv&@<{* z{r*PV*sm)tu~D`$Pkp5BikMNla>4w9ke!Jse#_kv-G-;;qO1g93 zNUZ;$9h{?bgxGAnn6R9j(z*EgYWq`q9=NVn!{|%GfLY7o)N5wy+s@R9=6kKRC}%`J zx}VK8c5)_M`n=hj2X&pL=WSw^Lu2!??Zi`FqHR8ApN_BOjwgRrJvj81Dbmtj8RZqNr}5=R{wP%k z8JF~c06%fwI$6jX*Xx|O(0!3eSo!JqBb7q)bNX{bTt;P2?8-d3t(2{wpY|~E$jfL`rH<-6oEsgRc1`dM$^6RRuC(PM*MY>~bCa?78biG@+GP4tTtk5> ztZ|}ojL-GLa^iGpD>L0(aGRcnFZuaGBhjOzyNM-HR+_cbO7xJJeRjCqL}`7$e%O7Bd)lJtFctC^NA;o z@vr|nJeby$Z;F9Q@^d5auB!8WoZ~KxrANv32Tt?8GvIM_YIgJCF3ZfpV4eOD5TNr& z^vYNa@gRbJ8C7&eDIEInTr3_xvYPM^CbWHsd)0@*oQ+6706I41{h3c&oA<`PC&!n9 zgWQ6c>lw7AB_664>&BBdK6jug38Vqd$5D&JSQ3+tAd;CL6htuH4`@z6AU%U%KO)J8 z!hv{DJZTJl=zMi86hb5GLmkl;Y8HOR6fc@dD2rknYH3Fb^&#nyp#}%Qdcjx#fKK5M zA;ENC1{)i!58cGY0^b{ABowl#!tv3E9<{KB7&BQEh$cc4p$5kX(*jY@17L_Ai%iAZ z;0WI#fIoex7l-4AMIwWOf)GI(2qw!Dsjj1=gH%HyQ7AZ|0cVFWIK*H$gDt-S@eKn< zVUt)iKMsw_fNWqAJ(vL;eJB(dhy3Xu-Os|}2RwuQodtjoWH8YWsg6)X(&@;bE!Z4< zAOP~+q5o*XwgY-Nqz#453}BHc_&^GSBmXl5ne@ZnFM#E{84j6*r1(Cq+$*h{ljK;c7ZsXt*XBr3Kd}k<{RtXp$xxgF=x=8ah8g znKIZMB7;QPfC9h~Gyq3UgGkmPqSfJQnpzrgO%Ealu1%#<;2KnQsyayrP1MGae}b@L z(SWKX`u^Ff+7<+L>!Sr0YKGIS^$k$buByKsiBQk1IWZ+)qc`v zl4;bC|4Vyg`9So(CEbL^2Iddh6n$G$wiN$wf8Tz6X`71)0@+*^SR(0L2y9{?D< z0I#inCdb$y&^FPHn+uedE(r+vIi?nP{wW?|@P6*YpI-I=&GaKvoT1&(hS|$mHVp@b zV+~T3r@rvLee!(!tgd@BuCY5Yb(f(sNK`sifHzu?aK5e^2f6(f)YhH#t4Y`1T-qY) z_wM5HM%TdguS>&&+oWHf%i-5Ip-UTpBZ6}3jC((Li{$g-4o!8O7fe+Y>at5T>vCtm zh(A%~)g-|EBEpBya(HnbiF8&n7P2{HnBI~e%Xn;*Z4VkzvbH~EXTJO83!X>4 z_`44?$UarS%xw#I`#nMi<65d!hu{4U(lS|_tTwRPc5qqbdCfll3aNJxKe62q%hYyJ zDIWa0XLtj9&|85f$6t<4)OMFfW;u%xxo~JZt_s{OWyY|Rik6ulNci@Sr;+u(gD1rr z2vVsNT}_gjT(H#&!C_o%cL4$(qn*(EC;!Jg7zZcZmlR zngiYrYh66yfU6w91dAD8Jn^jJ`N6_t#oKVxLP-x6ANJ*pSKZuWapKAnN`$*$u6=C= zZL{T^k%Ua~;gg^TSI@hJ$xFcVxu+dwE+_vHV)>v`S=7)|>@2BSV1I~+_=&NThCSLT x54ZBLCGC%;m)VIAObtKdg)SqvFJrP+L2J9xjeF2?K%PM$Q@kav$jCkVe*jYZ+9m)1 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..64df1f4b60114d0cb4abca885f5613deeb4fa291 GIT binary patch literal 7137 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d8fd6de2cee865983010c7645a5262b6049c63e2 GIT binary patch literal 7195 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 G9{C>!S)=j* literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..72787349f5d2ae281414af9afc9627c74caedc1e GIT binary patch literal 6443 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..dba5234233d1b1da970829d9c556291d21220c01 GIT binary patch literal 7527 zcmeHMc{G&m`v%#5`xDJ795DiVqkl0=q@BoRtw zCrh$56|!&1@1frJ?LEKmIluFs^ZWky&Y5Ss?(6#8*Y&xt`#R5cp7WL##zOqN`MJ2b zgs>(C*1#3Cc5L1VoLNCZGhAE()F4|&wl&Tl#9-3N6mJrU9mpVoNC6Zw7gs=c{#DB5 z9yQ@JtJC5vUY`wD1{uKxbXZ;b)~v*bkKPZb9d?Ln*9+GHiNqzZu6QM{ew!@h6_Xuy z#6MX$vn?u5aLQN9cePvTYyXPR{DB{<15W~77^zcxR~G3P=U3hYE~k9ht0)tQvKpU{ zn48qwGXH(C#bCbSIJkMTOkQ!`pzpfCTv|pN)unJ~CPaXmHW=WS@Fx9TdYpF3%JAxz z2_c;1M9)mS>-V6EiyMxw-keN2HQKTCGRA%Hct88dS^diCmM=E7I zpN>k;+};y>VRdL|0TXhf&vB9c>}k8tgoE(nzVg`7F-!=mLw@2*zq3q1g}a}XeofnC zUu#j4=Eus~ZI9T-Qo1LmY1X>XJ)8gySvVZGEPUeIIO#%fgTzQ+5T#?4qV8J4X z$BUxqcw+cU!TmtU7KOTrQNtAVpYW+lr+(tNTg)Njz5633p zO%<{eJm=8FdU$p!tNvlxM=5iK!mI={g@+Hwq>+Y}q&=S0dy5h&S0#>mmM7V(f8*RR zi`AU%F)Mg3r2QdQ)OdGbqE+^F9m-h+l_tdWp(02|$rC4BeUwuElZM=aJH5D-sTJMU zuac^zGPh8-9a`0{HLFC}VMJqgZD7ecN~a-sxwH>)AKay8g@XxrzhR0$>USV&hi4EWr(MKqh-ISy3-wt3XQJ1+2p)N zy9>QnG8a$c?JiX0MXS~|*LgJ?wx?Y`0GFK|&Y6K75fOlcD?F=Pc_>@Kulj;qNww}; zPo2YAzV-#Bi>ERhrh*-vh)F`{dp|LsFO&pXhgig1UrILhrH(|N@9$)d3RNW~*Tplm8|1dY_JP0n*+g@2IN{ z$4u_pSCFa8?D%KZmiN=2!?Ze%$(i>$eA5n@(g@IuO>xy~>|?agE$%pGmkKG=njE^-UrU6QK1*HdqrYpN)A_;l=w_QHZM zB6MhwUS&2nA1P_bQXaMNbuPN7H8SLyV3}jtb7Nx;N$ic#%RQrG8N=IK$e1EzTbI7? zp?9ulI4Imb^Q#?Ful!ZK`&=3xdrjXf7oHr45oY%|K!zJslW)x z@z8W1?9Rv9L^|&BKYr(YyM&8wvBOeDW}v@b*Rf)Qp40XvN7c)74}h)BypqdPU&uW~ zcgwYgzEwFKVkAYyHv0{G)EE%n6=dlgE^@xnee>Bo?wA;x;D`Nu(7w

I!D2H1eA2 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/meta.json b/Resources/Textures/Structures/Storage/canister.rsi/meta.json index b3ed9a9889..8a006d78df 100644 --- a/Resources/Textures/Structures/Storage/canister.rsi/meta.json +++ b/Resources/Textures/Structures/Storage/canister.rsi/meta.json @@ -13,12 +13,18 @@ { "name": "black-1" }, + { + "name": "mask-black" + }, { "name": "blue" }, { "name": "blue-1" }, + { + "name": "mask-blue" + }, { "name": "can-connector" }, @@ -58,60 +64,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" }, @@ -129,6 +165,21 @@ }, { "name": "paper" + }, + { + "name": "integrity-unshaded-0" + }, + { + "name": "integrity-unshaded-1" + }, + { + "name": "integrity-unshaded-2" + }, + { + "name": "integrity-unshaded-3" + }, + { + "name": "integrity-unshaded-4" } ] } From 48353172304207d971c9ae351dc38305c3823446 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 19 Apr 2026 05:37:33 +0000 Subject: [PATCH 216/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index d62a407ae0..e30a4ec526 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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 @@ -4035,3 +4028,20 @@ 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 From 4ed20da9be4916103868f5943d9b24aa95f4248d Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Sat, 18 Apr 2026 22:35:33 -0700 Subject: [PATCH 217/247] Cleanup passive valve (#43552) * file scope * cleanup and method extraction * inline and rename var * fix ema * (in HL scientist voice) this should help * cleanup comp as well while we're here --- .../EntitySystems/AtmosphereSystem.API.cs | 20 ++++ .../Components/GasPassiveGateComponent.cs | 29 +++-- .../EntitySystems/GasPassiveGateSystem.cs | 108 +++++++----------- 3 files changed, 73 insertions(+), 84 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs index 35782060a7..7bb2c0c89c 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -806,6 +806,26 @@ public partial class AtmosphereSystem return contains; } + ///

+ /// Applies an exponential moving average to a value, given a new value, an old value, and a time delta. + /// + /// The new value to factor into the average. + /// The old value to factor into the average. + /// The time delta to factor into the average. + /// The time constant to use for the average. + /// Higher values will make the average change more slowly, + /// while lower values will make it change more quickly. + /// The result of the exponential moving average. + [PublicAPI] + public static float ExponentialMovingAverage(float newValue, + float oldValue, + float deltaTime, + float tau = 0.5f) + { + var a = deltaTime / tau; + return a * newValue + (1 - a) * oldValue; + } + [ByRefEvent] private record struct SetSimulatedGridMethodEvent( EntityUid Grid, diff --git a/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs b/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs index 57d48b64e9..1e3ffc869e 100644 --- a/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs +++ b/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs @@ -1,20 +1,19 @@ -using Content.Shared.Atmos; +namespace Content.Server.Atmos.Piping.Binary.Components; -namespace Content.Server.Atmos.Piping.Binary.Components +/// +/// Defines a passive gate, which equalizes gas from +/// inlet to outlet, but does not allow gas to flow from outlet to inlet. +/// +[RegisterComponent] +public sealed partial class GasPassiveGateComponent : Component { - [RegisterComponent] - public sealed partial class GasPassiveGateComponent : Component - { - [ViewVariables(VVAccess.ReadWrite)] - [DataField("inlet")] - public string InletName { get; set; } = "inlet"; + [DataField("inlet")] + public string InletName = "inlet"; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("outlet")] - public string OutletName { get; set; } = "outlet"; + [DataField("outlet")] + public string OutletName = "outlet"; - [ViewVariables(VVAccess.ReadOnly)] - [DataField("flowRate")] - public float FlowRate { get; set; } = 0; - } + [ViewVariables(VVAccess.ReadOnly)] + [DataField] + public float FlowRate; } diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs index b3da10513e..abbddd72fe 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs @@ -2,87 +2,57 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Binary.Components; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.Nodes; -using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Examine; using JetBrains.Annotations; -namespace Content.Server.Atmos.Piping.Binary.EntitySystems +namespace Content.Server.Atmos.Piping.Binary.EntitySystems; + +[UsedImplicitly] +public sealed class GasPassiveGateSystem : EntitySystem { - [UsedImplicitly] - public sealed class GasPassiveGateSystem : EntitySystem + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + + public override void Initialize() { - [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; - [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + base.Initialize(); - public override void Initialize() + SubscribeLocalEvent(OnPassiveGateUpdated); + SubscribeLocalEvent(OnExamined); + } + + private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, ref AtmosDeviceUpdateEvent args) + { + if (!_nodeContainer.TryGetNodes(uid, gate.InletName, gate.OutletName, out PipeNode? inlet, out PipeNode? outlet)) + return; + + // ReSharper disable thrice InconsistentNaming + var P1 = inlet.Air.Pressure; + var P2 = outlet.Air.Pressure; + var V1 = inlet.Air.Volume; + var pressureDelta = P1 - P2; + + var dt = args.dt; + float dV = 0; + if (pressureDelta > 0 && P1 > 0) { - base.Initialize(); + var transferFrac = _atmosphereSystem.FractionToEqualizePressure(inlet.Air, outlet.Air); + dV = transferFrac * V1; - SubscribeLocalEvent(OnPassiveGateUpdated); - SubscribeLocalEvent(OnExamined); + // Actually transfer the gas. + _atmosphereSystem.Merge(outlet.Air, inlet.Air.RemoveRatio(transferFrac)); } - private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, ref AtmosDeviceUpdateEvent args) - { - if (!_nodeContainer.TryGetNodes(uid, gate.InletName, gate.OutletName, out PipeNode? inlet, out PipeNode? outlet)) - return; + gate.FlowRate = AtmosphereSystem.ExponentialMovingAverage(dV, gate.FlowRate, dt); + } - var n1 = inlet.Air.TotalMoles; - var n2 = outlet.Air.TotalMoles; - var P1 = inlet.Air.Pressure; - var P2 = outlet.Air.Pressure; - var V1 = inlet.Air.Volume; - var V2 = outlet.Air.Volume; - var T1 = inlet.Air.Temperature; - var T2 = outlet.Air.Temperature; - var pressureDelta = P1 - P2; + private void OnExamined(Entity gate, ref ExaminedEvent args) + { + if (!Transform(gate).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. + return; - float dt = args.dt; - float dV = 0; - var denom = (T1*V2 + T2*V1); - - if (pressureDelta > 0 && P1 > 0 && denom > 0) - { - // Calculate the number of moles to transfer to equalize the final pressure of - // both sides of the valve. You can derive this equation yourself by solving - // the equations: - // - // P_inlet,final = P_outlet,final (pressure equilibrium) - // n_inlet,initial + n_outlet,initial = n_inlet,final + n_outlet,final (mass conservation) - // - // These simplifying assumptions allow an easy closed-form solution: - // - // T_inlet,initial = T_inlet,final - // T_outlet,initial = T_outlet,final - // - // If you don't want to push through the math, just know that this behaves like a - // pump that can equalize pressure instantly, i.e. much faster than pressure or - // volume pumps. - var transferMoles = n1 - (n1+n2)*T2*V1 / denom; - - // Get the volume transfered to update our flow meter. - // When you remove x from one side and add x to the other the total difference is 2x. - // Also account for atmos speedup so that measured flow rate matches the setting on the volume pump. - dV = 2*transferMoles*Atmospherics.R*T1/P1 / _atmosphereSystem.Speedup; - - // Actually transfer the gas. - _atmosphereSystem.Merge(outlet.Air, inlet.Air.Remove(transferMoles)); - } - - // Update transfer rate with an exponential moving average. - var tau = 1; // Time constant (averaging time) in seconds - var a = dt/tau; - gate.FlowRate = a*dV/tau + (1-a)*gate.FlowRate; // in L/sec - } - - private void OnExamined(Entity gate, ref ExaminedEvent args) - { - if (!Comp(gate).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. - return; - - var str = Loc.GetString("gas-passive-gate-examined", ("flowRate", $"{gate.Comp.FlowRate:0.#}")); - args.PushMarkup(str); - } + var str = Loc.GetString("gas-passive-gate-examined", ("flowRate", $"{gate.Comp.FlowRate:0.#}")); + args.PushMarkup(str); } } From 31c80deb819b87bf5aa828d3636fed96b6388945 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 19 Apr 2026 05:53:04 +0000 Subject: [PATCH 218/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e30a4ec526..4767f790df 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4045,3 +4038,11 @@ 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 From adead81cd94224b0e9963f2a35d7d08b2314e5fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 10:42:50 +0200 Subject: [PATCH 219/247] Update Credits (#43643) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index 554fb27446..01ff4724d6 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0-Anon, 0leshe, 0tito, 0x6273, 11BelowStudio, 12rabbits, 1337dakota, 13spacemen, 154942, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 27alaing, 2DSiggy, 3nderall, 4310v343k, 4dplanner, 5tickman, 612git, 778b, 96flo, aaron, abadaba695, Ablankmann, abregado, Absolute-Potato, Absotively, achookh, Acruid, ActiveMammmoth, actually-reb, ada-please, adamsong, Adeinitas, adm2play, Admiral-Obvious-001, adrian, Adrian16199, Ady4ik, Aearo-Deepwater, Aerocrux, Aeshus, Aexolott, Aexxie, AffleWaffle, africalimedrop, afrokada, AftrLite, AgentSmithRadio, Agoichi, ahandleman, Ahion, aiden, aidenkrz, Aidenkrz, Aisu9, ajcm, AJCM-git, AjexRose, Alekshhh, alexalexmax, alexkar598, AlexMorgan3817, alexum418, alexumandxgabriel08x, Alice4267, Alithsko, Alkheemist, alliephante, ALMv1, Alpaccalypse, AlphaQwerty, Altoids1, amatwiedle, amylizzle, Andre19926, Andrew-Fall, AndrewEyeke, AndrewFenriz, AndreyCamper, 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, 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, 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, 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, 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, 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, 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, zekins3366, 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 From 3f9b088e21fcbbc7c163f1fca03c83a31ade25a2 Mon Sep 17 00:00:00 2001 From: _Svist_ <148935188+Svist666s@users.noreply.github.com> Date: Sun, 19 Apr 2026 16:05:57 +0400 Subject: [PATCH 220/247] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=9A=D0=97=20(#3574)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ru-RU/corvax/guidebook/SOP/general.ftl | 2 +- .../guidebook/corporatelaw/articles.ftl | 96 ++++++++----------- .../Guidebook/CorporateLaw/Chapter31.xml | 2 +- .../Guidebook/CorporateLaw/Chapter32.xml | 5 - .../Guidebook/CorporateLaw/Chapter42.xml | 12 +-- .../Guidebook/CorporateLaw/CrimeList.xml | 12 +-- 6 files changed, 47 insertions(+), 82 deletions(-) diff --git a/Resources/Locale/ru-RU/corvax/guidebook/SOP/general.ftl b/Resources/Locale/ru-RU/corvax/guidebook/SOP/general.ftl index d9603048eb..550f561f1f 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/SOP/general.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/SOP/general.ftl @@ -18,7 +18,7 @@ guidebook-SOP-general-basic-rights = guidebook-SOP-general-guests = {"[italic]«Права и обязанности гостей станции и ее территории»[/italic]"} - Представленные обязанности должны исполнятся всеми разумными существами, прибывшими на территорию станции, NanoTrasen обеспечивает реализацию всех нижеизложенных прав при условии надлежащего исполнения обязанностей. Ненадлежащее исполнение обязанностей или их неисполнение может повлечь привлечение к ответственности по статье «Проникновение на территорию объекта NanoTrasen» Корпоративного закона. + Представленные обязанности должны исполнятся всеми разумными существами, прибывшими на территорию станции, NanoTrasen обеспечивает реализацию всех нижеизложенных прав при условии надлежащего исполнения обязанностей. Ненадлежащее исполнение обязанностей или их неисполнение может повлечь привлечение к ответственности по статье «Проникновение на закрытую корпоративную территорию» Корпоративного закона. guidebook-SOP-general-guests-duties = 1. По прибытии на территорию станции сообщить о своем прибытии в общий канал связи. diff --git a/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl b/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl index 0ece5dd9b5..dab4c9a3da 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 } @@ -304,7 +297,7 @@ guidebook-corporatelaw-413-note = guidebook-corporatelaw-415-desc = [italic]Хищение или приобретение права редкое или важного имущество путем обмана или злоупотребления доверием. Незаконное создание, приобретение, хранение или сбыт поддельных документов с печатями глав отделов, магистрата, центрального командования. Незаконное использование закрытых каналов отдела службы безопасности, командования, центрального командования.[/italic] guidebook-corporatelaw-415-note = { -guidebook-corporatelaw-note } - - При получении особо ценных предметов путем обмана должна применяться статья "Хищение особо ценного имущества" (325). + - При получении особо ценных предметов путем обмана должна применяться статья "Хищение особо ценного имущества" (315). - Редкость или важность имущества определяется с учётом обстоятельств: - Для классификации редкости рекомендуется использовать критерии: возможность производства, заказа или добычи указанного предмета, сложность добычи. - Для классификации важности рекомендуется использовать критерии: наличие аналогов украденному предмету, возможность быстрой замены украденного предмета. @@ -324,15 +317,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 +334,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/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter31.xml b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter31.xml index 179d0622e3..f8efb31941 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter31.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter31.xml @@ -24,7 +24,7 @@ -## 315 - Разбой +## 315 - Хищение особо ценного имущества diff --git a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter32.xml b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter32.xml index fb2b548321..b4cd74da64 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter32.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter32.xml @@ -24,11 +24,6 @@ -## 325 - Хищение особо ценного имущества - - - - ## 326 - Уничтожение особо ценного имущества diff --git a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter42.xml b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter42.xml index dd88b6eb6f..ec73acd82c 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter42.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/Chapter42.xml @@ -19,19 +19,9 @@ -## 424 - Проникновение в защищенную стратегическую точку - - - - -## 425 - Незаконная эвакуация с территории комплекса +## 425 - Проникновение на закрытую корпоративную территорию -## 426 - Проникновение на территорию объекта NanoTrasen - - - - diff --git a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/CrimeList.xml b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/CrimeList.xml index 43e151212d..2fb185c9f8 100644 --- a/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/CrimeList.xml +++ b/Resources/ServerInfo/Corvax/Guidebook/CorporateLaw/CrimeList.xml @@ -373,7 +373,7 @@ 315 - [textlink="Разбой" link="CLChapter31"] + [textlink="Хищение особо ценного имущества" link="CLChapter32"] @@ -415,13 +415,11 @@ - 325 - [textlink="Хищение особо ценного имущества" link="CLChapter32"] - 326 + 326 [textlink="Уничтожение особо ценного имущества" link="CLChapter32"] @@ -496,20 +494,16 @@ - 424 - [textlink="Проникновение в защищенную стратегическую точку" link="CLChapter42"] 425 - [textlink="Незаконная эвакуация с территории комплекса" link="CLChapter42"] + [textlink="Проникновение на закрытую корпоративную территорию" link="CLChapter42"] - 426 - [textlink="Проникновение на территорию объекта NanoTrasen" link="CLChapter42"] From f6b458f7613b10d41e0b62b26bea5e94ac323169 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sun, 19 Apr 2026 16:10:54 +0200 Subject: [PATCH 221/247] Add changeling flesh clothing transformation (#43590) * add feature with placeholder sprites * handle chameleon clothing * add store sprite * fix * more fixes * fix path * remove clothe ripping * alert sprite * i hate warnings as errors * fix tests * Update Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> * verb * color * validate --------- Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Co-authored-by: ScarKy0 --- .../Access/Systems/AgentIDCardSystem.cs | 25 +- .../Cloning/CloningSystem.Subscriptions.cs | 12 +- Content.Server/Cloning/CloningSystem.cs | 114 +++----- .../Systems/ChameleonClothingSystem.cs | 11 +- .../Implants/ChameleonControllerSystem.cs | 4 +- .../Store/Systems/StoreSystem.Ui.cs | 9 + ...ChangelingFleshClothingAbilityComponent.cs | 33 +++ .../ChangelingFleshClothingComponent.cs | 11 + .../Components/ChangelingIdentityComponent.cs | 4 +- .../Systems/ChangelingClonerSystem.cs | 8 +- .../Systems/ChangelingDevourSystem.cs | 23 -- .../Systems/ChangelingFleshClothingSystem.cs | 137 +++++++++ .../Systems/SharedChangelingIdentitySystem.cs | 33 +-- .../Cloning/CloningSettingsPrototype.cs | 43 +-- Content.Shared/Cloning/SharedCloningSystem.cs | 123 +++++++- .../Components/ChameleonClothingComponent.cs | 18 +- .../SharedChameleonClothingSystem.cs | 14 +- .../Inventory/InventorySystem.Slots.cs | 28 ++ .../StatusEffectNew/StatusEffectSystem.API.cs | 6 +- Content.Shared/Store/ListingPrototype.cs | 14 + .../Locale/en-US/changeling/changeling.ftl | 4 + .../Locale/en-US/store/changeling-catalog.ftl | 7 +- Resources/Prototypes/Alerts/changeling.yml | 13 + .../Prototypes/Catalog/changeling_catalog.yml | 20 +- .../Entities/Clothing/Misc/changeling.yml | 269 ++++++++++++++++++ .../Entities/Mobs/Player/changeling.yml | 21 ++ .../Prototypes/Entities/Mobs/Player/clone.yml | 15 +- .../changeling2.rsi/flesh_clothing.png | Bin 0 -> 1863 bytes .../changeling2.rsi/flesh_clothing_alt.png | Bin 0 -> 1626 bytes .../Actions/changeling2.rsi/meta.json | 17 ++ .../changeling.rsi/flesh-clothing-off.png | Bin 0 -> 542 bytes .../changeling.rsi/flesh-clothing-on.png | Bin 0 -> 804 bytes .../Interface/Alerts/changeling.rsi/meta.json | 17 ++ 33 files changed, 872 insertions(+), 181 deletions(-) create mode 100644 Content.Shared/Changeling/Components/ChangelingFleshClothingAbilityComponent.cs create mode 100644 Content.Shared/Changeling/Components/ChangelingFleshClothingComponent.cs create mode 100644 Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs create mode 100644 Resources/Prototypes/Alerts/changeling.yml create mode 100644 Resources/Prototypes/Entities/Clothing/Misc/changeling.yml create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing.png create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing_alt.png create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/meta.json create mode 100644 Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-off.png create mode 100644 Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-on.png create mode 100644 Resources/Textures/Interface/Alerts/changeling.rsi/meta.json diff --git a/Content.Server/Access/Systems/AgentIDCardSystem.cs b/Content.Server/Access/Systems/AgentIDCardSystem.cs index 0586c33ada..6e7cd6a406 100644 --- a/Content.Server/Access/Systems/AgentIDCardSystem.cs +++ b/Content.Server/Access/Systems/AgentIDCardSystem.cs @@ -1,22 +1,22 @@ -using Content.Server.Access.Components; -using Content.Server.Popups; -using Content.Shared.UserInterface; -using Content.Shared.Access.Components; -using Content.Shared.Access.Systems; -using Content.Shared.Interaction; -using Content.Shared.StatusIcon; -using Robust.Server.GameObjects; -using Robust.Shared.Prototypes; -using Content.Shared.Roles; using System.Diagnostics.CodeAnalysis; +using Content.Server.Access.Components; using Content.Server.Clothing.Systems; using Content.Server.Implants; -using Content.Server.VoiceMask; +using Content.Server.Popups; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.Clothing.Components; using Content.Shared.Implants; +using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Lock; using Content.Shared.PDA; +using Content.Shared.Roles; +using Content.Shared.StatusIcon; +using Content.Shared.UserInterface; using Content.Shared.VoiceMask; +using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; namespace Content.Server.Access.Systems { @@ -80,7 +80,8 @@ namespace Content.Server.Access.Systems if (!proto.TryGetComponent(out var comp, EntityManager.ComponentFactory)) return; - _chameleon.SetSelectedPrototype(ent, comp.IdCard); + if (TryComp(ent, out var chameleonComp) && chameleonComp.CanBeSetByController) + _chameleon.SetSelectedPrototype(ent, comp.IdCard, component: chameleonComp); } private void OnVoiceMaskNameChanged(Entity ent, ref InventoryRelayedEvent args) diff --git a/Content.Server/Cloning/CloningSystem.Subscriptions.cs b/Content.Server/Cloning/CloningSystem.Subscriptions.cs index 2f8491b2f3..995f3e32eb 100644 --- a/Content.Server/Cloning/CloningSystem.Subscriptions.cs +++ b/Content.Server/Cloning/CloningSystem.Subscriptions.cs @@ -1,6 +1,8 @@ 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; @@ -10,8 +12,8 @@ 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; @@ -34,6 +36,7 @@ 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() @@ -50,6 +53,7 @@ public sealed partial class CloningSystem SubscribeLocalEvent(OnCloneItemPaper); SubscribeLocalEvent(OnCloneItemForensics); SubscribeLocalEvent(OnCloneItemStore); + SubscribeLocalEvent(OnCloneItemChameleon); // These are for cloning components that cannot be cloned using CopyComp. // Put them into CloningSettingsPrototype.EventComponents to have them be applied to the clone. @@ -100,6 +104,12 @@ public sealed partial class CloningSystem } } + private void OnCloneItemChameleon(Entity ent, ref CloningItemEvent args) + { + // copy the prototype the original is mimicing + _chameleonClothing.SetSelectedPrototype(args.CloneUid, ent.Comp.Default); + } + private void OnCloneVocal(Entity ent, ref CloningEvent args) { if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name)) diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index ed72004551..abff99a407 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,14 +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!; - [Dependency] private readonly EntityQuery _cloneableEffectQuery = 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)) @@ -57,10 +55,13 @@ public sealed partial class CloningSystem : SharedCloningSystem if (!_prototype.Resolve(humanoid.Species, out var speciesPrototype)) return false; // invalid species - var attemptEv = new CloningAttemptEvent(settings); - RaiseLocalEvent(original, ref attemptEv); - if (attemptEv.Cancelled && !settings.ForceCloning) - return false; // cannot clone, for example due to the unrevivable trait + if (!settings.ForceCloning) + { + var attemptEv = new CloningAttemptEvent(settings); + RaiseLocalEvent(original, ref attemptEv); + if (attemptEv.Cancelled) + return false; // cannot clone, for example due to the unrevivable trait + } clone = coords == null ? Spawn(speciesPrototype.Prototype) : Spawn(speciesPrototype.Prototype, coords.Value); _visualBody.CopyAppearanceFrom(original, clone.Value); @@ -87,13 +88,17 @@ public sealed partial class CloningSystem : SharedCloningSystem var originalName = _nameMod.GetBaseName(original); // Set the clone's name. The raised events will also adjust their PDA and ID card names. - _metaData.SetEntityName(clone.Value, originalName); + _metaData.SetEntityName(clone.Value, originalName, raiseEvents: settings.RaiseEntityRenamedEvent); + _identity.QueueIdentityUpdate(clone.Value); // We have to manually refresh the identity in case we did not raise events. _adminLogger.Add(LogType.Chat, LogImpact.Medium, $"The body of {original:player} was cloned as {clone.Value:player}"); return true; } - public override void CloneComponents(EntityUid original, EntityUid clone, ProtoId settings) + public override void CloneComponents( + EntityUid original, + EntityUid clone, + ProtoId settings) { if (!_prototype.Resolve(settings, out var proto)) return; @@ -101,7 +106,10 @@ public sealed partial class CloningSystem : SharedCloningSystem CloneComponents(original, clone, proto); } - public override void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings) + public override void CloneComponents( + EntityUid original, + EntityUid clone, + CloningSettingsPrototype settings) { var componentsToCopy = settings.Components; var componentsToEvent = settings.EventComponents; @@ -147,11 +155,12 @@ public sealed partial class CloningSystem : SharedCloningSystem 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; @@ -169,15 +178,11 @@ public sealed partial class CloningSystem : SharedCloningSystem } } - /// - /// Copies an item and its storage recursively, placing all items at the same position in grid storage. - /// This uses the original prototype of the items, so any changes to components that are done after spawning are lost! - /// - /// - /// This is not perfect and only considers item in storage containers. - /// Some components have their own additional spawn logic on map init, so we cannot just copy all containers. - /// - public EntityUid? CopyItem(EntityUid original, EntityCoordinates coords, EntityWhitelist? whitelist = null, EntityWhitelist? blacklist = null) + public override EntityUid? CopyItem( + EntityUid original, + EntityCoordinates coords, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) { // we use a whitelist and blacklist to be sure to exclude any problematic entities if (!_whitelist.CheckBoth(original, blacklist, whitelist)) @@ -213,12 +218,11 @@ public sealed partial class CloningSystem : SharedCloningSystem return spawned; } - /// - /// Copies an item's storage recursively to another storage. - /// The storage grids should have the same shape or it will drop on the floor. - /// Basically the same as CopyItem, but we don't copy the outermost container. - /// - public void CopyStorage(Entity original, Entity target, EntityWhitelist? whitelist = null, EntityWhitelist? blacklist = null) + public override void CopyStorage( + Entity original, + Entity target, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) { if (!Resolve(original, ref original.Comp, false) || !Resolve(target, ref target.Comp, false)) return; @@ -237,17 +241,12 @@ public sealed partial class CloningSystem : SharedCloningSystem } } - /// - /// Copies all implants from one mob to another. - /// Might result in duplicates if the target already has them. - /// Can copy the storage inside a storage implant according to a whitelist and blacklist. - /// - /// Entity to copy implants from. - /// Entity to copy implants to. - /// If true will copy storage of the implants (E.g storage implant) - /// Whitelist for the storage copy (If copyStorage is true) - /// Blacklist for the storage copy (If copyStorage is true) - public void CopyImplants(Entity original, EntityUid target, bool copyStorage = false, EntityWhitelist? whitelist = null, EntityWhitelist? blacklist = null) + public override void CopyImplants( + Entity original, + EntityUid target, + bool copyStorage = false, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) { if (!Resolve(original, ref original.Comp, false)) return; // they don't have any implants to copy! @@ -274,26 +273,5 @@ public sealed partial class CloningSystem : SharedCloningSystem if (copyStorage) CopyStorage(originalImplant, targetImplant.Value, whitelist, blacklist); // only needed for storage implants } - - } - - /// - /// Scans all permanent status effects applied to the original entity and transfers them to the clone. - /// - public void CopyStatusEffects(Entity original, Entity target) - { - 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.Server/Clothing/Systems/ChameleonClothingSystem.cs b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs index 645b47d180..8a654b4ab9 100644 --- a/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs +++ b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs @@ -22,7 +22,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem private void OnMapInit(EntityUid uid, ChameleonClothingComponent component, MapInitEvent args) { - SetSelectedPrototype(uid, component.Default, true, component); + SetSelectedPrototype(uid, component.Default, true, component: component); } private void OnSelected(EntityUid uid, ChameleonClothingComponent component, ChameleonPrototypeSelectedMessage args) @@ -39,10 +39,7 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem UI.SetUiState(uid, ChameleonUiKey.Key, state); } - /// - /// Change chameleon items name, description and sprite to mimic other entity prototype. - /// - public override void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false, + public override void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false, bool validate = true, ChameleonClothingComponent? component = null) { if (!Resolve(uid, ref component, false)) @@ -56,8 +53,10 @@ public sealed class ChameleonClothingSystem : SharedChameleonClothingSystem // make sure that it is valid change if (string.IsNullOrEmpty(protoId) || !_proto.TryIndex(protoId, out EntityPrototype? proto)) return; - if (!IsValidTarget(proto, component.Slot, component.RequireTag)) + + if (validate && !IsValidTarget(proto, component.Slot, component.RequireTag)) return; + component.Default = protoId; UpdateIdentityBlocker(uid, component, proto); diff --git a/Content.Server/Implants/ChameleonControllerSystem.cs b/Content.Server/Implants/ChameleonControllerSystem.cs index e884e181ee..3de51a4949 100644 --- a/Content.Server/Implants/ChameleonControllerSystem.cs +++ b/Content.Server/Implants/ChameleonControllerSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Clothing.Systems; +using Content.Server.Clothing.Systems; using Content.Server.Preferences.Managers; using Content.Shared.Clothing; using Content.Shared.Clothing.Components; @@ -105,7 +105,7 @@ public sealed class ChameleonControllerSystem : SharedChameleonControllerSystem private void ChameleonControllerOutfitItemSelected(Entity ent, ref InventoryRelayedEvent args) { - if (!_inventory.TryGetContainingSlot(ent.Owner, out var slot)) + if (!ent.Comp.CanBeSetByController || !_inventory.TryGetContainingSlot(ent.Owner, out var slot)) return; _chameleonClothingSystem.SetSelectedPrototype(ent, GetGearForSlot(args, slot.Name), component: ent.Comp); diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index 7a87f8b162..713146856b 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -13,6 +13,7 @@ using Content.Shared.Store; using Content.Shared.Store.Components; using Content.Shared.UserInterface; using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; namespace Content.Server.Store.Systems; @@ -26,6 +27,7 @@ public sealed partial class StoreSystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly StackSystem _stack = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; private void InitializeUi() { @@ -121,6 +123,13 @@ public sealed partial class StoreSystem component.BalanceSpent[currency] += amount; } + //apply components + if (listing.ProductComponents != null) + { + if (_proto.Resolve(listing.ProductComponents, out var productComponentsEntity)) + EntityManager.AddComponents(buyer, productComponentsEntity.Components); + } + //spawn entity if (listing.ProductEntity != null) { diff --git a/Content.Shared/Changeling/Components/ChangelingFleshClothingAbilityComponent.cs b/Content.Shared/Changeling/Components/ChangelingFleshClothingAbilityComponent.cs new file mode 100644 index 0000000000..40df5b2bad --- /dev/null +++ b/Content.Shared/Changeling/Components/ChangelingFleshClothingAbilityComponent.cs @@ -0,0 +1,33 @@ +using Content.Shared.Alert; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Changeling.Components; + +/// +/// Allows the changeling to spawn dummy chameleon clothing items that will transform with them, +/// mimicing the equipment of the stored disguise. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ChangelingFleshClothingAbilityComponent : Component +{ + /// + /// Is the ability currently enabled? + /// + [DataField, AutoNetworkedField] + public bool Enabled = true; + + /// + /// The alert for showing if the ability is active and for toggling it. + /// + [DataField] + public ProtoId AlertId = "ChangelingFleshClothing"; + + /// + /// The chameleon clothing items to spawn into any empty slots if the changeling transformed. + /// These need so that they will change their visuals according to the identity we transformed into. + /// The key of the dictionary is the corresponding inventory slot. + /// + [DataField] + public Dictionary ClothingPrototypes = new(); +} diff --git a/Content.Shared/Changeling/Components/ChangelingFleshClothingComponent.cs b/Content.Shared/Changeling/Components/ChangelingFleshClothingComponent.cs new file mode 100644 index 0000000000..affd1f3a8f --- /dev/null +++ b/Content.Shared/Changeling/Components/ChangelingFleshClothingComponent.cs @@ -0,0 +1,11 @@ +using Content.Shared.Clothing.Components; +using Robust.Shared.GameStates; + +namespace Content.Shared.Changeling.Components; + +/// +/// Allows this clothing item to transform along with the changeling so that it looks like whatever the mob we transform into is wearing in that slot. +/// Requires and . +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ChangelingFleshClothingComponent : Component; diff --git a/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs b/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs index fa667f4bf2..bef822b720 100644 --- a/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs +++ b/Content.Shared/Changeling/Components/ChangelingIdentityComponent.cs @@ -27,8 +27,8 @@ public sealed partial class ChangelingIdentityComponent : Component public EntityUid? CurrentIdentity; /// - /// The cloning settings passed to the CloningSystem, contains a list of all components to copy or have handled by their - /// respective systems. + /// The cloning settings to use when cloning a devoured identity to the paused map. + /// This contains a whitelist of all components that need to be backed up so that the changeling can transform into them later. /// [DataField] public ProtoId IdentityCloningSettings = "ChangelingCloningSettings"; diff --git a/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs b/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs index 385ed5c9e9..63a0a67f9b 100644 --- a/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingClonerSystem.cs @@ -209,14 +209,14 @@ public sealed class ChangelingClonerSystem : EntitySystem if (!HasComp(target)) return; // cloning only works for humanoids at the moment - if (!_prototype.Resolve(ent.Comp.Settings, out var settings)) - return; - _adminLogger.Add(LogType.Identity, $"{user} is using {ent.Owner} to draw DNA from {target}."); // Make a copy of the target on a paused map, so that we can apply their components later. - ent.Comp.ClonedBackup = _changelingIdentity.CloneToPausedMap(settings, target); + ent.Comp.ClonedBackup = _changelingIdentity.CloneToPausedMap(ent.Comp.Settings, target); + if (ent.Comp.ClonedBackup == null) + return; + ent.Comp.State = ChangelingClonerState.Filled; _appearance.SetData(ent.Owner, ChangelingClonerVisuals.State, ChangelingClonerState.Filled); Dirty(ent); diff --git a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs index b9bdfa29c7..e0cb17f51f 100644 --- a/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingDevourSystem.cs @@ -10,13 +10,10 @@ using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; using Content.Shared.Inventory; using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; using Content.Shared.Popups; -using Content.Shared.Storage; using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; -using Robust.Shared.Random; namespace Content.Shared.Changeling.Systems; @@ -30,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() { @@ -172,10 +167,6 @@ public sealed class ChangelingDevourSystem : EntitySystem _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ent.Owner):player} successfully devoured {ToPrettyString(target):player}'s identity"); - if (_inventorySystem.TryGetSlotEntity(target, "jumpsuit", out var item) - && TryComp(item, out var butcherable)) - RipClothing(target, (item.Value, butcherable)); - if (!TryComp(ent.Owner, out var identityStorage)) return; @@ -270,18 +261,4 @@ public sealed class ChangelingDevourSystem : EntitySystem return false; } - - // TODO: This should just be an API method in the butcher system - private void RipClothing(EntityUid victim, Entity item) - { - var spawnEntities = EntitySpawnCollection.GetSpawns(item.Comp.SpawnedEntities, _robustRandom); - - foreach (var proto in spawnEntities) - { - // TODO: once predictedRandom is in, make this a Coordinate offset of 0.25f from the victims position - PredictedSpawnNextToOrDrop(proto, victim); - } - - PredictedQueueDel(item.Owner); - } } diff --git a/Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs b/Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs new file mode 100644 index 0000000000..8082c3bb87 --- /dev/null +++ b/Content.Shared/Changeling/Systems/ChangelingFleshClothingSystem.cs @@ -0,0 +1,137 @@ +using Content.Shared.Alert; +using Content.Shared.Changeling.Components; +using Content.Shared.Clothing.Components; +using Content.Shared.Clothing.EntitySystems; +using Content.Shared.Inventory; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Changeling.Systems; + +public sealed class ChangelingFleshClothingSystem : EntitySystem +{ + [Dependency] private readonly SharedChameleonClothingSystem _chameleonClothing = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnToggle); + SubscribeLocalEvent(OnBeforeChangelingTransform); + SubscribeLocalEvent(OnAfterChangelingTransform); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + _alerts.ShowAlert(ent.Owner, ent.Comp.AlertId, 1); + } + + private void OnToggle(Entity ent, ref ToggleFleshClothingEvent args) + { + if (args.Handled) + return; + + ent.Comp.Enabled = !ent.Comp.Enabled; + Dirty(ent); + _alerts.ShowAlert(ent.Owner, ent.Comp.AlertId, (short)(ent.Comp.Enabled ? 1 : 0)); + + args.Handled = true; + } + + // If we transform into another species and loose an inventory item then it will get dropped as a result. + // But we don't want fleeting clothing to do a sound and popup in that case, + // so we have to delete any flesh clothing slots that would drop before transforming. + private void OnBeforeChangelingTransform(Entity ent, ref BeforeChangelingTransformEvent args) + { + // We always remove slots that are no longer supported by the transformation, even if the component is disabled. + RemoveRedundantFleshClothing(ent.Owner, args.StoredIdentity); + } + + private void OnAfterChangelingTransform(Entity ent, ref AfterChangelingTransformEvent args) + { + if (ent.Comp.Enabled) + SpawnAndTransformClothing(ent.Owner, args.StoredIdentity, ent.Comp.ClothingPrototypes); + } + + /// + /// Removes any flesh clothing items the target is wearing in slots the original does not have. + /// + public void RemoveRedundantFleshClothing(Entity target, Entity original) + { + // TODO: Not predicted yet because equipping is not predicted either and we don't want to be nude for a few frames. + if (_net.IsClient) + return; + + if (!Resolve(target, ref target.Comp)) + return; + + Resolve(original, ref original.Comp, false); // They might now have an inventory at all. + + var slots = _inventory.GetSlotEnumerator(target, SlotFlags.WITHOUT_POCKET); + while (slots.NextItem(out var targetItem, out var slotDefinition)) + { + if (!HasComp(targetItem)) + continue; // Do nothing for normal items. + + // If the original does not have that slot or it contains an invalid prototype then we remove any flesh clothing item in our corresponding slot. + if (original.Comp == null + || !_inventory.TryGetSlotEntity(original, slotDefinition.Name, out var originalItem, inventoryComponent: original.Comp) + || MetaData(originalItem.Value).EntityPrototype?.ID == null) + { + _inventory.TryUnequip(target, slotDefinition.Name, silent: true, force: true, inventory: target.Comp); + QueueDel(targetItem); + } + } + } + + /// + /// Spawns the given clothing items into empty inventory slots. + /// If the item has and then + /// it will have its visuals changed to match the item another given player is wearing in the same slot. + /// + public void SpawnAndTransformClothing(Entity target, Entity original, Dictionary clothingPrototypes) + { + // TODO: Remove this guard when chameleon clothing is properly predicted. + // Otherwise we will see the default item sprite for a moment after spawn until it's updated. + if (_net.IsClient) + return; + + if (!Resolve(target, ref target.Comp) || !Resolve(original, ref original.Comp, false)) // Don't log because the original might be outside PVS range since it's on another map. + return; + + var slots = _inventory.GetSlotEnumerator(target, SlotFlags.WITHOUT_POCKET); + var coords = Transform(target).Coordinates; + while (slots.MoveNext(out var containerSlot, out var slotDefinition)) + { + var targetItem = containerSlot.ContainedEntity; + if (!_inventory.TryGetSlotEntity(original, slotDefinition.Name, out var originalItem, inventoryComponent: original.Comp) + || MetaData(originalItem.Value).EntityPrototype?.ID is not { } chameleonProtoId) + continue; + + // If our slot is empty then spawn a new chameleon clothing item inside. + if (targetItem == null && clothingPrototypes.TryGetValue(slotDefinition.Name, out var fleshProtoId)) + { + targetItem = SpawnAtPosition(fleshProtoId, coords); + _inventory.TryEquip(target, targetItem.Value, slotDefinition.Name, silent: true, force: true, inventory: target.Comp); + } + + // If the item in our slot is flesh clothing then set the chameleon prototype to mirror the target. + if (HasComp(targetItem)) + { + if (TryComp(originalItem, out var originalChameleonComp)) + _chameleonClothing.SetSelectedPrototype(targetItem.Value, originalChameleonComp.Default, validate: false); // If it is also a chameleon item then use whatever that is mimicing. + else + _chameleonClothing.SetSelectedPrototype(targetItem.Value, chameleonProtoId, validate: false); // We don't validate because a lot of clothing is not chameleon whitelisted, like warden's beret. + } + } + } +} + +/// +/// Event raised to toggle the . +/// +public sealed partial class ToggleFleshClothingEvent : BaseAlertEvent; diff --git a/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs b/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs index b9e2fbef3e..8e28cd48ad 100644 --- a/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs +++ b/Content.Shared/Changeling/Systems/SharedChangelingIdentitySystem.cs @@ -1,10 +1,6 @@ using System.Linq; -using System.Numerics; -using Content.Shared.Body; 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; @@ -16,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; @@ -132,31 +125,26 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem ///
/// The settings to use for cloning. /// The target to clone. - public EntityUid? CloneToPausedMap(CloningSettingsPrototype settings, EntityUid target) + public EntityUid? CloneToPausedMap(ProtoId settings, EntityUid target) { // Don't create client side duplicate clones or a clientside map. if (_net.IsClient) return null; - if (!TryComp(target, out var humanoid) - || !_prototype.Resolve(humanoid.Species, out var speciesPrototype)) + EnsurePausedMap(); + if (PausedMapId == null) return null; - EnsurePausedMap(); - var clone = Spawn(speciesPrototype.Prototype, new MapCoordinates(Vector2.Zero, PausedMapId!.Value)); + var mapCoords = new MapCoordinates(0, 0, PausedMapId.Value); + if (!_cloningSystem.TryCloning(target, mapCoords, settings, out var clone)) + return null; - var storedIdentity = EnsureComp(clone); - storedIdentity.OriginalEntity = target; // TODO: network this once we have WeakEntityReference or the autonetworking source gen is fixed + var storedIdentity = EnsureComp(clone.Value); + storedIdentity.OriginalEntity = target; // TODO: network this once we have a relations system so that this does not cause PVS errors. if (TryComp(target, out var actor)) storedIdentity.OriginalSession = actor.PlayerSession; - _visualBody.CopyAppearanceFrom(target, clone); - _cloningSystem.CloneComponents(target, clone, settings); - - var targetName = _nameMod.GetBaseName(target); - _metaSystem.SetEntityName(clone, targetName); - return clone; } @@ -168,10 +156,7 @@ public abstract class SharedChangelingIdentitySystem : EntitySystem /// The target to clone. public EntityUid? CloneToPausedMap(Entity ent, EntityUid target) { - if (!_prototype.Resolve(ent.Comp.IdentityCloningSettings, out var settings)) - return null; - - var clone = CloneToPausedMap(settings, target); + var clone = CloneToPausedMap(ent.Comp.IdentityCloningSettings, target); if (clone == null) return null; diff --git a/Content.Shared/Cloning/CloningSettingsPrototype.cs b/Content.Shared/Cloning/CloningSettingsPrototype.cs index 0b531561ca..ffc4dd06dd 100644 --- a/Content.Shared/Cloning/CloningSettingsPrototype.cs +++ b/Content.Shared/Cloning/CloningSettingsPrototype.cs @@ -1,15 +1,13 @@ using Content.Shared.Inventory; using Content.Shared.Whitelist; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; namespace Content.Shared.Cloning; /// -/// Settings for cloning a humanoid. -/// Used to decide which components should be copied. +/// Settings for cloning a humanoid. +/// Used to decide which components should be copied. /// [Prototype] public sealed partial class CloningSettingsPrototype : IPrototype, IInheritingPrototype @@ -18,70 +16,79 @@ public sealed partial class CloningSettingsPrototype : IPrototype, IInheritingPr [IdDataField] public string ID { get; private set; } = default!; + /// [ParentDataField(typeof(PrototypeIdArraySerializer))] public string[]? Parents { get; private set; } + /// [AbstractDataField] [NeverPushInheritance] public bool Abstract { get; private set; } /// - /// Determines if cloning can be prevented by traits etc. + /// Determines if cloning can be prevented by traits etc. /// [DataField] public bool ForceCloning = true; /// - /// Which inventory slots will receive a copy of the original's clothing. - /// Disabled when null. + /// Which inventory slots will receive a copy of the original's clothing. + /// Disabled when null. /// [DataField] public SlotFlags? CopyEquipment = SlotFlags.All; /// - /// Whether or not to copy slime storage and storage implant contents. + /// Whether or not to copy slime storage and storage implant contents. /// [DataField] public bool CopyInternalStorage = true; /// - /// Whether or not to copy implants. + /// Whether or not to copy implants. /// [DataField] public bool CopyImplants = true; /// - /// Should infinite status effects applied to an entity be copied or not? + /// Should infinite status effects (which are used for some traits) applied to an entity be copied or not? /// [DataField] public bool CopyStatusEffects = true; /// - /// Whitelist for the equipment allowed to be copied. + /// Should the event for renaming the clone be raised? + /// This will also set the clone's id card and PDA name if they have one equipped. + /// + [DataField] + public bool RaiseEntityRenamedEvent = true; + + /// + /// Whitelist for the equipment allowed to be copied. /// [DataField] public EntityWhitelist? Whitelist; /// - /// Blacklist for the equipment allowed to be copied. + /// Blacklist for the equipment allowed to be copied. /// [DataField] public EntityWhitelist? Blacklist; /// TODO: Make this not a string https://github.com/space-wizards/RobustToolbox/issues/5709 /// - /// Components to copy from the original to the clone using CopyComp. - /// This makes a deepcopy of all datafields, including information the clone might not own! - /// If you need to exclude data or do additional component initialization, then subscribe to CloningEvent instead! - /// Components in this list that the orginal does not have will be removed from the clone. + /// Components to copy from the original to the clone using CopyComp. + /// This makes a deepcopy of all datafields, including information the clone might not own! + /// If you need to exclude data or do additional component initialization, then subscribe to CloningEvent instead! + /// Components in this list that the orginal does not have will be removed from the clone. /// [DataField] [AlwaysPushInheritance] public HashSet Components = new(); /// - /// Components to remove from the clone and copy over manually using a CloneEvent raised on the original. - /// Use this when the component cannot be copied using CopyComp, for example when having an Uid as a datafield. + /// Components to remove from the clone and copy over manually using a CloneEvent raised on the original. + /// Use this when the component cannot be copied using CopyComp, for example when having an EntityUid as a datafield. /// [DataField] [AlwaysPushInheritance] diff --git a/Content.Shared/Cloning/SharedCloningSystem.cs b/Content.Shared/Cloning/SharedCloningSystem.cs index e44264fb41..ca21d686cf 100644 --- a/Content.Shared/Cloning/SharedCloningSystem.cs +++ b/Content.Shared/Cloning/SharedCloningSystem.cs @@ -1,17 +1,30 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Implants.Components; +using Content.Shared.Inventory; +using Content.Shared.StatusEffectNew.Components; +using Content.Shared.Storage; +using Content.Shared.Whitelist; +using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.Shared.Cloning; public abstract partial class SharedCloningSystem : EntitySystem { + [Dependency] private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!; //TODO: This system has to support both the old and new status effect systems, until the old is able to be fully removed. + [Dependency] private readonly EntityQuery _cloneableEffectQuery = default!; + /// - /// Copy components from one entity to another based on a CloningSettingsPrototype. + /// Spawns a clone of the given humanoid mob at the specified location or in nullspace. /// - /// The orignal Entity to clone components from. - /// The target Entity to clone components to. - /// The clone settings prototype containing the list of components to clone. - public virtual void CloneComponents(EntityUid original, EntityUid clone, CloningSettingsPrototype settings) + public virtual bool TryCloning( + EntityUid original, + MapCoordinates? coords, + ProtoId settingsId, + [NotNullWhen(true)] out EntityUid? clone) { + clone = null; + return false; } /// @@ -20,7 +33,105 @@ public abstract partial class SharedCloningSystem : EntitySystem /// The orignal Entity to clone components from. /// The target Entity to clone components to. /// The clone settings prototype id containing the list of components to clone. - public virtual void CloneComponents(EntityUid original, EntityUid clone, ProtoId settings) + public virtual void CloneComponents( + EntityUid original, + EntityUid clone, + ProtoId settings) { } + + /// + /// Copy components from one entity to another based on a CloningSettingsPrototype. + /// + /// The orignal Entity to clone components from. + /// The target Entity to clone components to. + /// The clone settings prototype containing the list of components to clone. + public virtual void CloneComponents( + EntityUid original, + EntityUid clone, + CloningSettingsPrototype settings) + { + } + + /// + /// Copies the equipment the original has to the clone. + /// This uses the original prototype of the items, so any changes to components that are done after spawning are lost! + /// + public virtual void CopyEquipment( + Entity original, + Entity clone, + SlotFlags slotFlags, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) + { + } + + /// + /// Copies an item and its storage recursively, placing all items at the same position in grid storage. + /// This uses the original prototype of the items, so any changes to components that are done after spawning are lost! + /// + /// + /// This is not perfect and only considers item in storage containers. + /// Some components have their own additional spawn logic on map init, so we cannot just copy all containers. + /// + public virtual EntityUid? CopyItem( + EntityUid original, + EntityCoordinates coords, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) + { + return null; + } + + /// + /// Copies an item's storage recursively to another storage. + /// The storage grids should have the same shape or it will drop on the floor. + /// Basically the same as CopyItem, but we don't copy the outermost container. + /// + public virtual void CopyStorage( + Entity original, + Entity target, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) + { + } + + /// + /// Copies all implants from one mob to another. + /// Might result in duplicates if the target already has them. + /// Can copy the storage inside a storage implant according to a whitelist and blacklist. + /// + /// Entity to copy implants from. + /// Entity to copy implants to. + /// If true will copy storage of the implants (E.g storage implant) + /// Whitelist for the storage copy (If copyStorage is true) + /// Blacklist for the storage copy (If copyStorage is true) + public virtual void CopyImplants( + Entity original, + EntityUid target, + bool copyStorage = false, + EntityWhitelist? whitelist = null, + EntityWhitelist? blacklist = null) + { + } + + /// + /// Scans all permanent status effects applied to the original entity and transfers them to the clone. + /// + public void CopyStatusEffects(Entity original, Entity target) + { + foreach (var effect in _statusEffects.EnumerateStatusEffects(original, _cloneableEffectQuery)) + { + // We are not interested in temporary effects, only permanent ones. + if (effect.Comp1.EndEffectTime is not null) + continue; + + var effectProto = Prototype(effect); + + if (effectProto is null) + continue; + + _statusEffects.TrySetStatusEffectDuration(target, effectProto); + } + } } diff --git a/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs b/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs index 56991fb1a3..4c61c67c6d 100644 --- a/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs +++ b/Content.Shared/Clothing/Components/ChameleonClothingComponent.cs @@ -21,8 +21,12 @@ 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; @@ -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/EntitySystems/SharedChameleonClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs index 0aa0670624..ccd0e04953 100644 --- a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs @@ -142,6 +142,9 @@ public abstract class SharedChameleonClothingSystem : EntitySystem if (!args.CanAccess || !args.CanInteract || _lock.IsLocked(ent.Owner)) return; + if (!ent.Comp.ShowVerb) + return; + // Can't pass args from a ref event inside of lambdas var user = args.User; @@ -192,6 +195,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem // check if it's valid clothing if (!proto.TryGetComponent(out ClothingComponent? clothing, Factory)) return false; + if (!clothing.Slots.HasFlag(chameleonSlot)) return false; @@ -258,7 +262,15 @@ public abstract class SharedChameleonClothingSystem : EntitySystem } // TODO: Predict and use component states for the UI - public virtual void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false, + /// + /// Change chameleon items name, description and sprite to mimic other entity prototype. + /// + /// The entity who's appearance to swap. + /// The target protoId of the target appearance. + /// Whether to force update appearance, even if the same one was selected. + /// Whether to validate if the target prototype is a valid chameleon target. + /// The of the entity we are updating. + public virtual void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdate = false, bool validate = true, ChameleonClothingComponent? component = null) { } } diff --git a/Content.Shared/Inventory/InventorySystem.Slots.cs b/Content.Shared/Inventory/InventorySystem.Slots.cs index 3f0bdf10d4..005f78eef7 100644 --- a/Content.Shared/Inventory/InventorySystem.Slots.cs +++ b/Content.Shared/Inventory/InventorySystem.Slots.cs @@ -263,6 +263,10 @@ public partial class InventorySystem : EntitySystem _containers = containers; } + /// + /// Get the next ContainerSlot in this inventory. + /// The slot may not contain an item. + /// public bool MoveNext([NotNullWhen(true)] out ContainerSlot? container) { while (_nextIdx < _slots.Length) @@ -281,6 +285,30 @@ public partial class InventorySystem : EntitySystem return false; } + /// + /// Get the next ContainerSlot in this inventory, along with the corresponding SlotDefinition. + /// The slot may not contain an item. + /// + public bool MoveNext([NotNullWhen(true)] out ContainerSlot? container, [NotNullWhen(true)] out SlotDefinition? slot) + { + while (_nextIdx < _slots.Length) + { + var i = _nextIdx++; + var slotCandidate = _slots[i]; + + if ((slotCandidate.SlotFlags & _flags) == 0) + continue; + + container = _containers[i]; + slot = slotCandidate; + return true; + } + + container = null; + slot = null; + return false; + } + public bool NextItem(out EntityUid item) { while (_nextIdx < _slots.Length) diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs index ffd7f11c0e..33f9b7f59b 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.API.cs @@ -416,7 +416,7 @@ public sealed partial class StatusEffectsSystem public IEnumerable> EnumerateStatusEffects( Entity container) { - if (!_containerQuery.Resolve(container, ref container.Comp) || container.Comp.ActiveStatusEffects == null) + if (!_containerQuery.Resolve(container, ref container.Comp, false) || container.Comp.ActiveStatusEffects == null) yield break; foreach (var effect in container.Comp.ActiveStatusEffects.ContainedEntities) @@ -435,7 +435,7 @@ public sealed partial class StatusEffectsSystem public IEnumerable> EnumerateStatusEffects( Entity container) where T : Component { - if (!_containerQuery.Resolve(container, ref container.Comp) || container.Comp.ActiveStatusEffects == null) + if (!_containerQuery.Resolve(container, ref container.Comp, false) || container.Comp.ActiveStatusEffects == null) yield break; foreach (var effect in container.Comp.ActiveStatusEffects.ContainedEntities) @@ -450,7 +450,7 @@ public sealed partial class StatusEffectsSystem Entity container, EntityQuery query) where T : Component { - if (!_containerQuery.Resolve(container, ref container.Comp) || container.Comp.ActiveStatusEffects == null) + if (!_containerQuery.Resolve(container, ref container.Comp, false) || container.Comp.ActiveStatusEffects == null) yield break; foreach (var effect in container.Comp.ActiveStatusEffects.ContainedEntities) diff --git a/Content.Shared/Store/ListingPrototype.cs b/Content.Shared/Store/ListingPrototype.cs index 7e92c6c5d6..00d912ede9 100644 --- a/Content.Shared/Store/ListingPrototype.cs +++ b/Content.Shared/Store/ListingPrototype.cs @@ -29,6 +29,7 @@ public partial class ListingData : IEquatable other.Icon, other.Priority, other.ProductEntity, + other.ProductComponents, other.ProductAction, other.ProductUpgradeId, other.ProductActionEntity, @@ -55,6 +56,7 @@ public partial class ListingData : IEquatable SpriteSpecifier? icon, int priority, EntProtoId? productEntity, + EntProtoId? productComponents, EntProtoId? productAction, ProtoId? productUpgradeId, EntityUid? productActionEntity, @@ -77,6 +79,7 @@ public partial class ListingData : IEquatable Icon = icon; Priority = priority; ProductEntity = productEntity; + ProductComponents = productComponents; ProductAction = productAction; ProductUpgradeId = productUpgradeId; ProductActionEntity = productActionEntity; @@ -147,6 +150,15 @@ public partial class ListingData : IEquatable [DataField] public int Priority; + /// + /// A dummy entity containing the components to be added to the buyer if the listing is bought. + /// + /// + /// We use an EntProtoId rather than a ComponentRegistry to keep ListingData equatable. + /// + [DataField] + public EntProtoId? ProductComponents; + /// /// The entity that is given when the listing is purchased. /// @@ -222,6 +234,7 @@ public partial class ListingData : IEquatable Name != listing.Name || Description != listing.Description || ProductEntity != listing.ProductEntity || + ProductComponents != listing.ProductComponents || ProductAction != listing.ProductAction || ProductEvent?.GetType() != listing.ProductEvent?.GetType() || RestockTime != listing.RestockTime || @@ -296,6 +309,7 @@ public sealed partial class ListingDataWithCostModifiers : ListingData listingData.Icon, listingData.Priority, listingData.ProductEntity, + listingData.ProductComponents, listingData.ProductAction, listingData.ProductUpgradeId, listingData.ProductActionEntity, diff --git a/Resources/Locale/en-US/changeling/changeling.ftl b/Resources/Locale/en-US/changeling/changeling.ftl index d5c88d06f2..4c639ec77b 100644 --- a/Resources/Locale/en-US/changeling/changeling.ftl +++ b/Resources/Locale/en-US/changeling/changeling.ftl @@ -19,6 +19,10 @@ changeling-devour-consume-complete-others = { CAPITALIZE(POSS-ADJ($user)) } unca # 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} diff --git a/Resources/Locale/en-US/store/changeling-catalog.ftl b/Resources/Locale/en-US/store/changeling-catalog.ftl index eb7795ff36..07dee0aa33 100644 --- a/Resources/Locale/en-US/store/changeling-catalog.ftl +++ b/Resources/Locale/en-US/store/changeling-catalog.ftl @@ -1,2 +1,5 @@ -changeling-arm-blade-name = Retractable Arm Blade -changeling-arm-blade-desc = Transform your arm into a terrifying flesh blade. Can be toggled. +changeling-catalog-arm-blade-name = Retractable Arm Blade +changeling-catalog-arm-blade-desc = Transform your arm into a terrifying flesh blade. Can be toggled. + +changeling-catalog-flesh-clothing-name = Flesh Clothing +changeling-catalog-flesh-clothing-desc = Your body's surface will adapt to mirror the clothing of any person you are transforming into. However, these clothing items are non-functional and will make you easy to identify as a changeling if someone tries to remove them. Can be toggled. diff --git a/Resources/Prototypes/Alerts/changeling.yml b/Resources/Prototypes/Alerts/changeling.yml new file mode 100644 index 0000000000..b6702fb4b4 --- /dev/null +++ b/Resources/Prototypes/Alerts/changeling.yml @@ -0,0 +1,13 @@ +# for ChangelingFleshClothingAbilityComponent +- type: alert + id: ChangelingFleshClothing + clickEvent: !type:ToggleFleshClothingEvent + icons: + - sprite: /Textures/Interface/Alerts/changeling.rsi + state: flesh-clothing-off + - sprite: /Textures/Interface/Alerts/changeling.rsi + state: flesh-clothing-on + name: changeling-flesh-clothing-alert-name + description: changeling-flesh-clothing-alert-desc + minSeverity: 0 + maxSeverity: 1 diff --git a/Resources/Prototypes/Catalog/changeling_catalog.yml b/Resources/Prototypes/Catalog/changeling_catalog.yml index dea0f908eb..57b0addaa6 100644 --- a/Resources/Prototypes/Catalog/changeling_catalog.yml +++ b/Resources/Prototypes/Catalog/changeling_catalog.yml @@ -2,9 +2,8 @@ - type: listing id: ChangelingArmBlade - name: changeling-arm-blade-name - description: changeling-arm-blade-desc - productAction: ActionRetractableItemArmBlade + name: changeling-catalog-arm-blade-name + description: changeling-catalog-arm-blade-desc applyToMob: true cost: ChangelingDNA: 25 @@ -13,3 +12,18 @@ conditions: - !type:ListingLimitedStockCondition stock: 1 + productAction: ActionRetractableItemArmBlade + +- type: listing + id: ChangelingFleshClothing + name: changeling-catalog-flesh-clothing-name + description: changeling-catalog-flesh-clothing-desc + icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: flesh_clothing } + cost: + ChangelingDNA: 10 + categories: + - ChangelingAbilities + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + productComponents: ChangelingFleshClothingAbilityStoreDummy diff --git a/Resources/Prototypes/Entities/Clothing/Misc/changeling.yml b/Resources/Prototypes/Entities/Clothing/Misc/changeling.yml new file mode 100644 index 0000000000..ca9cc5db59 --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Misc/changeling.yml @@ -0,0 +1,269 @@ +# this file contains chameleon clothing for the changelings transformation ability +# one for each slot + +- type: entity + abstract: true + parent: Clothing + id : ChangelingFleshClothingBase + suffix: Changeling + components: + - type: Clothing + quickEquip: false + - type: FleetingClothing + removedSound: + path: /Audio/Voice/Slime/slime_squish.ogg # TODO: replace placeholder + playSoundOnSelfUnequip: false # silent if you remove it yourself + selfUnquipPopupWearer: null + selfUnquipPopupOthers: null + removedPopup: changeling-flesh-clothing-removed-popop + examineWearer: changeling-flesh-clothing-examine-wearer + examineOthers: null # stealthy unless stripped + - type: ChangelingFleshClothing + - type: ChameleonClothing # has the component, but not the BUI, so that it cannot be set manually + affectedByEmp: false + canBeSetByController: false + showVerb: false + - type: FlavorProfile + flavors: + - meaty + - type: Tag + tags: [] # ignore "WhitelistChameleon" tag + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingBack + name: changeling flesh backpack + components: + - type: Sprite + sprite: Clothing/Back/Backpacks/backpack.rsi + state: icon + - type: Item + size: Huge + - type: Clothing + slots: + - back + - type: ChameleonClothing + default: ChangelingFleshClothingBack + slot: + - back + # does not have storage, it's just a visual replacement for stealth purposes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingBelt + name: changeling flesh belt + components: + - type: Sprite + sprite: Clothing/Belt/utility.rsi + state: icon + - type: Item + size: Normal + - type: Clothing + slots: + - belt + - type: ChameleonClothing + default: ChangelingFleshClothingBelt + slot: + - belt + - type: StaticPrice # same as ClothingBeltBase + price: 20 + # does not have storage, it's just a visual replacement for stealth purposes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingEars + name: changeling flesh headset + components: + - type: Sprite + sprite: Clothing/Ears/Headsets/base.rsi + state: icon + - type: Item + size: Small + - type: Clothing + slots: + - ears + - type: ChameleonClothing + default: ChangelingFleshClothingEars + slot: + - ears + # not functional, it's just a visual replacement for stealth purposes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingEyes + name: changeling flesh glasses + components: + - type: Sprite + sprite: Clothing/Eyes/Glasses/glasses.rsi + state: icon + - type: Item + size: Small + storedRotation: -90 + - type: Clothing + slots: + - eyes + - type: ChameleonClothing + default: ChangelingFleshClothingEyes + slot: + - eyes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingGloves + name: changeling flesh gloves + components: + - type: Sprite + sprite: Clothing/Hands/Gloves/Color/color.rsi + layers: + - state: icon + color: "#999999" + - type: Item + size: Small + storedRotation: -90 + - type: Clothing + slots: + - gloves + - type: ChameleonClothing + default: ChangelingFleshClothingGloves + slot: + - gloves + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingHead + name: changeling flesh hat + components: + - type: Sprite + sprite: Clothing/Head/Soft/greysoft.rsi + state: icon + - type: Item + size: Small + storedRotation: -90 + - type: Clothing + slots: + - head + - type: ChameleonClothing + default: ChangelingFleshClothingHead + slot: + - head + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingMask + name: changeling flesh mask + components: + - type: Sprite + sprite: Clothing/Mask/gas.rsi + state: icon + - type: Item + size: Small + - type: Clothing + slots: + - mask + - type: ChameleonClothing + default: ChangelingFleshClothingMask + slot: + - mask + - type: StaticPrice # same as ClothingMaskBase + price: 25 + # identity blockers are dynamically added by ChameleonClothingSystem + # TODO: check if we need to do the same for HideLayerClothingComponent + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingNeck + name: changeling flesh tie + components: + - type: Sprite + sprite: Clothing/Neck/Ties/redtie.rsi + state: icon + - type: Item + size: Small + - type: Clothing + slots: + - neck + - type: ChameleonClothing + default: ChangelingFleshClothingNeck + slot: + - neck + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingInner + name: changeling flesh jumpsuit + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/color.rsi + layers: + - state: icon + color: "#b3b3b3" + - state: trinkets-icon + - type: Item + size: Normal + - type: Clothing + slots: + - innerclothing + - type: ChameleonClothing + default: ChangelingFleshClothingInner + slot: + - innerclothing + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingOuter + name: changeling flesh vest + components: + - type: Sprite + sprite: Clothing/OuterClothing/Vests/vest.rsi + state: icon + - type: Item + size: Huge + - type: Clothing + slots: + - outerClothing + - type: ChameleonClothing + default: ChangelingFleshClothingOuter + slot: + - outerClothing + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingFeet + name: changeling flesh shoes + components: + - type: Sprite + sprite: Clothing/Shoes/color.rsi + layers: + - state: icon + color: "#EAE8E8" #Deliberately NOT pure white + - type: Item + size: Normal + - type: Clothing + slots: + - feet + - type: ChameleonClothing + default: ChangelingFleshClothingFeet + slot: + - feet + # not functional, it's just a visual replacement for stealth purposes + +- type: entity + parent: ChangelingFleshClothingBase + id: ChangelingFleshClothingSuitStorage + name: changeling flesh gas tank + components: + - type: Sprite + sprite: Objects/Tanks/generic.rsi + state: icon + - type: Item + size: Normal + - type: Clothing + quickEquip: false + sprite: Objects/Tanks/generic.rsi + slots: + - suitStorage + - type: ChameleonClothing + default: ChangelingFleshClothingSuitStorage + slot: + - suitStorage + # not functional, it's just a visual replacement for stealth purposes diff --git a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml index 424c942c66..dcddf77e1f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml @@ -13,3 +13,24 @@ - type: Store balance: ChangelingDNA: 50 + +- 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 7914175cf5..394e8878bb 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -84,16 +84,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 @@ -113,7 +117,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. @@ -134,9 +138,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/Textures/Interface/Actions/changeling2.rsi/flesh_clothing.png b/Resources/Textures/Interface/Actions/changeling2.rsi/flesh_clothing.png new file mode 100644 index 0000000000000000000000000000000000000000..7b593f34492569cb7b049c73c4b5afe15157bcdb GIT binary patch literal 1863 zcmV-N2e|l&P)Px+0ZBwbR9JS<&pfwD90v@E8IFgc(9yIBgf44FJrMImwnh+jkVOPl zn_5;umqn3~Lia<6MN!!_rgjKWL3E(C6^g7-RYXmtu%=SNYj76T0TMz)vtZRuaDL#~ zjswJwy+6!*c@H}@>sPY$@ZB*oesW7H&BNLv_*w^L~08W>cCKg+5>^|!hMD=E!p8v91uLqKjG`&`p2zos% zw%Q1KJ&K+&W>CIceLmK%Dh9v`Nd3KR%hfCLEFi#hj0&qbR2WHEEy=Y(uZO1BsuId6 zUs<;fUV8qatF&+u)<@W9wu3R7O43EK(_0N;y_0}vJ?2r`ZG zqH{xo{H^-u1ic;_JHJfX2;k7>=K&ZMRsrA{9pTcgD`|DrYZ^PhWZTm}B>G^^LE*f^ zk!bb#SgqG{ZfKC)Cl>KW=?30)U486^-v)1#ZUE+3ZS3n4#8#h=^89>-w%_flAn5gA z6m<*QG#raB*nxLlSDBcZ<<&LoXzc3~0L0Cwk+C3(+3Ny$m%wR=92IiU^>MQCL+SSy{VHv&rBMw})vp2Moy`hy{ zNF3y;U;{=`r%713D{AwaL1i?+d=8ix!UGg(oP0l=x&MQDyu3y4)x&=v+6kS)h8-TapIl-}+tvnTM(ClT58CV4HtikXnfZo6)d7Cm3 zA3&aXj;!XBOxRv$?UMH~em|3&1~+2oQf`F;6s=rC$B z^s@1?i5vcFoVA?Cd9fMk`XxoKt_m4;yB$}zn{X_SbYPmr#_ww6!_g=}(t&IK`>eMZ zUzA0G@+(;`Bjk_qi-B^ovX?U+ zigBv-I5tZ@`^#(jV)zEmo+K;Pe|Zyt(ib)YD9Nf$*wuQymVvzQJTsRVqK@%UjQ!=c z9KCpqJ-L4;)bayUJ~q)c}6KpV84#(xG@$1&wEp2mr!` z*Z+_ROoj1W|6I9_jiQcUZmHt^4kz6&cJWjD&$u6&Wvi=9(*XfZ7d-LFzchwi-EKZ; zsMq{AF*D1rx9p^7TG-|+CRarQJ*RZ>Y+NhP25e#A?~ zI{`F(z}4;6gi#%az*HCj-L5l71n=E3Y(+m-ghwwP(_}d$8&Sl458s0z&YouKE9%J0 z5pni36OP5n%Ms}~de>W==lbW0fpn## z-f%woqhXs51!gm|zJ8?s-ed^8cjq1(>R8E;FC^K)M;%HBbso$IYdnA`_4kq`ga}5X zoZkL2e{5^fY?{f}eAO}6nj?=GeAJ;UT^(HM>PRaO$Kssc{xZSINg&CDm>d=?1aJ2$ z0yS23RHk_=&EZi7!fh?{Xu?}%JMiAQL+98S@(?5(DlAlHyWwb*^89=bc6C0+yjnde2g;&?C1Ck5~>XFQ9GT{vbC@`DTj#p|Fm{Uu4zBTD|0m9K}(gA?YW;5PE zK%+1mjq>5(pfb^ddVnQGR~}M9+gs!cQ)DI+M!gK-IuSWOWwN8z0&4vw$rL%J{;=79$aq{X_;eSM#Ktl@=11$gm002ovPDHLkV1oSB BlI#Eg literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..45eb0e3854046302f15ce1789d4a0a841da5edfc GIT binary patch literal 1626 zcmV-g2BrClP)Px*6iGxuR9J2b4Z9wh1P2p{O{_(iC z@3r0B{3b7X_ug~QIrsd$SK&5}1_F9Q9Ra{OHkVhLniQsiqk(`f{zvm40U#&KDc57e z339TWTnjhyc0))3sH^np*&Z+JzC5m|W@gRdzbLfZ0Yi~vZ>-hhkucdFFY!oN(mBWG z%K!Rklm`};0N??Fms+WqZQ zgsE*lpT-Dad--wz`jmwLg!_6q{rj&OY0Z0To6l3R=y9~0Nd=Xofq-5gjZ$d0bGW;U z`Lk#7u5TF!LTytr{6Bctw+u*Hwf5pgMXirUS><+1vwcvtns_9PQ?*TtX<}?*+6o*9 zwQ=p%I2-O?Ozp*s3V^NLdl$Ba^C@z<03$dKgxW}qO-%VcTk;~$mb{oz(58tl?u*w< zsz{N`C9_90%n=4P>D#GJ|N4__ab)EKFgo1N#=tuEHyxAf_r+_#39FpzsE@>X)5 zgw+AmBA1Hu_Y{6Yh-y;4N@7I^56J)zyT1qnv9jX3H5re*e7y zyvh4iDKMC8SVOq0&~8W692G=fkEF_z*t)t}{)c;`L~q88L2putlYd`k*O_xxkk9HsP_CF{s)s0sD4CNU5vz>6S`Z+kBq+vuDW|Xzy$UtTB-q1LFT|@F%vs zw@K24S}p+a*`9suy|sxdWe152@t~$oIB#oeHoL&+a6f}XV=!d6!96Ei{^u`UAO-r$ zN&z@hvz6C&>?Rltrtvx&2X#sKa!J#pJX#F-xT(puW3LnOcn;;1} z?f3ho1%g{f$#yPCQvi(EQ^8uhkDvP{Be6Nv0Me`jzD6O2Nm^eIsLI_jh|Oid9!vbMjOJ-G6k_-``JP zUtd~56AJZc8ijVd(F??)(b>*RO#!~B|2*vkV$RHbbhw|BryB5;mC67NwOrsxP3CS| zXt!G}kazb(79rDZFv1OvtbB^fN-cFj1YstWtXNI$!JP_#dg85ZdTcmBxZ_8_Fq8Ro z@2uJk7%mXz7Gr_4J->0a#Usb5)i4zue|B@?t!;YM!JSH48UidWT506?ZRRC;x#Znx zB^6^wRzB@NAE*7}M)>d(=>kY^YG%vEhK;mUs}Al|K6>V5-CO)Lbaw&CAQVV1BSvEq z=AX=ElVw4;uNTieqy5_eIhYCdHyx9m(W%-pws0{oKB16jB1YuK0EgCZ)-$@m%xr1V zN&x8+b0rjYAoZl89!w{gl|$<{>j2@7A35CJMWMq!=|+EX1y2+{EQJpajae$^qRQtj z^uITJpT^_bt#Nj(D5rB^fQT)}NW;jDWGPO~0yEk>8!i1I6`2H11x92aLO+YCIMSh+k=y3p2q?l>NB!c_LS@(mXJL zcmyfimn@ODp&m_RmD|nxzckNSk^jQ373Cx}joNd+vf=*4fMEf}{3P?_-EQ9;slhEuzfJwVWKB zc00lA*NwNKP{NFI?_Fs^#PeD}?0af?CBaLr7TUw@%@*2?%a-Z$#*;VV8?$DyzpYLA Y7p7UupzfOwJpcdz07*qoM6N<$f-3h6DF6Tf literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json b/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json new file mode 100644 index 0000000000..a9c507fe65 --- /dev/null +++ b/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark.", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "flesh_clothing" + }, + { + "name": "flesh_clothing_alt" + } + ] +} diff --git a/Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-off.png b/Resources/Textures/Interface/Alerts/changeling.rsi/flesh-clothing-off.png new file mode 100644 index 0000000000000000000000000000000000000000..47991017abbffa96b3c30a3aeb16d07727b187ac GIT binary patch literal 542 zcmV+(0^$9MP)Px$*hxe|R9J=WRV{DAKp1{*8M3-TOl@Wo14%KcCHqj_1p5&nir_a?P(ncNH=u^W zuq7A8bQ z)Uo;zv5wV`nDV%0VInmktpOb-^}cJcx&r{fe6bA0UFRh<_TpA|fZlgaS`Uee&~Y39 zK+9+_Z|oUTRn-?1&66RKfz|aH9OKI$SF2UwydduwUszqAN%uaLO&FrhhM(;aXLSdt zluIG5QZD(Y61Pr-RDB>Q2JDX5`{hj~0ZY3;(=;j;i%?Y+=NFf_yS>8mXpE2dH*B|C zY&ILXuA6vY=@sy}e|QXK`aBw=QZD)2e6fV;>)Z_Ffx|Qq*J6{jULL87a5jC#Z2BtY4hGVWMMRO$DPxSKPx%-bqA3R9J=WmCs5WQ5431S52oGVx2N->ZBzNT3X!HO`v2g*$F}(A{EW4|oY>0miYvzfz;x=g zTNN*9+=!7c6==6A-o9T1@<3v40iY+j`+Zck0a%*RsddKr{QVmMFQ3j1lwx97;_a2J z1gP3J$EPR!7`rB>OhLoMbn3*EDdI+qCE%!Pdj?!jt;)Caud!+u1LNOGz-_hS`5Agh zWh=icpeu_>om66xINj1?b!CV(Iuy^(0GQEr-WNX(yPtmv+~o65W{Ddypj-NCqvL6w zN-R?P`l+Wqhar#zS~%jhAb=W)5|02DUUw*}7UWKYyI^xUBiHtFqH5dB=sF=)Azvy` zZFG7}=vKsZ>Qrqz5P?7vK(7q0`%{TU%2wBuSB^vOaNl>_p@Zm8)$u3d}uzM09d8P|}+ST!B~7uF2F$lq<(!<6uWrnoaMW zn9=po5ZGMKNY`NPRt2ke;c2aeHE$>dg>M2%SYs={D?WU&P$SVEx4Xk9Zp3gdYk>&( z_JBLjbBFsPF*SMXm2*3x)M$=|0CY|0T-Io`uf5R>@aEYIUt{;bgzteNrMy*kA%t+( zg%H9$=l;Go0>6XyTgP2@`3WK1bziXq&jBeVeTqSVOaC2#`^LQyxGBd-2?!zXOw>=B iq4E44990+mbNm8$tz&2^(>><^0000 Date: Sun, 19 Apr 2026 17:34:38 +0000 Subject: [PATCH 222/247] Container toolshed commands (#43098) * First commit. * container:id * contents * locale --------- Co-authored-by: UpAndLeaves <92269094+Alpha-Two@users.noreply.github.com> Co-authored-by: ScarKy0 --- Content.Server/Containers/ContainerCommand.cs | 87 +++++++++++++++++++ Content.Server/Inventory/InventoryCommand.cs | 2 +- Content.Server/Storage/StorageCommand.cs | 2 +- .../commands/toolshed/container-command.ftl | 14 +++ .../commands/toolshed/inventory-command.ftl | 2 +- .../commands/toolshed/storage-command.ftl | 2 +- 6 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 Content.Server/Containers/ContainerCommand.cs create mode 100644 Resources/Locale/en-US/commands/toolshed/container-command.ftl diff --git a/Content.Server/Containers/ContainerCommand.cs b/Content.Server/Containers/ContainerCommand.cs new file mode 100644 index 0000000000..914c9025a0 --- /dev/null +++ b/Content.Server/Containers/ContainerCommand.cs @@ -0,0 +1,87 @@ +using System.Linq; +using Content.Server.Administration; +using Content.Shared.Administration; +using Robust.Shared.Containers; +using Robust.Shared.Toolshed; + +namespace Content.Server.Containers; + +[ToolshedCommand, AdminCommand(AdminFlags.Debug)] +public sealed class ContainerCommand : ToolshedCommand +{ + private SharedContainerSystem? _container; + + [CommandImplementation("contents")] + public IEnumerable ContainerQuery([PipedArgument] IEnumerable storageEnts, string id) => + storageEnts.SelectMany(x => ContainerQueryBase(x, id)); + + + public IEnumerable ContainerQueryBase(EntityUid ent, string id) + { + _container ??= GetSys(); + + if (!_container.TryGetContainer(ent, id, out var container)) + return []; + + return container.ContainedEntities; + } + + [CommandImplementation("get")] + public IEnumerable ContainerGet([PipedArgument] IEnumerable storageEnts, string id) => + storageEnts.Select(x => ContainerGetBase(x, id)).Where(s => s != null).Select(s => s!); + + [CommandImplementation("id")] + public IEnumerable ContainerId([PipedArgument] IEnumerable containers) => + containers.Select(x => x.ID); + + + public BaseContainer? ContainerGetBase(EntityUid ent, string id) + { + _container ??= GetSys(); + + if (!_container.TryGetContainer(ent, id, out var container)) + return null; + + return container; + } + + [CommandImplementation("insertmultiple")] + public BaseContainer ContainerInsert([PipedArgument] BaseContainer container, bool doForce, IEnumerable ents) + { + _container ??= GetSys(); + + foreach (var ent in ents) + { + if (doForce) + { + _container.Insert(ent, container, null, true); + } + else + { + _container.InsertOrDrop(ent, container); + } + } + return container; + } + + [CommandImplementation("insert")] + public BaseContainer ContainerInsert([PipedArgument] BaseContainer container, bool doForce, EntityUid ent) + { + return ContainerInsert(container, doForce, [ent]); + } + + [CommandImplementation("list")] + public IEnumerable ContainerList([PipedArgument] EntityUid ent) + { + _container ??= GetSys(); + + return _container.GetAllContainers(ent).Select(container => container.ID); + } + [CommandImplementation("getall")] + public IEnumerable ContainerGetAll([PipedArgument] EntityUid ent) + { + _container ??= GetSys(); + + return _container.GetAllContainers(ent); + } +} diff --git a/Content.Server/Inventory/InventoryCommand.cs b/Content.Server/Inventory/InventoryCommand.cs index e628ee6e01..2a7bcab909 100644 --- a/Content.Server/Inventory/InventoryCommand.cs +++ b/Content.Server/Inventory/InventoryCommand.cs @@ -12,7 +12,7 @@ public sealed class InventoryCommand : ToolshedCommand { private InventorySystem? _inventorySystem; - [CommandImplementation("query")] + [CommandImplementation("contents")] public IEnumerable InventoryQuery([PipedArgument] IEnumerable entities) => entities.SelectMany(InventoryQuery); diff --git a/Content.Server/Storage/StorageCommand.cs b/Content.Server/Storage/StorageCommand.cs index 1c39801622..d5855388fa 100644 --- a/Content.Server/Storage/StorageCommand.cs +++ b/Content.Server/Storage/StorageCommand.cs @@ -52,7 +52,7 @@ public sealed class StorageCommand : ToolshedCommand return null; } - [CommandImplementation("query")] + [CommandImplementation("contents")] public IEnumerable StorageQuery([PipedArgument] IEnumerable storageEnts, bool recursive) => storageEnts.SelectMany(x => StorageQueryRecursiveBase(x, recursive)); diff --git a/Resources/Locale/en-US/commands/toolshed/container-command.ftl b/Resources/Locale/en-US/commands/toolshed/container-command.ftl new file mode 100644 index 0000000000..077c1439a3 --- /dev/null +++ b/Resources/Locale/en-US/commands/toolshed/container-command.ftl @@ -0,0 +1,14 @@ +command-description-container-contents = + Gets all entities inside a container on an entity via the container's ID. +command-description-container-get = + Gets a container on an entity via the container's ID. +command-description-container-insert = + Puts an entity inside the piped container. +command-description-container-insertmultiple = + Put multiple entities inside the piped container. +command-description-container-list = + Gets the IDs of all containers in an entity. +command-description-container-getall = + Gets all containers in an entity. +command-description-container-id = + Gets the string id of the piped in containers. diff --git a/Resources/Locale/en-US/commands/toolshed/inventory-command.ftl b/Resources/Locale/en-US/commands/toolshed/inventory-command.ftl index f430197495..667db5166f 100644 --- a/Resources/Locale/en-US/commands/toolshed/inventory-command.ftl +++ b/Resources/Locale/en-US/commands/toolshed/inventory-command.ftl @@ -18,5 +18,5 @@ command-description-inventory-ensure = Puts a given entity on the first piped entity that has a slot matching the given flag if none exists, passing through the UID of whatever is in the slot by the end. command-description-inventory-ensurespawn = Spawns a given prototype on the first piped entity that has a slot matching the given flag if none exists, passing through the UID of whatever is in the slot by the end. -command-description-inventory-query = +command-description-inventory-contents = Gets the entities in the inventory slots of the piped entities and passes them along. diff --git a/Resources/Locale/en-US/commands/toolshed/storage-command.ftl b/Resources/Locale/en-US/commands/toolshed/storage-command.ftl index 154196fbb1..c912df4d76 100644 --- a/Resources/Locale/en-US/commands/toolshed/storage-command.ftl +++ b/Resources/Locale/en-US/commands/toolshed/storage-command.ftl @@ -2,5 +2,5 @@ command-description-storage-fasttake = Takes the most recently placed item from the piped storage entity. command-description-storage-insert = Inserts the piped entity into the given storage entity. -command-description-storage-query = +command-description-storage-contents = Gets the entities in the storagebase of the piped entities and passes them along. From 287a18a8a5641c6b9544ff0d8aecffe74ab719b5 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 19 Apr 2026 17:48:39 +0000 Subject: [PATCH 223/247] Automatic changelog update --- Resources/Changelog/Admin.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 43420c1019..96e27d879f 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -1707,5 +1707,14 @@ Entries: 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 From c7300798f65c8619c7750d84001b7ce25f92baf3 Mon Sep 17 00:00:00 2001 From: eoineoineoin Date: Sun, 19 Apr 2026 18:38:07 +0100 Subject: [PATCH 224/247] Fix AI eye warp examine text (#43650) * Fix AI eye warp examine text * Update Content.Shared/Warps/WarpPointSystem.cs Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> * Missed FTL file, whoops --------- Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> --- Content.Shared/Warps/WarpPointSystem.cs | 4 ++-- Resources/Locale/en-US/warps/warp-point-component.ftl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Warps/WarpPointSystem.cs b/Content.Shared/Warps/WarpPointSystem.cs index c8474acd33..7993b76234 100644 --- a/Content.Shared/Warps/WarpPointSystem.cs +++ b/Content.Shared/Warps/WarpPointSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Examine; +using Content.Shared.Examine; using Content.Shared.Ghost; namespace Content.Shared.Warps; @@ -16,7 +16,7 @@ public sealed class WarpPointSystem : EntitySystem if (!HasComp(args.Examiner)) return; - var loc = component.Location == null ? "" : $"'{component.Location}'"; + var loc = component.Location == null ? Name(uid) : component.Location; args.PushText(Loc.GetString("warp-point-component-on-examine-success", ("location", loc))); } } diff --git a/Resources/Locale/en-US/warps/warp-point-component.ftl b/Resources/Locale/en-US/warps/warp-point-component.ftl index 988c172363..9878d0bf3d 100644 --- a/Resources/Locale/en-US/warps/warp-point-component.ftl +++ b/Resources/Locale/en-US/warps/warp-point-component.ftl @@ -1 +1 @@ -warp-point-component-on-examine-success = This one's location ID is {$location} \ No newline at end of file +warp-point-component-on-examine-success = This one's location ID is '{$location}' From 4c6e84522b4c0a3cd0b4e6ea819593a66a05e050 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 19 Apr 2026 10:47:37 -0700 Subject: [PATCH 225/247] Fix Canister overfill and leaks (#43652) * whoops * oh right forgot to update that * remove limits (real) * buge --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs | 5 +++-- .../Atmos/Piping/Unary/Components/GasCanisterComponent.cs | 4 ++++ Content.Shared/CCVar/CCVars.Atmos.cs | 4 ++-- Resources/ConfigPresets/WizardsDen/wizardsDen.toml | 2 +- .../Entities/Structures/Storage/Canisters/gas_canisters.yml | 4 ++-- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 632e06caa8..60e6f5a20d 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -83,7 +83,7 @@ public sealed class GasCanisterSystem : SharedGasCanisterSystem private void ToggleSafetyValve(Entity entity, bool open) { - entity.Comp.SafetyValveOpen = true; + entity.Comp.SafetyValveOpen = open; Audio.PlayPvs(entity.Comp.ValveSound, entity); } @@ -117,7 +117,8 @@ public sealed class GasCanisterSystem : SharedGasCanisterSystem ? _atmos.GetContainingMixture(entity.Owner, args.Grid, args.Map, false, true) : CompOrNull(entity.Comp.GasTankSlot.Item.Value)?.Air; - _atmos.FlowGas(entity.Comp.Air, output, entity.Comp.ReleasePressure, args.dt,ReleaseArea); + // 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. diff --git a/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs b/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs index aa1fcf6559..2c19638ee0 100644 --- a/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs +++ b/Content.Shared/Atmos/Piping/Unary/Components/GasCanisterComponent.cs @@ -20,6 +20,10 @@ public sealed partial class GasCanisterComponent : GasMaxPressureHolderComponent [DataField] public ItemSlot GasTankSlot = new(); + /// + /// The safety release valve on this gas canister. Automatically opens + /// when is reached. + /// [DataField] public bool SafetyValveOpen; diff --git a/Content.Shared/CCVar/CCVars.Atmos.cs b/Content.Shared/CCVar/CCVars.Atmos.cs index ab4626cd57..eccee01151 100644 --- a/Content.Shared/CCVar/CCVars.Atmos.cs +++ b/Content.Shared/CCVar/CCVars.Atmos.cs @@ -154,8 +154,8 @@ 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", 0f, CVar.SERVER); diff --git a/Resources/ConfigPresets/WizardsDen/wizardsDen.toml b/Resources/ConfigPresets/WizardsDen/wizardsDen.toml index bcd4ee648f..680b5d0455 100644 --- a/Resources/ConfigPresets/WizardsDen/wizardsDen.toml +++ b/Resources/ConfigPresets/WizardsDen/wizardsDen.toml @@ -52,7 +52,7 @@ alert.min_players_sharing_connection = 2 allow_multi_server_play = false [atmos] -max_explosion_range = 10 +max_explosion_range = 0 [status] privacy_policy_link = "https://spacestation14.com/about/privacy/#game-server-privacy-policy" diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index 86b285c7a9..961e3a6487 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -127,8 +127,8 @@ maxIntegrity: 25 # 5 times stronger than a gas tank integrity: 25 maxReleasePressure: 1013.25 - safetyPressure: 5066.25 - overpressure: 15198.75 # Purposefully high because cans react really fucking fast, may need to redo this value if reactions change! + safetyPressure: 7599.375 # High enough that liquid tanks don't burst immediately + 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 From ff2a5d22291f21adae81df93a4676376228b1e16 Mon Sep 17 00:00:00 2001 From: Tortosaur <135037883+AffleWaffle@users.noreply.github.com> Date: Mon, 20 Apr 2026 04:25:20 +0200 Subject: [PATCH 226/247] IntercomAssembly Fix (#43647) Add Destructible --- .../Wallmounts/WallmountMachines/intercom.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml index f1d182fa60..fda76c6ce1 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/WallmountMachines/intercom.yml @@ -128,6 +128,25 @@ - state: panel visible: false map: [ "wires" ] + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalGlassBreak + params: + volume: -4 - type: Appearance - type: GenericVisualizer visuals: From c9df5ef5d675b0d1d226828bddf6b78c28502d91 Mon Sep 17 00:00:00 2001 From: PJBot Date: Mon, 20 Apr 2026 02:41:08 +0000 Subject: [PATCH 227/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4767f790df..f8bb00f157 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4046,3 +4039,10 @@ 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 From 5c63c2d6fb465d4d0689d41d9cd853a3159e02f9 Mon Sep 17 00:00:00 2001 From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Date: Mon, 20 Apr 2026 00:50:21 -0700 Subject: [PATCH 228/247] Fix a `HeatContainerHelpers` generic issue (#43661) fix multigeneric --- .../Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs index 8551f5688c..0bbd9698aa 100644 --- a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs +++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs @@ -55,7 +55,9 @@ public static partial class HeatContainerHelpers /// integration steps with adaptive step size. /// [PublicAPI] - public static float ConductHeat(ref T cA, ref T cB, float deltaTime, float g) where T : IHeatContainer + 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, ref cB, deltaTime, g); AddHeat(ref cA, dQ); From 06646af58729f9e508c62aa8fbb3a7f48a26d454 Mon Sep 17 00:00:00 2001 From: Jessica M Date: Mon, 20 Apr 2026 15:34:20 -0700 Subject: [PATCH 229/247] Remove PuddlesQuery.cs (#43672) HAPPY 4/20 --- Content.Server/NPC/Queries/Queries/PuddlesQuery.cs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Content.Server/NPC/Queries/Queries/PuddlesQuery.cs diff --git a/Content.Server/NPC/Queries/Queries/PuddlesQuery.cs b/Content.Server/NPC/Queries/Queries/PuddlesQuery.cs deleted file mode 100644 index 0c88cc2de6..0000000000 --- a/Content.Server/NPC/Queries/Queries/PuddlesQuery.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Server.NPC.Queries.Queries; - -public sealed partial class PuddlesQuery : UtilityQuery -{ - -} From 5b9c0a6bdc13d946014b3075bb94a73c6e902e65 Mon Sep 17 00:00:00 2001 From: B_Kirill <153602297+B-Kirill@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:23:17 +1000 Subject: [PATCH 230/247] NPCs combat improvements (#40900) * NPCs combat improvements * CanRack and improved logic * Minor stylistic improvements * Just a test for my sanity * Review --- .../Preconditions/ItemTogglePrecondition.cs | 39 ++++++++++++++ .../NeedToRackBoltPrecondition.cs | 38 +++++++++++++ .../HTN/Preconditions/WieldedPrecondition.cs | 45 ++++++++++++++++ .../Operators/Combat/RackBoltOperator.cs | 33 ++++++++++++ .../Operators/Combat/WieldOperator.cs | 53 +++++++++++++++++++ .../Interactions/UseItemInHandOperator.cs | 24 +++++++++ .../SharedGunSystem.ChamberMagazine.cs | 4 +- Resources/Prototypes/NPCs/Combat/gun.yml | 23 ++++++++ Resources/Prototypes/NPCs/Combat/melee.yml | 28 ++++++++++ 9 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 Content.Server/NPC/HTN/Preconditions/ItemTogglePrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/NeedToRackBoltPrecondition.cs create mode 100644 Content.Server/NPC/HTN/Preconditions/WieldedPrecondition.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/RackBoltOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/WieldOperator.cs create mode 100644 Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/UseItemInHandOperator.cs diff --git a/Content.Server/NPC/HTN/Preconditions/ItemTogglePrecondition.cs b/Content.Server/NPC/HTN/Preconditions/ItemTogglePrecondition.cs new file mode 100644 index 0000000000..431e68a55b --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/ItemTogglePrecondition.cs @@ -0,0 +1,39 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Item.ItemToggle.Components; + +namespace Content.Server.NPC.HTN.Preconditions; + +/// +/// Checks if the item in the active hand has and whether its +/// state matches the expected value. +/// +public sealed partial class ItemTogglePrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + /// + /// The activation state to check for. + /// + [DataField] + public bool Activated = true; + + public override bool IsMet(NPCBlackboard blackboard) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return false; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var heldEntity)) + return false; + + if (!_entManager.TryGetComponent(heldEntity, out var itemToggle)) + return false; + + if (!itemToggle.OnUse) + return false; + + return itemToggle.Activated == Activated; + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/NeedToRackBoltPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/NeedToRackBoltPrecondition.cs new file mode 100644 index 0000000000..7872a66f02 --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/NeedToRackBoltPrecondition.cs @@ -0,0 +1,38 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Systems; + +namespace Content.Server.NPC.HTN.Preconditions; + +/// +/// Checks if the NPC's gun needs bolt racking - either bolt is open OR bolt is closed but chamber is empty. +/// Returns true if gun needs racking to prepare for firing. +/// +public sealed partial class NeedToRackBoltPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + public override bool IsMet(NPCBlackboard blackboard) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return false; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var heldEntity)) + return false; + + if (!_entManager.TryGetComponent(heldEntity, out var chamberMagazine)) + return false; + + if (!chamberMagazine.CanRack) + return false; + + var gunSystem = _entManager.System(); + var chamberEntity = gunSystem.GetChamberEntity(heldEntity.Value); + bool hasRoundInChamber = chamberEntity is not null; + + return chamberMagazine.BoltClosed == false || !hasRoundInChamber; + } +} diff --git a/Content.Server/NPC/HTN/Preconditions/WieldedPrecondition.cs b/Content.Server/NPC/HTN/Preconditions/WieldedPrecondition.cs new file mode 100644 index 0000000000..dc9a9cbb7d --- /dev/null +++ b/Content.Server/NPC/HTN/Preconditions/WieldedPrecondition.cs @@ -0,0 +1,45 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Wieldable; +using Content.Shared.Wieldable.Components; + +namespace Content.Server.NPC.HTN.Preconditions; + +/// +/// Checks if the item in the active hand has and whether its +/// state matches the expected value. +/// When is false, also checks that the item can actually be wielded. +/// +public sealed partial class WieldedPrecondition : HTNPrecondition +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + /// + /// The wield state to check for. + /// + [DataField] + public bool Wielded = true; + + public override bool IsMet(NPCBlackboard blackboard) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return false; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var heldEntity)) + return false; + + if (!_entManager.TryGetComponent(heldEntity, out var wieldable)) + return false; + + if (Wielded) + return wieldable.Wielded; + + if (wieldable.Wielded) + return false; + + var wieldableSystem = _entManager.System(); + return wieldableSystem.CanWield(heldEntity.Value, wieldable, owner, quiet: true); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/RackBoltOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/RackBoltOperator.cs new file mode 100644 index 0000000000..6dc8e49401 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/RackBoltOperator.cs @@ -0,0 +1,33 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Systems; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; + +/// +/// Operator that racks the bolt of a gun with . +/// +public sealed partial class RackBoltOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return HTNOperatorStatus.Failed; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var gunUid)) + return HTNOperatorStatus.Failed; + + if (!_entManager.TryGetComponent(gunUid, out var chamberMagazine)) + return HTNOperatorStatus.Failed; + + var gunSystem = _entManager.System(); + gunSystem.UseChambered(gunUid.Value, chamberMagazine, owner); + + return HTNOperatorStatus.Finished; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/WieldOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/WieldOperator.cs new file mode 100644 index 0000000000..98cd2afd4d --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/WieldOperator.cs @@ -0,0 +1,53 @@ +using Content.Server.Hands.Systems; +using Content.Shared.Wieldable; +using Content.Shared.Wieldable.Components; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; + +/// +/// Operator that wields or unwields a weapon. +/// +public sealed partial class WieldOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + /// + /// If true, tries to wield the item. If false, tries to unwield it. + /// + [DataField] + public bool Wield = true; + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return HTNOperatorStatus.Failed; + + var handsSystem = _entManager.System(); + if (!handsSystem.TryGetHeldItem(owner, activeHand, out var weaponUid)) + return HTNOperatorStatus.Failed; + + if (!_entManager.TryGetComponent(weaponUid, out var wieldable)) + return HTNOperatorStatus.Failed; + + var wieldableSystem = _entManager.System(); + + if (Wield) + { + if (wieldable.Wielded) + return HTNOperatorStatus.Failed; + + return wieldableSystem.TryWield(weaponUid.Value, wieldable, owner) + ? HTNOperatorStatus.Finished + : HTNOperatorStatus.Failed; + } + + if (!wieldable.Wielded) + return HTNOperatorStatus.Failed; + + return wieldableSystem.TryUnwield(weaponUid.Value, wieldable, owner) + ? HTNOperatorStatus.Finished + : HTNOperatorStatus.Failed; + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/UseItemInHandOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/UseItemInHandOperator.cs new file mode 100644 index 0000000000..3048e043b5 --- /dev/null +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Interactions/UseItemInHandOperator.cs @@ -0,0 +1,24 @@ +using Content.Shared.Hands.EntitySystems; + +namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Interactions; + +/// +/// Uses the item in the NPC's active hand. +/// +public sealed partial class UseItemInHandOperator : HTNOperator +{ + [Dependency] private readonly IEntityManager _entManager = default!; + + public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) + { + var owner = blackboard.GetValue(NPCBlackboard.Owner); + + if (!blackboard.TryGetValue(NPCBlackboard.ActiveHand, out var activeHand, _entManager)) + return HTNOperatorStatus.Failed; + + var handsSystem = _entManager.System(); + var success = handsSystem.TryUseItemInHand(owner, handName: activeHand); + + return success ? HTNOperatorStatus.Finished : HTNOperatorStatus.Failed; + } +} diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs index 9f4cc9d6aa..a75af8947d 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs @@ -92,7 +92,7 @@ public abstract partial class SharedGunSystem /// /// Opens then closes the bolt, or just closes it if currently open. /// - private void UseChambered(EntityUid uid, ChamberMagazineAmmoProviderComponent component, EntityUid? user = null) + public void UseChambered(EntityUid uid, ChamberMagazineAmmoProviderComponent component, EntityUid? user = null) { if (component.BoltClosed == false) { @@ -310,7 +310,7 @@ public abstract partial class SharedGunSystem return true; } - protected EntityUid? GetChamberEntity(EntityUid uid) + public EntityUid? GetChamberEntity(EntityUid uid) { if (!Containers.TryGetContainer(uid, ChamberSlot, out var container) || container is not ContainerSlot slot) diff --git a/Resources/Prototypes/NPCs/Combat/gun.yml b/Resources/Prototypes/NPCs/Combat/gun.yml index 2b5ccd9fa5..abecd18719 100644 --- a/Resources/Prototypes/NPCs/Combat/gun.yml +++ b/Resources/Prototypes/NPCs/Combat/gun.yml @@ -96,6 +96,29 @@ - type: htnCompound id: RangedCombatCompound branches: + # Close or rack the gun chamber if necessary + - preconditions: + - !type:ActiveHandComponentPrecondition + components: + - type: Gun + - !type:GunAmmoPrecondition + minPercent: 0.001 + - !type:NeedToRackBoltPrecondition + tasks: + - !type:HTNPrimitiveTask + operator: !type:RackBoltOperator + + # Wield gun + - preconditions: + - !type:ActiveHandComponentPrecondition + components: + - type: Gun + - !type:WieldedPrecondition + wielded: false + tasks: + - !type:HTNPrimitiveTask + operator: !type:WieldOperator + # Move to target and shoot them if ammo - preconditions: - !type:GunAmmoPrecondition diff --git a/Resources/Prototypes/NPCs/Combat/melee.yml b/Resources/Prototypes/NPCs/Combat/melee.yml index e25284bb88..c6fce02416 100644 --- a/Resources/Prototypes/NPCs/Combat/melee.yml +++ b/Resources/Prototypes/NPCs/Combat/melee.yml @@ -50,6 +50,34 @@ - !type:HTNCompoundTask task: PickupMeleeCompound + # Wield melee weapon + - preconditions: + - !type:ActiveHandComponentPrecondition + components: + - type: MeleeWeapon + damage: + types: + Blunt: 0 + - !type:WieldedPrecondition + wielded: false + tasks: + - !type:HTNPrimitiveTask + operator: !type:WieldOperator + + # Activate toggleable weapons + - preconditions: + - !type:ActiveHandComponentPrecondition + components: + - type: MeleeWeapon + damage: + types: + Blunt: 0 + - !type:ItemTogglePrecondition + activated: false + tasks: + - !type:HTNPrimitiveTask + operator: !type:UseItemInHandOperator + # Melee combat (unarmed or otherwise) - tasks: - !type:HTNCompoundTask From 63b69234a7168b0faf6dba05f8af0578e39e9169 Mon Sep 17 00:00:00 2001 From: PJBot Date: Tue, 21 Apr 2026 04:38:57 +0000 Subject: [PATCH 231/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f8bb00f157..f440643c60 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4046,3 +4039,15 @@ 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 From 0e34c450df105f95c0e0f9ccabccfa61b5eafe78 Mon Sep 17 00:00:00 2001 From: _Svist_ <148935188+Svist666s@users.noreply.github.com> Date: Tue, 21 Apr 2026 23:07:35 +0400 Subject: [PATCH 232/247] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20411=20(#3576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl b/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl index dab4c9a3da..4ec478e50e 100644 --- a/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl +++ b/Resources/Locale/ru-RU/corvax/guidebook/corporatelaw/articles.ftl @@ -281,6 +281,7 @@ guidebook-corporatelaw-411-note = - Битье стаканов в баре. - Броски скользких предметов под ноги с целью опрокидывания. - Осквернение стен, пола при помощи мелков. + - Распространение ложной информации о наличии угрозы безопасности для объекта. guidebook-corporatelaw-413-desc = [italic]Хищение или приобретение права на имущества общего пользования, личных вещей, имущества отделов станции, домашних животных путем обмана или злоупотребления доверием. Незаконное создание, приобретение, хранение или сбыт поддельных документов с печатями, денежных средств. Незаконное использование или прослушивание закрытых каналов отделов.[/italic] guidebook-corporatelaw-413-note = From 5b3f98c20b292d9e29e2870bd44675dc8ab3f6c3 Mon Sep 17 00:00:00 2001 From: Pok <113675512+Pok27@users.noreply.github.com> Date: Wed, 22 Apr 2026 01:14:33 +0300 Subject: [PATCH 233/247] =?UTF-8?q?[Wiki]=20=D0=A0=D0=B0=D0=B7=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20Actions=20(#3577)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/update-wiki.yml | 34 ++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index e40e102e44..0fbbf68dd6 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -33,13 +33,21 @@ jobs: - 'Content.Shared/**' - 'Content.Server/**' - 'Content.Client/**' - - 'Resources/**' + - 'Resources/Prototypes/**' - 'RobustToolbox/**' prototypes: - '.github/workflows/update-wiki.yml' - 'Resources/Prototypes/**' + textures: + - '.github/workflows/update-wiki.yml' + - 'Resources/Textures/**' + + locale: + - '.github/workflows/update-wiki.yml' + - 'Resources/Locale/**' + - name: Setup Submodule run: | git submodule update --init --recursive @@ -67,6 +75,30 @@ jobs: run: dotnet ./bin/Content.Server/Content.Server.dll --cvar autogen.destination_file=prototypes.json continue-on-error: true + - name: Upload loc to wiki + if: ${{ github.event_name == 'workflow_dispatch' || steps.changes.outputs.locale == 'true' }} + continue-on-error: true + uses: jtmullen/mediawiki-edit-action@v0.1.1 + with: + wiki_text_file: ./bin/Content.Server/data/loc.json + edit_summary: Update loc.json via GitHub Actions + page_name: "${{ secrets.WIKI_PAGE_ROOT }}/loc.json" + api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php + username: ${{ secrets.WIKI_BOT_USER }} + password: ${{ secrets.WIKI_BOT_PASS }} + + - name: Update meta license + if: ${{ github.event_name == 'workflow_dispatch' || steps.changes.outputs.textures == 'true' }} + continue-on-error: true + uses: jtmullen/mediawiki-edit-action@v0.1.1 + with: + wiki_text_file: ./bin/Content.Server/data/meta_license.json + edit_summary: Update meta_license.json via GitHub Actions + page_name: "${{ secrets.WIKI_PAGE_ROOT }}/meta_license.json" + api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php + username: ${{ secrets.WIKI_BOT_USER }} + password: ${{ secrets.WIKI_BOT_PASS }} + # Проходит по всем JSON-файлам в директории BASE и загружает каждый файл как страницу в MediaWiki. # Имя страницы формируется из относительного пути к файлу. - name: Upload JSON files to wiki From c32d52502b8350061a9571df554e6660ec044b1e Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Tue, 21 Apr 2026 20:44:10 -0400 Subject: [PATCH 234/247] Mail Fraud [Traitor Objective] (#43560) * save * cutting open your own letter check * typo * update and use API method * tweak text * generic counter system * update requires * review comments * api method * Update Content.Server/Objectives/Systems/ConditionCounterSystem.cs Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> * Update Content.Server/Objectives/Systems/ConditionCounterSystem.cs Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> * Update Content.Server/Objectives/Systems/ConditionCounterSystem.cs Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> * remove break --------- Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> --- .../Components/CounterConditionComponent.cs | 12 +++++ .../Components/MailFraudConditionComponent.cs | 12 +++++ .../Systems/ConditionCounterSystem.cs | 48 +++++++++++++++++++ .../Systems/MailFraudObjectiveSystem.cs | 38 +++++++++++++++ .../FingerprintReaderSystem.cs | 5 +- .../objectives/conditions/mail-fraud.ftl | 1 + .../Prototypes/Objectives/base_objectives.yml | 10 ++++ .../Prototypes/Objectives/objectiveGroups.yml | 1 + Resources/Prototypes/Objectives/traitor.yml | 18 +++++++ 9 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 Content.Server/Objectives/Components/CounterConditionComponent.cs create mode 100644 Content.Server/Objectives/Components/MailFraudConditionComponent.cs create mode 100644 Content.Server/Objectives/Systems/ConditionCounterSystem.cs create mode 100644 Content.Server/Objectives/Systems/MailFraudObjectiveSystem.cs create mode 100644 Resources/Locale/en-US/objectives/conditions/mail-fraud.ftl diff --git a/Content.Server/Objectives/Components/CounterConditionComponent.cs b/Content.Server/Objectives/Components/CounterConditionComponent.cs new file mode 100644 index 0000000000..c89c9aa9e9 --- /dev/null +++ b/Content.Server/Objectives/Components/CounterConditionComponent.cs @@ -0,0 +1,12 @@ +namespace Content.Server.Objectives.Components; + +/// +/// This is used as a generic counter for objectives. +/// Requires to function. +/// +[RegisterComponent] +public sealed partial class CounterConditionComponent : Component +{ + [DataField] + public int Count; +} diff --git a/Content.Server/Objectives/Components/MailFraudConditionComponent.cs b/Content.Server/Objectives/Components/MailFraudConditionComponent.cs new file mode 100644 index 0000000000..850b7f2f13 --- /dev/null +++ b/Content.Server/Objectives/Components/MailFraudConditionComponent.cs @@ -0,0 +1,12 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Objective condition that requires the player to cut into letters and packages addressed to others. +/// Requires to function. +/// Requires to function. +/// +[RegisterComponent] +public sealed partial class MailFraudConditionComponent : Component +{ + +} diff --git a/Content.Server/Objectives/Systems/ConditionCounterSystem.cs b/Content.Server/Objectives/Systems/ConditionCounterSystem.cs new file mode 100644 index 0000000000..1cf9318b53 --- /dev/null +++ b/Content.Server/Objectives/Systems/ConditionCounterSystem.cs @@ -0,0 +1,48 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Objectives.Components; +using JetBrains.Annotations; + +namespace Content.Server.Objectives.Systems; + +/// +/// This system handles returning the progress for CounterConditionComponents, +/// which simple increment for traitor NumberObjectives, e.g. cut into 12 envelopes. +/// +public sealed class CounterConditionSystem : EntitySystem +{ + [Dependency] private readonly NumberObjectiveSystem _number = default!; + [Dependency] private EntityQuery _compQuery = default!; + + /// + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnCounterGetProgress); + } + + private void OnCounterGetProgress(Entity ent, ref ObjectiveGetProgressEvent args) + { + args.Progress = GetProgress(ent, _number.GetTarget(ent.Owner)); + } + + private float GetProgress(Entity ent, int target) + { + // prevent divide-by-zero + if (target == 0) + return 1f; + + if (ent.Comp.Count >= target) + return 1f; + + return (float)ent.Comp.Count / target; + } + + [PublicAPI] + public void IncreaseCount(Entity objective) + { + if (_compQuery.Resolve(objective, ref objective.Comp)) + { + objective.Comp.Count++; + } + } +} diff --git a/Content.Server/Objectives/Systems/MailFraudObjectiveSystem.cs b/Content.Server/Objectives/Systems/MailFraudObjectiveSystem.cs new file mode 100644 index 0000000000..a82005fa57 --- /dev/null +++ b/Content.Server/Objectives/Systems/MailFraudObjectiveSystem.cs @@ -0,0 +1,38 @@ +using Content.Server.Mind; +using Content.Server.Objectives.Components; +using Content.Shared.Delivery; +using Content.Shared.FingerprintReader; + +namespace Content.Server.Objectives.Systems; + +public sealed partial class MailFraudObjectiveSystem : EntitySystem +{ + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly FingerprintReaderSystem _fingerprintReader = default!; + [Dependency] private readonly CounterConditionSystem _counterCondition = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnDeliveryOpened); + } + + private void OnDeliveryOpened(Entity ent, ref DeliveryOpenedEvent args) + { + if (!ent.Comp.WasPenalized) + return; //not fraud + + if (_fingerprintReader.IsAllowed(ent.Owner, args.User, out var _, showPopup: false, checkGloves: false)) + return; //cutting open your own letter + + if (!_mind.TryGetMind(args.User, out _, out var mind)) + return; + + foreach (var obj in mind.Objectives) + { + if (HasComp(obj)) + { + _counterCondition.IncreaseCount(obj); + } + } + } +} diff --git a/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs b/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs index 73b06cac9b..4ae08ed95b 100644 --- a/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs +++ b/Content.Shared/FingerprintReader/FingerprintReaderSystem.cs @@ -46,10 +46,11 @@ public sealed class FingerprintReaderSystem : EntitySystem /// User trying to gain access. /// Whether to display a popup with the reason you are not allowed to access this. /// The reason why access was denied. + /// Allows bypassing glove check regardless of the component's IgnoreGloves param. /// True if access was granted, otherwise false. // TODO: Remove showPopup, just keeping it here for backwards compatibility while I refactor mail [PublicAPI] - public bool IsAllowed(Entity target, EntityUid user, [NotNullWhen(false)] out string? denyReason, bool showPopup = true) + public bool IsAllowed(Entity target, EntityUid user, [NotNullWhen(false)] out string? denyReason, bool showPopup = true, bool checkGloves = true) { denyReason = null; if (!Resolve(target, ref target.Comp, false)) @@ -59,7 +60,7 @@ public sealed class FingerprintReaderSystem : EntitySystem return true; // Check for gloves first - if (!target.Comp.IgnoreGloves && TryGetBlockingGloves(user, out var gloves)) + if (checkGloves && !target.Comp.IgnoreGloves && TryGetBlockingGloves(user, out var gloves)) { denyReason = Loc.GetString("fingerprint-reader-fail-gloves", ("blocker", gloves)); diff --git a/Resources/Locale/en-US/objectives/conditions/mail-fraud.ftl b/Resources/Locale/en-US/objectives/conditions/mail-fraud.ftl new file mode 100644 index 0000000000..99d8aeaead --- /dev/null +++ b/Resources/Locale/en-US/objectives/conditions/mail-fraud.ftl @@ -0,0 +1 @@ +objective-condition-mail-fraud-title = Cut into {$count} letters or packages not addressed to you. diff --git a/Resources/Prototypes/Objectives/base_objectives.yml b/Resources/Prototypes/Objectives/base_objectives.yml index 1abbe74f3a..12204153c2 100644 --- a/Resources/Prototypes/Objectives/base_objectives.yml +++ b/Resources/Prototypes/Objectives/base_objectives.yml @@ -119,3 +119,13 @@ id: BaseFreeObjective components: - type: FreeObjective + +# requires the player to perform some action a certain number of times +- type: entity + abstract: true + parent: BaseObjective + id: BaseCounterObjective + description: We're interested in Nanotrasen's correspondence. Letter opener not provided. + components: + - type: CounterCondition + - type: NumberObjective # min and max to be set in the inheritor \ No newline at end of file diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index 9c524e57af..e4cee1239d 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -56,6 +56,7 @@ id: TraitorObjectiveGroupOther weights: HijackTradeStationObjective: 1 + MailFraudObjective: 1 #Thief groups diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index 6829084207..cf1d04559f 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -413,3 +413,21 @@ - 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 From 00390dd9d9ff89dd3db269176053f911ef824e42 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 22 Apr 2026 01:00:07 +0000 Subject: [PATCH 235/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f440643c60..8b3592ec77 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4051,3 +4044,10 @@ 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 From 8553b5df4a23f4040573095439a0e454a9b072a2 Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Wed, 22 Apr 2026 00:44:12 -0400 Subject: [PATCH 236/247] Fix ninja glove stun visuals (#43279) * Fix ninja stun visuals * do it by event instead --- Content.Shared/Damage/Systems/SharedStaminaSystem.cs | 3 +-- Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Damage/Systems/SharedStaminaSystem.cs b/Content.Shared/Damage/Systems/SharedStaminaSystem.cs index 450acdd81a..1832eeee14 100644 --- a/Content.Shared/Damage/Systems/SharedStaminaSystem.cs +++ b/Content.Shared/Damage/Systems/SharedStaminaSystem.cs @@ -397,8 +397,7 @@ public abstract partial class SharedStaminaSystem : EntitySystem component.Critical = true; component.StaminaDamage = component.CritThreshold; - if (StunSystem.TryUpdateParalyzeDuration(uid, component.StunTime)) - StunSystem.TrySeeingStars(uid); + StunSystem.TryUpdateParalyzeDuration(uid, component.StunTime); // Give them buffer before being able to be re-stunned component.NextUpdate = Timing.CurTime + component.StunTime + StamCritBufferTime; diff --git a/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs b/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs index 4d49621a8a..3ab1411428 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.Visualizer.cs @@ -10,6 +10,12 @@ public abstract partial class SharedStunSystem { SubscribeLocalEvent(OnStunMobStateChanged); SubscribeLocalEvent(OnSleepStateChanged); + SubscribeLocalEvent(OnStunned); + } + + private void OnStunned(Entity ent, ref StunnedEvent args) + { + TrySeeingStars(ent.Owner); } private bool GetStarsData(Entity entity) From 3fa062dbaf52550295be0c4827d9fc89b5eba7d4 Mon Sep 17 00:00:00 2001 From: PJBot Date: Wed, 22 Apr 2026 05:00:03 +0000 Subject: [PATCH 237/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 8b3592ec77..3df857b962 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4051,3 +4044,11 @@ 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 From 02eb3133171a5bd87cd455039811f3adcef2d999 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:45:39 +0200 Subject: [PATCH 238/247] HeatContainer codeownership (#43692) weh --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f32c3de5f9..2a4a458277 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -26,7 +26,7 @@ /Content.*/Forensics/ @ficcialfaint -/Content.*/Trigger/ @slarticodefast +/Content.*/HeatContainer/ @slarticodefast /Content.*/Stunnable/ @Princess-Cheeseballs /Content.*/Nutrition/ @Princess-Cheeseballs From 65e6e7aef474654fdc77c49fbd9fb870dd7bec64 Mon Sep 17 00:00:00 2001 From: Samuka <47865393+Samuka-C@users.noreply.github.com> Date: Wed, 22 Apr 2026 19:02:03 -0300 Subject: [PATCH 239/247] Tile gun starts filled (#43558) tile gun start full --- .../Entities/Objects/Specific/Robotics/borg_modules.yml | 2 +- Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index a10437d5b7..64311dcde6 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -1552,7 +1552,7 @@ - state: icon-xenoborg-tile - type: ItemBorgModule hands: - - item: WeaponTileGunEmpty + - item: WeaponTileGun - type: BorgModuleIcon icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-tile-module } diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml index a3fc4df7f4..402ad51ce7 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/tile_gun.yml @@ -39,7 +39,7 @@ components: - FloorTile capacity: 30 - proto: FloorTileItemSteel + proto: FloorTileItemXenoborg soundInsert: path: /Audio/Weapons/Guns/MagIn/tile_load.ogg - type: ContainerContainer From e228cd414c23c017429eda27a4c3b361cf87fa00 Mon Sep 17 00:00:00 2001 From: jkwookee <157201244+jkwookee@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:53:11 -0400 Subject: [PATCH 240/247] Fix immovable rods not destroying full sized grilles (#40714) * FINALLY FOUND IT OMG * a * a --- Resources/Prototypes/Entities/Structures/Walls/grille.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Walls/grille.yml b/Resources/Prototypes/Entities/Structures/Walls/grille.yml index 55b06f1e60..b20256ec7e 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/grille.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/grille.yml @@ -59,6 +59,8 @@ shape: !type:PhysShapeAabb bounds: "-0.5,-0.5,0.5,0.5" + mask: + - FullTileMask layer: - GlassLayer - type: Destructible From 42d33ff89936f3a92c7499ceb62728a89695e1ac Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 23 Apr 2026 03:08:30 +0000 Subject: [PATCH 241/247] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3df857b962..15e491ec4c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- 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. @@ -4052,3 +4045,10 @@ 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 From 527735f20d4e4041ca2acc81586d45753e94d3f9 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Wed, 22 Apr 2026 20:34:29 -0700 Subject: [PATCH 242/247] A couple more Max Cap fixes/rebalances (#43659) * max cap more fixes and rebalances * arbitrary * bugefix cleanup, rebalance --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Atmos/EntitySystems/MaxPressureVisualsSystem.cs | 4 ++-- Content.Server/Atmos/EntitySystems/GasTankSystem.cs | 12 +++--------- .../Components/GasMaxPressureHolderComponent.cs | 11 +++++++---- .../Atmos/Components/IGasMaxPressureHolder.cs | 4 ++-- .../Atmos/EntitySystems/GasMaxPressureSystem.cs | 9 +++++---- .../EntitySystems/SharedAtmosphereSystem.Gases.cs | 2 +- Resources/Prototypes/Atmospherics/gases.yml | 2 +- .../Prototypes/Catalog/Fills/Items/gas_tanks.yml | 5 +++-- .../Prototypes/Entities/Objects/Tools/jetpacks.yml | 5 ----- .../Structures/Storage/Canisters/gas_canisters.yml | 6 +++--- 10 files changed, 27 insertions(+), 33 deletions(-) diff --git a/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs b/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs index d92a7b6741..72eff2b409 100644 --- a/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs +++ b/Content.Client/Atmos/EntitySystems/MaxPressureVisualsSystem.cs @@ -46,10 +46,10 @@ public sealed class MaxPressureVisualsSystem : EntitySystem if (args.Sprite is not { } sprite) return; - if (!args.AppearanceData.TryGetValue(GasIntegrity.Integrity, out var obj) || obj is not int integrity) + 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 int maxIntegrity) + 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. diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index 74f4856d78..fba6c145ae 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -21,9 +21,9 @@ public sealed class GasTankSystem : SharedGasTankSystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly ThrowingSystem _throwing = default!; - private const float MinimumSoundValvePressure = 10.0f; + private const float MinimumSoundValvePressure = 21.3f; // Arbitrary number - private const float ReleaseArea = 0.0001f; // About 1cm^2 + private const float ReleaseArea = 0.0005f; // About 5cm^2 // A vector bias for throwing our gas tanks in radians. Averages about -43 degrees since the sprite is at a 45-degree angle. private static readonly Vector2 ThrowVector = new (-1.0f, -0.5f); @@ -106,7 +106,7 @@ public sealed class GasTankSystem : SharedGasTankSystem _atmosphereSystem.Merge(environment, removed); // If we wouldn't produce a sound, don't throw or play a sound. - if (deltaP < MinimumSoundValvePressure) + if (removed.Pressure < MinimumSoundValvePressure) return; Audio.PlayPvs(entity.Comp.ReleaseSound, entity); @@ -141,12 +141,6 @@ public sealed class GasTankSystem : SharedGasTankSystem return gasTank.Comp.Air.Remove(amount); } - public void AssumeAir(Entity ent, GasMixture giver) - { - _atmosphereSystem.Merge(ent.Comp.Air, giver); - CheckStatus(ent); - } - protected override void SafetyMeasures(Entity entity) { if (entity.Comp.ReleaseValveOpen) diff --git a/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs b/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs index 7c6ac9cfaf..573d3cbdf2 100644 --- a/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs +++ b/Content.Shared/Atmos/Components/GasMaxPressureHolderComponent.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Atmos.Components; /// public abstract partial class GasMaxPressureHolderComponent : Component, IGasMaxPressureHolder { - private const int DefaultIntegrity = 5; + private const float DefaultIntegrity = 3f; private const float DefaultOutputPressure = Atmospherics.OneAtmosphere; /// @@ -49,7 +49,10 @@ public abstract partial class GasMaxPressureHolderComponent : Component, IGasMax /// Sound made when air is leaked out of this device. /// [DataField] - public SoundSpecifier? ReleaseSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/spray.ogg"); + public SoundSpecifier? ReleaseSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/spray.ogg") + { + Params = AudioParams.Default.WithVolume(-5f), + }; /// /// The mixture of air contained in this device. @@ -73,8 +76,8 @@ public abstract partial class GasMaxPressureHolderComponent : Component, IGasMax public LocId? SafetyAlert { get; set; } = "gas-max-pressure-alert"; [DataField] - public int MaxIntegrity { get; set; } = DefaultIntegrity; + public float MaxIntegrity { get; set; } = DefaultIntegrity; [DataField] - public int Integrity { get; set; } = DefaultIntegrity; + public float Integrity { get; set; } = DefaultIntegrity; } diff --git a/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs b/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs index 88386a5c74..e05d80c96c 100644 --- a/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs +++ b/Content.Shared/Atmos/Components/IGasMaxPressureHolder.cs @@ -33,12 +33,12 @@ public interface IGasMaxPressureHolder : IGasMixtureHolder /// An overpressure is defined as pressure exceeding /// This determines the maximum value /// - int MaxIntegrity { get; set; } + 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 /// - int Integrity { get; set; } + float Integrity { get; set; } } diff --git a/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs b/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs index 68bfaa6a11..31e6a4f6c4 100644 --- a/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs +++ b/Content.Shared/Atmos/EntitySystems/GasMaxPressureSystem.cs @@ -38,7 +38,7 @@ public abstract class GasMaxPressureSystem : EntitySystem where T : IGasMaxPr 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)) + if (CheckStatus(entity, args.dt)) DeviceUpdated(entity, ref args); } @@ -89,8 +89,9 @@ public abstract class GasMaxPressureSystem : EntitySystem where T : IGasMaxPr /// 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) + protected bool CheckStatus(Entity entity, float dt) { var pressure = entity.Comp.Air.Pressure; @@ -117,13 +118,13 @@ public abstract class GasMaxPressureSystem : EntitySystem where T : IGasMaxPr if (pressure > entity.Comp.Overpressure) { IntegrityLost(entity); - entity.Comp.Integrity--; + 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++; + 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); } diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs index 5433ec6570..240711f048 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosphereSystem.Gases.cs @@ -421,7 +421,7 @@ public abstract partial class SharedAtmosphereSystem if (deltaP <= 0) return null; - return ReleaseGasAt(mixture, (float)GetFlowVolume(mixture, deltaP, area, dt), deltaP); + return ReleaseGasAt(mixture, (float)GetFlowVolume(mixture, deltaP, area, dt), mixture.Pressure); } /// diff --git a/Resources/Prototypes/Atmospherics/gases.yml b/Resources/Prototypes/Atmospherics/gases.yml index 2108b68e53..b217074ca8 100644 --- a/Resources/Prototypes/Atmospherics/gases.yml +++ b/Resources/Prototypes/Atmospherics/gases.yml @@ -53,7 +53,7 @@ abbreviation: gas-tritium-abbreviation molarHeatCapacity: 10 heatCapacityRatio: 1.3 - molarMass: 6 + molarMass: 3 gasOverlaySprite: sprite: /Textures/Effects/atmospherics.rsi state: tritium diff --git a/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml b/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml index cfc35c1589..f377d96db9 100644 --- a/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml +++ b/Resources/Prototypes/Catalog/Fills/Items/gas_tanks.yml @@ -225,8 +225,9 @@ air: volume: 0.66 moles: - Oxygen: 0.1805213564 - Tritium: 0.0902606782 + 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 diff --git a/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml b/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml index 1ba2d790fe..94dd306d8c 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jetpacks.yml @@ -241,11 +241,6 @@ sprite: Objects/Tanks/Jetpacks/security.rsi - type: Clothing sprite: Objects/Tanks/Jetpacks/security.rsi - - type: GasTank - maxIntegrity: 5 # You may ask why this microbalance? The answer is: Sec jetpacks sprite is based off regular jetpacks, and I'm too lazy to make their sprite data different. - integrity: 5 - - type: MaxPressureVisuals - steps: 5 #Filled security - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index 961e3a6487..7ffc594342 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -124,10 +124,10 @@ - type: MaxPressureVisuals integrityMask: mask-grey - type: GasCanister - maxIntegrity: 25 # 5 times stronger than a gas tank - integrity: 25 + maxIntegrity: 15 # 5 times stronger than a gas tank + integrity: 15 maxReleasePressure: 1013.25 - safetyPressure: 7599.375 # High enough that liquid tanks don't burst immediately + 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 From 3cbd977ad632dfd7ec90ae65ea81dfb680cbdbbe Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 26 Apr 2026 13:52:51 -0700 Subject: [PATCH 243/247] [STAGING] Fix AI Holopads from breaking the client. (#43654) evil Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Content.Client/Holopad/HolopadWindow.xaml.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Client/Holopad/HolopadWindow.xaml.cs b/Content.Client/Holopad/HolopadWindow.xaml.cs index 25982b901c..cb618484e2 100644 --- a/Content.Client/Holopad/HolopadWindow.xaml.cs +++ b/Content.Client/Holopad/HolopadWindow.xaml.cs @@ -173,9 +173,9 @@ public sealed partial class HolopadWindow : FancyWindow var callerId = _telephoneSystem.GetFormattedCallerIdForEntity(telephone.LastCallerId.Item1, telephone.LastCallerId.Item2, Color.LightGray, "Default", 11); var holoapdId = _telephoneSystem.GetFormattedDeviceIdForEntity(telephone.LastCallerId.Item3, Color.LightGray, "Default", 11); - CallerIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId)); - HolopadIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(holoapdId)); - LockOutIdText.SetMessage(FormattedMessage.FromMarkupOrThrow(callerId)); + CallerIdText.SetMessage(FormattedMessage.FromMarkupPermissive(callerId)); + HolopadIdText.SetMessage(FormattedMessage.FromMarkupPermissive(holoapdId)); + LockOutIdText.SetMessage(FormattedMessage.FromMarkupPermissive(callerId)); // Sort holopads alphabetically var holopadArray = holopads.ToArray(); From e20710b5de2d8c3e7a2a1a3005706e28588764c6 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:14:21 -0700 Subject: [PATCH 244/247] [STAGING] Revert #39173 (#43729) * revert * dependencies --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- .../Systems/RadiationSystem.GridCast.cs | 296 +++++++++--------- .../Radiation/Systems/RadiationSystem.cs | 116 +------ .../Components/RadiationSourceComponent.cs | 4 - .../Systems/SharedRadiationSystem.cs | 6 - 4 files changed, 162 insertions(+), 260 deletions(-) diff --git a/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs b/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs index 8bbfa7ad41..796c1e1fce 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs @@ -1,15 +1,11 @@ -using System.Buffers; -using System.Collections.Concurrent; -using System.Linq; using System.Numerics; using Content.Server.Radiation.Components; using Content.Server.Radiation.Events; using Content.Shared.Radiation.Components; using Content.Shared.Radiation.Systems; -using JetBrains.Annotations; +using Robust.Shared.Collections; using Robust.Shared.Map.Components; -using Robust.Shared.Physics; -using Robust.Shared.Threading; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Radiation.Systems; @@ -17,62 +13,113 @@ namespace Content.Server.Radiation.Systems; // main algorithm that fire radiation rays to target public partial class RadiationSystem { + private List> _grids = new(); + private readonly record struct SourceData( float Intensity, - float Slope, - float MaxRange, Entity Entity, Vector2 WorldPosition) { - public EntityUid Uid => Entity.Owner; + public EntityUid? GridUid => Entity.Comp2.GridUid; + public float Slope => Entity.Comp1.Slope; public TransformComponent Transform => Entity.Comp2; } private void UpdateGridcast() { + // should we save debug information into rays? + // if there is no debug sessions connected - just ignore it var debug = _debugSessions.Count > 0; - var stopwatch = new Robust.Shared.Timing.Stopwatch(); + + var stopwatch = new Stopwatch(); stopwatch.Start(); - var sourcesCount = _sourceDataMap.Count; - if (_activeReceivers.Count == 0 || sourcesCount == 0) + _sources.Clear(); + _sources.EnsureCapacity(Count()); + + var sources = EntityQueryEnumerator(); + var destinations = EntityQueryEnumerator(); + + while (sources.MoveNext(out var uid, out var source, out var xform)) { - RaiseLocalEvent(new RadiationSystemUpdatedEvent()); - return; + if (!source.Enabled) + continue; + + var worldPos = _transform.GetWorldPosition(xform); + + // Intensity is scaled by stack size. + var intensity = source.Intensity * _stack.GetCount(uid); + + // Apply rad modifier if the source is enclosed within a radiation blocking container + // Note that this also applies to receivers, and it doesn't bother to check if the container sits between them. + // I.e., a source & receiver in the same blocking container will get double-blocked, when no blocking should be applied. + intensity = GetAdjustedRadiationIntensity(uid, intensity); + + _sources.Add(new(intensity, (uid, source, xform), worldPos)); } - var results = new float[_activeReceivers.Count]; - var debugRays = debug ? new ConcurrentBag() : null; + var debugRays = debug ? new List() : null; + var receiversTotalRads = new ValueList<(Entity, float)>(); - var job = new RadiationJob + // TODO RADIATION Parallelize + // Would need to give receiversTotalRads a fixed size. + // Also the _grids list needs to be local to a job. (or better yet cached in SourceData) + // And I guess disable parallelization when debugging to make populating the debug List easier. + // Or just make it threadsafe? + while (destinations.MoveNext(out var destUid, out var dest, out var destTrs)) { - System = this, - SourceTree = _sourceTree, - SourceDataMap = _sourceDataMap, - Destinations = _activeReceivers, - Results = results, - DebugRays = debugRays, - Debug = debug - }; + var destWorld = _transform.GetWorldPosition(destTrs); - _parallel.ProcessNow(job, _activeReceivers.Count); - - for (var i = 0; i < _activeReceivers.Count; i++) - { - var uid = _activeReceivers[i]; - var rads = results[i]; - - if (_receiverQuery.TryComp(uid, out var receiver)) + var rads = 0f; + foreach (var source in _sources) { - receiver.CurrentRadiation = rads; - if (rads > 0) - IrradiateEntity(uid, rads, GridcastUpdateRate); + // send ray towards destination entity + if (Irradiate(source, destUid, destTrs, destWorld, debug) is not { } ray) + continue; + + // add rads to total rad exposure + if (ray.ReachedDestination) + rads += ray.Rads; + + if (!debug) + continue; + + debugRays!.Add(new DebugRadiationRay( + ray.MapId, + GetNetEntity(ray.SourceUid), + ray.Source, + GetNetEntity(ray.DestinationUid), + ray.Destination, + ray.Rads, + ray.Blockers ?? new()) + ); } + + // Apply modifier if the destination entity is hidden within a radiation blocking container + rads = GetAdjustedRadiationIntensity(destUid, rads); + + receiversTotalRads.Add(((destUid, dest), rads)); } - if (debugRays is not null) - UpdateGridcastDebugOverlay(stopwatch.Elapsed.TotalMilliseconds, sourcesCount, _activeReceivers.Count, debugRays.ToList()); + // update information for debug overlay + var elapsedTime = stopwatch.Elapsed.TotalMilliseconds; + var totalSources = _sources.Count; + var totalReceivers = receiversTotalRads.Count; + UpdateGridcastDebugOverlay(elapsedTime, totalSources, totalReceivers, debugRays); + // send rads to each entity + foreach (var (receiver, rads) in receiversTotalRads) + { + // update radiation value of receiver + // if no radiation rays reached target, that will set it to 0 + receiver.Comp.CurrentRadiation = rads; + + // also send an event with combination of total rad + if (rads > 0) + IrradiateEntity(receiver, rads, GridcastUpdateRate); + } + + // raise broadcast event that radiation system has updated RaiseLocalEvent(new RadiationSystemUpdatedEvent()); } @@ -80,28 +127,68 @@ public partial class RadiationSystem EntityUid destUid, TransformComponent destTrs, Vector2 destWorld, - bool saveVisitedTiles, - List> gridList) + bool saveVisitedTiles) { + // lets first check that source and destination on the same map + if (source.Transform.MapID != destTrs.MapID) + return null; + var mapId = destTrs.MapID; - var dist = (destWorld - source.WorldPosition).Length(); + + // get direction from rad source to destination and its distance + var dir = destWorld - source.WorldPosition; + var dist = dir.Length(); + + // check if receiver is too far away + if (dist > GridcastMaxDistance) + return null; + + // will it even reach destination considering distance penalty var rads = source.Intensity - source.Slope * dist; if (rads < MinIntensity) return null; + // create a new radiation ray from source to destination + // at first we assume that it doesn't hit any radiation blockers + // and has only distance penalty var ray = new RadiationRay(mapId, source.Entity, source.WorldPosition, destUid, destWorld, rads); - var box = Box2.FromTwoPoints(source.WorldPosition, destWorld); - gridList.Clear(); - _mapManager.FindGridsIntersecting(mapId, box, ref gridList, true); + // if source and destination on the same grid it's possible that + // between them can be another grid (ie. shuttle in center of donut station) + // however we can do simplification and ignore that case + if (GridcastSimplifiedSameGrid && destTrs.GridUid is { } gridUid && source.GridUid == gridUid) + { + if (!_gridQuery.TryGetComponent(gridUid, out var gridComponent)) + return ray; + return Gridcast((gridUid, gridComponent, Transform(gridUid)), ref ray, saveVisitedTiles, source.Transform, destTrs); + } - foreach (var grid in gridList) + // lets check how many grids are between source and destination + // do a box intersection test between target and destination + // it's not very precise, but really cheap + + // TODO RADIATION + // Consider caching this in SourceData? + // I.e., make the lookup for grids as large as the sources's max distance and store the result in SourceData. + // Avoids having to do a lookup per source*receiver. + var box = Box2.FromTwoPoints(source.WorldPosition, destWorld); + _grids.Clear(); + _mapManager.FindGridsIntersecting(mapId, box, ref _grids, true); + + // gridcast through each grid and try to hit some radiation blockers + // the ray will be updated with each grid that has some blockers + foreach (var grid in _grids) { ray = Gridcast((grid.Owner, grid.Comp, Transform(grid)), ref ray, saveVisitedTiles, source.Transform, destTrs); + + // looks like last grid blocked all radiation + // we can return right now if (ray.Rads <= 0) return ray; } + _grids.Clear(); + return ray; } @@ -113,12 +200,19 @@ public partial class RadiationSystem TransformComponent destTrs) { var blockers = saveVisitedTiles ? new List<(Vector2i, float)>() : null; + + // if grid doesn't have resistance map just apply distance penalty var gridUid = grid.Owner; if (!_resistanceQuery.TryGetComponent(gridUid, out var resistance)) return ray; - var resistanceMap = resistance.ResistancePerTile; + // get coordinate of source and destination in grid coordinates + + // TODO Grid overlap. This currently assumes the grid is always parented directly to the map (local matrix == world matrix). + // If ever grids are allowed to overlap, this might no longer be true. In that case, this should precompute and cache + // inverse world matrices. + Vector2 srcLocal = sourceTrs.ParentUid == grid.Owner ? sourceTrs.LocalPosition : Vector2.Transform(ray.Source, grid.Comp2.InvLocalMatrix); @@ -127,20 +221,28 @@ public partial class RadiationSystem ? destTrs.LocalPosition : Vector2.Transform(ray.Destination, grid.Comp2.InvLocalMatrix); - Vector2i sourceGrid = new((int)Math.Floor(srcLocal.X / grid.Comp1.TileSize), (int)Math.Floor(srcLocal.Y / grid.Comp1.TileSize)); - Vector2i destGrid = new((int)Math.Floor(dstLocal.X / grid.Comp1.TileSize), (int)Math.Floor(dstLocal.Y / grid.Comp1.TileSize)); + Vector2i sourceGrid = new( + (int)Math.Floor(srcLocal.X / grid.Comp1.TileSize), + (int)Math.Floor(srcLocal.Y / grid.Comp1.TileSize)); + Vector2i destGrid = new( + (int)Math.Floor(dstLocal.X / grid.Comp1.TileSize), + (int)Math.Floor(dstLocal.Y / grid.Comp1.TileSize)); + + // iterate tiles in grid line from source to destination var line = new GridLineEnumerator(sourceGrid, destGrid); while (line.MoveNext()) { var point = line.Current; if (!resistanceMap.TryGetValue(point, out var resData)) continue; - ray.Rads -= resData; - if (saveVisitedTiles && blockers is not null) - blockers.Add((point, ray.Rads)); + // save data for debug + if (saveVisitedTiles) + blockers!.Add((point, ray.Rads)); + + // no intensity left after blocker if (ray.Rads <= MinIntensity) { ray.Rads = 0; @@ -148,11 +250,13 @@ public partial class RadiationSystem } } - if (blockers is null || blockers.Count == 0) + if (!saveVisitedTiles || blockers!.Count <= 0) return ray; + // save data for debug if needed ray.Blockers ??= new(); ray.Blockers.Add(GetNetEntity(gridUid), blockers); + return ray; } @@ -187,92 +291,4 @@ public partial class RadiationSystem return rads; } - - [UsedImplicitly] - private readonly record struct RadiationJob : IParallelRobustJob - { - public int BatchSize => 5; - public required RadiationSystem System { get; init; } - public required B2DynamicTree SourceTree { get; init; } - public required Dictionary SourceDataMap { get; init; } - public required List Destinations { get; init; } - public required float[] Results { get; init; } - public required ConcurrentBag? DebugRays { get; init; } - public required bool Debug { get; init; } - - public void Execute(int index) - { - var destUid = Destinations[index]; - if (System.Deleted(destUid) || !System.TryComp(destUid, out TransformComponent? destTrs)) - { - Results[index] = 0; - return; - } - - var nearbySourcesArray = ArrayPool.Shared.Rent(256); - - var gridList = new List>(8); - - try - { - var destWorld = System._transform.GetWorldPosition(destTrs); - var rads = 0f; - var destMapId = destTrs.MapID; - - var queryAabb = new Box2(destWorld, destWorld); - - var state = (nearbySourcesArray, 0, SourceTree); - SourceTree.Query(ref state, - static (ref (EntityUid[] arr, int count, B2DynamicTree tree) tuple, - DynamicTree.Proxy proxy) => - { - if (tuple.count >= tuple.arr.Length) - return true; - - var uid = tuple.tree.GetUserData(proxy); - tuple.arr[tuple.count++] = uid; - return true; - }, - in queryAabb); - - var nearbySourcesSpan = nearbySourcesArray.AsSpan(0, state.Item2); - - foreach (var sourceUid in nearbySourcesSpan) - { - if (!SourceDataMap.TryGetValue(sourceUid, out var source) - || source.Transform.MapID != destMapId) - continue; - var delta = source.WorldPosition - destWorld; - if (delta.LengthSquared() > source.MaxRange * source.MaxRange) - continue; - var dist = delta.Length(); - var radsAfterDist = source.Intensity - source.Slope * dist; - if (radsAfterDist < System.MinIntensity) - continue; - if (System.Irradiate(source, destUid, destTrs, destWorld, Debug, gridList) is not { } ray) - continue; - - if (ray.ReachedDestination) - rads += ray.Rads; - - DebugRays?.Add(new DebugRadiationRay( - ray.MapId, - System.GetNetEntity(ray.SourceUid), - ray.Source, - System.GetNetEntity(ray.DestinationUid), - ray.Destination, - ray.Rads, - ray.Blockers ?? new Dictionary>()) - ); - } - - rads = System.GetAdjustedRadiationIntensity(destUid, rads); - Results[index] = rads; - } - finally - { - ArrayPool.Shared.Return(nearbySourcesArray); - } - } - } } diff --git a/Content.Server/Radiation/Systems/RadiationSystem.cs b/Content.Server/Radiation/Systems/RadiationSystem.cs index fd1a60710d..3fbc7e0d28 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.cs @@ -1,13 +1,11 @@ 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; -using Robust.Shared.Physics; -using Robust.Shared.Threading; -using System.Numerics; -using Content.Shared.Radiation.Systems; +using Robust.Shared.Map.Components; namespace Content.Server.Radiation.Systems; @@ -18,118 +16,19 @@ public sealed partial class RadiationSystem : SharedRadiationSystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] private readonly SharedMapSystem _maps = default!; - [Dependency] private readonly IParallelManager _parallel = default!; - [Dependency] private readonly EntityQuery _receiverQuery = default!; - [Dependency] private EntityQuery _blockerQuery = default; - [Dependency] private EntityQuery _resistanceQuery = default; - - private readonly B2DynamicTree _sourceTree = new(); - private readonly Dictionary _sourceDataMap = new(); - private readonly List _activeReceivers = new(); + [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(); public override void Initialize() { base.Initialize(); SubscribeCvars(); InitRadBlocking(); - - SubscribeLocalEvent(OnSourceStartup); - SubscribeLocalEvent(OnSourceShutdown); - SubscribeLocalEvent(OnSourceMove); - SubscribeLocalEvent(OnSourceStackChanged); - - SubscribeLocalEvent(OnReceiverStartup); - SubscribeLocalEvent(OnReceiverShutdown); - } - - private void OnSourceStartup(Entity entity, ref ComponentStartup args) - { - UpdateSource(entity); - } - - private void OnSourceShutdown(EntityUid uid, RadiationSourceComponent component, ComponentShutdown args) - { - if (component.Proxy != DynamicTree.Proxy.Free) - { - _sourceTree.DestroyProxy(component.Proxy); - component.Proxy = DynamicTree.Proxy.Free; - } - _sourceDataMap.Remove(uid); - } - - private void OnSourceMove(Entity entity, ref MoveEvent args) - { - if (args.NewPosition.EntityId == args.OldPosition.EntityId && - args.NewPosition.Position.EqualsApprox(args.OldPosition.Position)) - return; - - UpdateSource(entity); - } - - private void OnSourceStackChanged(Entity entity, ref StackCountChangedEvent args) - { - UpdateSource(entity); - } - - private void OnReceiverStartup(EntityUid uid, RadiationReceiverComponent component, ComponentStartup args) - { - _activeReceivers.Add(uid); - } - - private void OnReceiverShutdown(EntityUid uid, RadiationReceiverComponent component, ComponentShutdown args) - { - _activeReceivers.Remove(uid); - } - - protected override void UpdateSource(Entity entity) - { - var (uid, component) = entity; - var xform = Transform(uid); - - if (!component.Enabled || Terminating(uid)) - { - if (component.Proxy != DynamicTree.Proxy.Free) - { - _sourceTree.DestroyProxy(component.Proxy); - component.Proxy = DynamicTree.Proxy.Free; - } - _sourceDataMap.Remove(uid); - return; - } - - var worldPos = _transform.GetWorldPosition(xform); - var intensity = component.Intensity * _stack.GetCount(uid); - intensity = GetAdjustedRadiationIntensity(uid, intensity); - - if (intensity <= 0) - { - if (component.Proxy != DynamicTree.Proxy.Free) - { - _sourceTree.DestroyProxy(component.Proxy); - component.Proxy = DynamicTree.Proxy.Free; - } - _sourceDataMap.Remove(uid); - return; - } - - // Avoid division by 0 - var maxRange = component.Slope >= float.Epsilon ? intensity / component.Slope : GridcastMaxDistance; - maxRange = Math.Min(maxRange, GridcastMaxDistance); - - _sourceDataMap[uid] = new SourceData(intensity, component.Slope, maxRange, (uid, component, xform), worldPos); - var aabb = Box2.CenteredAround(worldPos, new Vector2(maxRange * 2, maxRange * 2)); - - if (component.Proxy != DynamicTree.Proxy.Free) - { - _sourceTree.MoveProxy(component.Proxy, in aabb); - } - else - { - component.Proxy = _sourceTree.CreateProxy(in aabb, uint.MaxValue, uid); - } } public override void Update(float frameTime) @@ -155,11 +54,8 @@ public sealed partial class RadiationSystem : SharedRadiationSystem { if (!Resolve(entity, ref entity.Comp, false)) return; - if (entity.Comp.Enabled == val) - return; entity.Comp.Enabled = val; - UpdateSource((entity.Owner, entity.Comp)); } /// diff --git a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs index 367d38309f..51979f1c02 100644 --- a/Content.Shared/Radiation/Components/RadiationSourceComponent.cs +++ b/Content.Shared/Radiation/Components/RadiationSourceComponent.cs @@ -1,5 +1,4 @@ using Content.Shared.Radiation.Systems; -using Robust.Shared.Physics; namespace Content.Shared.Radiation.Components; @@ -30,7 +29,4 @@ public sealed partial class RadiationSourceComponent : Component [DataField, ViewVariables(VVAccess.ReadWrite)] public bool Enabled = true; - - [ViewVariables] - public DynamicTree.Proxy Proxy = DynamicTree.Proxy.Free; } diff --git a/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs index 78e061b9c3..a431ffa4fd 100644 --- a/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs +++ b/Content.Shared/Radiation/Systems/SharedRadiationSystem.cs @@ -17,11 +17,5 @@ public abstract partial class SharedRadiationSystem : EntitySystem return; entity.Comp.Intensity = intensity; - UpdateSource((entity, entity.Comp)); } - - /// - /// Updates the radiation source cache. Does nothing on client, see server! - /// - protected virtual void UpdateSource(Entity entity) { } } From 1ca6aac7f6ebbb98ebed7060a699d19453cbde63 Mon Sep 17 00:00:00 2001 From: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Date: Sun, 26 Apr 2026 14:26:14 -0700 Subject: [PATCH 245/247] [STAGING] Heat Distortion Shader gamma fix for compatibility mode (#43732) fix Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> --- Resources/Textures/Shaders/heatBlur.swsl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Resources/Textures/Shaders/heatBlur.swsl b/Resources/Textures/Shaders/heatBlur.swsl index 269a74b3f9..0f7d2400e3 100644 --- a/Resources/Textures/Shaders/heatBlur.swsl +++ b/Resources/Textures/Shaders/heatBlur.swsl @@ -15,20 +15,20 @@ void fragment() // 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 + noiseVal2 - 0.5 ); - highp float heatStrength = texture2D(TEXTURE, UV).r; - + 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 = texture2D(SCREEN_TEXTURE, distortedUV); + + COLOR = zTextureSpec(SCREEN_TEXTURE, distortedUV); } From 21559ad7b42d0af7171ba04f105578e0e3ceff8a Mon Sep 17 00:00:00 2001 From: _Svist_ <148935188+Svist666s@users.noreply.github.com> Date: Tue, 28 Apr 2026 16:25:58 +0400 Subject: [PATCH 246/247] =?UTF-8?q?=D0=9B=D0=BE=D0=BA=D0=B0=D0=BB=D1=8C=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=BB=D0=B6=D0=BD=D0=BE=D1=81=D1=82=D0=B5=D0=B9=20?= =?UTF-8?q?(#3584)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Resources/Locale/ru-RU/job/job-names.ftl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Locale/ru-RU/job/job-names.ftl b/Resources/Locale/ru-RU/job/job-names.ftl index 531adafe4e..d8a51ebb77 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 = инженер From f2442c344a9b99a6a4db671ff8166a637bce38f0 Mon Sep 17 00:00:00 2001 From: MureixloI <132683811+MureixloI@users.noreply.github.com> Date: Tue, 28 Apr 2026 15:49:16 +0300 Subject: [PATCH 247/247] Fix canister's new states & fix assembly and panel_open for airlocks (#3582) --- .../Airlocks/Glass/firelock.rsi/assembly.png | Bin 1219 -> 892 bytes .../Doors/Airlocks/Glass/glass.rsi/meta.json | 2 +- .../Airlocks/Glass/glass.rsi/panel_open.png | Bin 269 -> 197 bytes .../canister.rsi/integrity-unshaded-0.png | Bin 5142 -> 170 bytes .../canister.rsi/integrity-unshaded-1.png | Bin 5289 -> 207 bytes .../canister.rsi/integrity-unshaded-2.png | Bin 5630 -> 283 bytes .../canister.rsi/integrity-unshaded-3.png | Bin 6042 -> 335 bytes .../canister.rsi/integrity-unshaded-4.png | Bin 6147 -> 350 bytes .../Storage/canister.rsi/mask-black.png | Bin 5792 -> 860 bytes .../Storage/canister.rsi/mask-blue.png | Bin 6943 -> 1013 bytes .../Storage/canister.rsi/mask-darkblue.png | Bin 6849 -> 870 bytes .../Storage/canister.rsi/mask-frezon.png | Bin 7186 -> 881 bytes .../Storage/canister.rsi/mask-green.png | Bin 7691 -> 858 bytes .../Storage/canister.rsi/mask-greenys.png | Bin 7956 -> 872 bytes .../Storage/canister.rsi/mask-grey.png | Bin 5981 -> 902 bytes .../Storage/canister.rsi/mask-orange.png | Bin 7218 -> 885 bytes .../Storage/canister.rsi/mask-red.png | Bin 7137 -> 952 bytes .../Storage/canister.rsi/mask-redws.png | Bin 7195 -> 1142 bytes .../Storage/canister.rsi/mask-water_vapor.png | Bin 6443 -> 1034 bytes .../Storage/canister.rsi/mask-yellow.png | Bin 7527 -> 1152 bytes .../Structures/Storage/canister.rsi/meta.json | 2 +- 21 files changed, 2 insertions(+), 2 deletions(-) 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 0b14b77aec5355875be80893fcc0065ffea6825b..3cc26d19ff4610aaaed33d8715d54ef06fe6c0ec 100644 GIT binary patch delta 857 zcmV-f1E&1L3H%0-Fn_ym_m{Vle@bOeV1`i{qUW{7qb+=zqj83{2AmK&vD_a!h4S zvtWG)zIvmUGea~$Ks{-d`2=BhX2!Fa1xNQbOZ!?SD*IcjQ99eO5@MY=$x#S2Grm zpZwtZ#75^GXfLR2h`_0AN%1?#t|ce|*Ghh69Qc=D>~<4i11O1{8!{qb%TESNh@pxPm3I!sO$Z{RXhw_c!uHy3Q0NoAahi<$&p!)x8 zqyQJyCx1|b@gE6z+CWwx*FP8F6{2`C&Ol~TR8}e(a-+Jm*d$W+ioQb`OE%J#Sh#}iHlLemQOUpBEN zR`LeQ0vy_B1HqNHrB#p{)y3n<;G@_@Xxh=K0e=)X2~KpwgE!8@2Q4?x&p#ipigIge z#ke(bwBynl*Dh`nFtsG$+}(CAhrT4w;JS|w{DC0?|ai}t_=@Ey*=S-!`~Bnb->@J{cY6M6Cz37YQf(V zDpILbN?4Xfe^1U))Zz5fK|A4h;qHLal+&XRjwdh-10@!V3HST3zbD73?60-ozlx!Z j=8>DG=?LH-exCmX`3P=(1t^o200000NkvXXu0mjf`o^Hz delta 1187 zcmV;U1YG<42Ez%EFn<69XF*Lt006O%3;baP00009a7bBm000id000id0mpBsWB>pJ zSV=@dR9HvFmrH0=R~W~?YsTo!WUfv!b@FhEF4BM+2Ps9mC>XPdkA*0)3sdZ>SxCf{ zVxhPa4B0lgu!~Y5-B=XC7DY?~U6de2Y@(Yi6o;sZF{AUy)PFSLdj8*>bLPyQJCB0F zAKaXC&+Grc-+APW)!EtUlAx=ri)LnKsDIZqUe}D#>0Q*((LqyFQ~b^bOmSy&J2i#s zD4VmmLC|J@G-L%it(Eg`R_2Ju!Z?fRRZ8uBQe?vF>+5rs_|u6N+HKoxzy#u7&dtfU z+W0FPWfSIyz<qNHv;!S0B2^@tQU;_xh6Xthc5LQ3&N`I-E9^f|xxoAErWv2jPJH52T zi|OIXL&xd^_4{iTkF;z}pbz?D425@b178D}H?{4sk}=Rdv8YnH)%_UUOexr$fH^Zo zTT>is3FBcGcnRQ7GV4+*SsbQwBUcB92TzW`03NJp5?>{&E8-PbV{w55nt&sK73p0A z_&Rj-Mt`D~2W^28=SY=-BLF6D70-`j*Ntft!acs|G!^F44|BVj;uuR56-7^NLn|JS zyUApd8XFsF-`ltJDx0OANQAC94mVJ!ue$c6@`-qSyO=70ikxB~m7;qK3-n1aNE4ej zQ6`h2a5!8r5W5tl__>reP!0@o?adqCL*w}7&3|sSzA*4`*rCMt*@A(^#YI|wKS2jR zZ>P(%vla%DeM%@NuBg`cRC~L7`|nx$?~52UH8l~zKr9w>KXb`NrEI%qKm%IPgtk5h zcF-P~TYF3`ZQwCIkh$tyYHM@-9$8%#Q%f6Ik&C<9K&TIkE#~*s(if@H0(udCI@HL! zAb;lO$8f~O(sS(KC~V?yMy3!a_N@FLU(LZJ|!1Ihq6H95&ChiQ44Mtghd#P~R2 z8;o{$Q@_Z2@heA}X#nRyI-O<%JobfyFX-~X3^!09q!7|`BvE^CHk#D77S|I-0;lr{d5BC#~Hv*vQg`^z07x~bc{E8{4ucyAdcV$VQN)b?K1J9k( z%Ywq30=u=fB-AFDI%QHtpc2|ZDk(D(gl%bhnq0Yhbr?-?k1y++pA&CQ&uI)A?@ zQ1CV!-1~@M9{e6H+J-ISU$P1x1}PUxv(B%b+JXSvaNE9@d=Va*+*xekyDR1A>O(Tz zU#AVM!_OmR6j5=d!h?h9r-1-Js?1xCyjka0?rjg0IJAp?oFWoG+-#;L5wiI|^I_Z8 zTex7Dz|^{6HXM1n5}GBBv9~JEWIQ7Nm>FBM11+LeR$n^eNR5dO^y2$PyY{zopr07z^^E&u=k delta 241 zcmVzxk#`rxOwM;0aY=)Jj-w@0gVX&bFERoKi*QxK02UwPOOybFI^`H1LxubXsCQd r3>e?+&khkGzRCUnC1d~t7!W!yYK1hNsBEdb00000NkvXXu0mjf#~5Jr diff --git a/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-0.png b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-0.png index 43a964f678e60f8d35fc8687de6cb1510710501d..ee5e372ef4455f8d9af6ea049c0b234f5ce2ebcb 100644 GIT binary patch delta 143 zcmV;A0C4}7D5?RFBYyw^b5ch_0Itp)=>Px#WJyFpR9J=WlR*lAFbo8v_*x(2chi#) z1leuyBAvN5nQluG0RRB2v#EkBBAvPy@)W&$Q~Vjf36Kr9)id)0Ga>9+CqNyd6!Pb} x9NxnSaEq$gd7-sAZh$IOmv>hd000(c0Dq7U4}SGG{r~^~00>D%PDHLkV1k_NH*f#| literal 5142 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

6Clqyl7esX!=#0cE@l24J;N zfI_L;dPCOidQ3Ii*y=1FZ@gt^W})X%`Q{m(vrha#q^42`x{x#iWJli z_bfHt^Z>Kgp5GiA>TI=Aa%NdWetl1O=YH<|Yl`N|EpPS>^o0S72~H6|pSnAkHSlV( zEITv%oK;X~B|dy=sOCwlDn-)t$CLr}y_7aEtt-^;bkas!m%YJ%^;*?yo+acKg`a*lyUCV4V`HrU?CY04v3 z=D(a#e66!~{Ym0I*TJiop0GU+TCm%}zCer0Te&@qg4Tidg77Jo{hIfU!KW00+=5Q@ z%6;zwvI=vp-Wmi|blXIRCO4)`JDR_dcGr3t!^*_vs+;Uu%S1h_zx(l)-NlK|7Ef{z zv?Wg-SHRxj;Nn6#umGn=xqHd)%%AAYy8fy|`wx{D6_#I^o>^|v z*qkk0N%50OfL&NfGUt)`FY+mMSx>i3>#o_wDoR_F<4`HP#9HJWdd?{hZd%CNel%Fz z=F%Egy!Ys-+rjG#>zxkVSadUZbxb8QYU|_7d$toQEo?kq?!;fdzRdSuiI@$7L&PgB ztD?S3Z0`8x-=&vttXR1^@kmBMTl^L5YSlfliuuEI!z&KKi;stv4SV;d{#84q-*O|v z#6v&@LzVAcz3IY2}#Q?8mIllOiFTSaJ{{UJ(XtsyRo@vtV`re(-6c0p2&#WtPRUYy2c?~7DE;;qC;rTc- z&(ODXhrnm2^COr$FL`AyyFWZoTv}UYm>T>r=jp3!d2gor)s!k-dmQub{5B7+_G~$x zK~#lzxR||glpIaOv65%97c28qcccbQjah5jR28z`FXGCIwy|6gaq^&Z)u-a553Z_yAy3*Tk&^E`O3W=_a`dH3u3>eiala~3}}Tk0QkG^tNV z_dB76iLrXY&(WL8^^Y76d1^jT(@1VR`C{v9qs%l@#k-4r8KJ+QsBy@>b7u!3lhgCt zo<8u;$lYdHdp59sd_UBo7jH4Yn0UwfptCB{{PC|>(XG`hWCjng=Csr)+nTLc6b^bX zsNa3^e%o6YBb%uuvtzAT7|hiGxze}2l2fs&yi4-^F-_bVnOEoarxtdDDw_@(4MRP* z(C!uIo8puC7cHd9iu&{e_TgnyF5gDqvHb3qzjcAt!D%MOHv3Zd9^4&2F?Q(4oQlf9T$u-zE zEH^e*W<9=o7BH;WQS=OBzY*trGVRX?6&5d^GoH8Wd6iubDvLq~P_Fnnc5t!h=f2j| zWLcuqxPIKjhZpJTm8LZj)HoND<6=|-&xv2+9#J|~J2XRYVF36{ro%nv@(n-FPR}^lr>pW2tzr8H< zc-d>lfazv^v3+gqfkMAXV|EeX-vv4Xk%P zGJL=J{=EYieq_^}rx~Jg@NXN_yzHhB(Lf<;}_4SPzDFXq+u3w4;Y&B+}l*-1(?g-D?L7+}y?&*k2BR zqgF+zr0)+2-@F+opvns_HyT7DyH1=C*GTo|hs%aGj%h4S!J%OGA1 ztOU4llu*LJy!iDz1`zTYn3WU`o+D$z(L%ojIUJaT>a=w7R+}mdq z0%D-`Dy)ixmCB=V1b26L9G-|H5N3rcx;QMAm1%X?t|xjCBMt|A_mU_J`PY%7_++L-&^QVzutEy%`wo_;kLM zC*;$0uVfO1OQh29AeT>a1Ia`Z5p?5n@gSd1Ci4h93XMi1je%lI6iP_KgSAiyI97<@ zPz78bi4Vgdk4WHwWFAofa!KwKkU}89GQVNFU3M3b` zs02KgG~$GIV(3UX2(^$lQxO221riONDTg7YR30RiiWnHJQ-Id;V>bs`P<%)Uc|%GV z0mT!kbUcYppayC0ZgesU#M9{bG5S)zP!RvWw6)6z@EDnNKcNB{KVByqSyO@Vx{=mM zQzXyCxK%q!{A1P#c3EKMf5oD!EUK}5l9 z?}b4jwY|+(jH>`kRT9tP+*3tI===^wvHg|u+;t;iDI*sc$auZ Gru+v$l={p7 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-2.png b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-2.png index e3ec17679cf6b9962aaee054cd35ebd40a102bc8..6791d3b81347d64ac5e0016e96bf23b096cf2e72 100644 GIT binary patch delta 256 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 bGEZ#;y@rOQS?*a1hmP=ao5w10;jH}+9|xfW diff --git a/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-3.png b/Resources/Textures/Structures/Storage/canister.rsi/integrity-unshaded-3.png index bdadc8c0bd5a4b301e12a3f1dafef5d45e0fa0ee..30e76da9faecc28844791e94900a5d48645bf268 100644 GIT binary patch delta 309 zcmV-50m}ZGFV6yyBYyw^b5ch_0Itp)=>Px$2}wjjR9J=Wmaz?jKoo{QjErF69BL?S z9l{Bmf`$o9oPZ&0EeY7TgoPteJPSM^sN6e<8k74P67KH(-@StXcsw4D$3h+J`}kmgF7IXL}VZKuK#%l{zE`+XXj(ku2FZuMZ#t7w101~NK2N)umqiN6B{U-KGq;7S1pwL-8uF~V+62_Ep(&`Dr=5%DE!aWOK8C*H-7eX7kER28RE4|j zN*j*m8R*K`L@rDhR+(FECy94s2M{wp=0|3fglq86KgXUsJyCO_IXq=~00000NkvXX Hu0mjfZF7P? literal 6042 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 Wj-ZENB4SYh0000`NBC6%I0l0qRBZMGIm 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 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-black.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-black.png index 5c7ec87a25180910cfcbced1ab91c05bc7d92a68..e44e4191e077e1e7f1a0135ec02d1744347c4448 100644 GIT binary patch delta 838 zcmV-M1G)U5E!+l>BYy)UNkl|3iOA@`G~lAlUYxn2<|QHc=?))?EnQbwcCC9&{$_symZ3(0;JsI&bEE@6G$( zTi_pKN!2j3wLRj$b&brm@q8wJ-`-p)ovmb)+1egCeMiMqz<)>@N+jN?9Om>LP@2Z& z<+mk-vJnJ#xU=(VWLaG$qDllfxZ}fvJ-kmOrq+JCE~>y>Vy{Nv@L&%BaPN7z_dEbV z>!LHW-e_GbpQ%&>@whnx07}zS|65iUl&0bN47x_dJ5T>$U`x+`qe8$)8VlDvpTX%n zQwosn2}1b_PJfP%MB0PS^SfCvmI5I$v@QTjH&DobK(*FI14%`$+`efJkW<}z4WUdHdoN3p)Prio`&*Ris^jB2fk2ljwiEbb47gRfEND_?=G z5makU z^c|E+`$C$NLM#sk_SuUFU=q30cc%Kk({~~x4DOL-LMlVk9H-jz85kR)RN7x?dvbgP z#)d+Yd~Xz_86YGEO4Aj~>YnNvfoUdia(uL~25j5qv!g;t?C%NSDUyokvs2G!ux)oH zI@@+dM2|PkL}lXWG~D?xzI)+#b3LKdjHCo|*%Yd^rXT?Tlx|e! z*)lf7=K7cbm#nM^r3C8r21=!UG+Nj2d?tv@tASO0>{!=`kgN=m(<-yIUB*OqM$(v) z;=CH*-~);eq}!XTg?%7WhCzqAM&S7jmeqylGk*{gn|dhSfN3VgXr6b*vk3&jqbe2) zGeZcI`?qZuxom1?G{2aDWpzc%)in}1;|YnuG!tSLU{b)cZ$oJY+MOPX#ey`da0ZIS z0@|IP?0B^+2u8IC;S8uaml7BZ>@%)oUIU9TuLhjHgQ!U;B@hOsY1HctSk~Q)40#5E zaz9jo>Q`}@7a$)<;X%VQ@SNzC3E=6|Y^EUv5xgc9=g%S#Cb9^sj{h6{0V>>=)XK1> Q?*IS*07*qoM6N<$g1dE*fB*mh literal 5792 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 rJmyobu)}IVUG~xdMqt511 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-blue.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-blue.png index cd496dfc27bb143e429ef92d8dfe919951f3df5c..d3bc54f284a18ec00f130617ef9bc52c67f59ddc 100644 GIT binary patch delta 992 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 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-darkblue.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-darkblue.png index 69549c4060f84e4c52c4794ed6f1c5d4986fdf4f..938cc9c16119ef0ee83dd07c6e139bcc02cf659b 100644 GIT binary patch delta 848 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 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-frezon.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-frezon.png index 532f2d278568d512cbc38fccf13f6c18d4559372..48da90174ea44bd18be9d620513a6270133099e8 100644 GIT binary patch delta 859 zcmV-h1ElcsCFuX3Z*t!x00mdkoA^gmpB@S zfC3AupjGrmSo&$D_50^9+&=j zTVnzDUm)d*kbiYYLYfFfQ$QpR^mNi(C^6d8<2V(LJtMahka++v$bB}?}C6xjw5kMp$_*ptX%+=O%fz$1w0AN zO&}2)2C@f+*rF+5DF!(#LXO1k#+COJ^mWN>&DbyiAb*i9VEsx2Y_RVCRnzV|z2cvik1pu%&t0aeZ#5HItWL<>ZJ771~znPiu%_>R%&;;+Ta&R<(u!zP? z8b5#0)PKntFtn3jb2Hz2o=&o|KYRcP%X!kmzH$u_M&b~bfyPYQoVs|KcFpkQlmY`r z@JQTOjQ#|+UI8;7AZ&|(rrL-&VP+i=foKp|iutx}Q|qzbUJ03=|)yP+q6xu*&_oGP?lIGnInKr<>;*(4uVB>?Je zdsgk>tUzUx*80GcK(>JO(k@UROE)#p4vq@c$I>Y6k`J#KTp~OazXRWPFxA95vfk2>b z=4M7Vz!$vn@NfZdMo7qK5QvW}#LkIngJXgH=rj+qFA2;H_9KBwL1YgQD5$?Ei5xhC z5;(Qa5WUCg4>~j9hn}3hesy@ae4l^na{Y4dtkGq;bDSp(GUZj)SDqo)N5|zP_nJK} zZ4UatE4RZ@>rva+{1vab+i@%7#fMkFGY1D~n##d0$G^9KoIYMho##l!PFPBFJzlHr z)ESl^mC)7Q#_V|C1HtTmr8gn*ux=sh9@ovuvREg2$Pt4SPi?B=qsjZ7h7U73zJJml zy|<@*JS}DD+sW*uIn@U<$5rd;V_Gh|r-IV!W*$@WHQ#9K4(e_>ee)hA%{w0-_ zwSWc5qLE_d?~fjR96A;{=oDN#I=@iecFnN<>A>WSbL=sf&u_xl+9+A0~*dlyggt_s}A z%YL4&<<(Wq^}yH?Do(%%gT0B7F^gtT<$U~!d-X)S(gcfz*%ZgPOln6SWxXlu$`6+9 z8|*ndUN(v%n#eYrW<+B@Cc2*nkL)|mA9YLb1Z2CM`4x!TH}R}yaa%i^!bf@T2{}nN zkMnbq!v&n_?Fy4e=+NRIGus17`EP6v6tz45QS*s;!?37BZP-Ah3vU6fdg0BjgJDjU z=B0;D1yql6uOi0GWa5z1cSPhn;H1`T9_gf)Te`1kC@MMo-$-u*OE6szg_Ttg(bK0Fpv8WM?|wTzK)=;_RmT16WR;Qbn%gE8kqpAuD+kPaeI(<6z*1hH}u4A z#Y$Xk?LoTD+~F4kN3ft)9#8p6!B^Cy%HIz92-NqZY$=Y0r&XGLowplEgP(sJ0E6G@ zMxCe`9bv&WZ(Y+;e__0f|FZDy5?o#G^NKrz-zw9@-LyJaF?1d6&M<(65ppOXKi6nZOz3N9<*2+|Dy{>oQ@sjUH2c1yK>I8^HC2bC(42~Ear-<8)|`P zJnb>I7RJ3KdVW^S<^3Sg3D3X8s-~yh_C8RUm*Wf_N8E;%Tl>X)M+ubDDyuqKyYgR} zoLs}5bk#4)uz0SevN8*@;2l^{{n|MUWEn#m`Hh@h-;pN=m0gJA|G@TD?lz zTG*bg1e-3h?l~+|oPI(myRvkokh*puKoCb{^f^s{n!%bC0q-vK-O$nVlPIQ0R!uxW zv_4JTtEt;)Gn8Rm8fIjBOG#nt;Hay)%sXPZl7>WF`(<720R`~^)^*kUnH8qi$@5be z3SSOUWF>iZMkMskon%?$XKnYllTJ!Y<|`cy?db0eRFVitk2Eg~EWg__m&!Ggpk0j@ zFP+Uk<>0^7o^j&6YYCVet`+_o+EVO$mF0A>;S6u50t!7&*gNN_UfS%Ukaxj@K7~8v zoj=miD9%pu${1YC^=j=Z`LN1nyI_m<85rZK(8F#MiNDl6(l>q+nYWG0B0dGo%8?g1 z!tcuN#X4}jpBBO1eOA`*=w1GhnCLXqgWP5(8s6Q}+BFLz*NO#%XF~gZCkmyC2>M>x z5jvo%1d~AfGPfSHq0A^kW`ES#Wr4)k7?3nJdM3jm7o(-yM1}OFFQqLh2QL%_LGL#5485HTxyP4T;>m4*w#NRGjqP6Bc?C_ zR%=_TUt-^v7#mK`f9d-i;`ZgWZR*X_WvAL4$Kr%cFAnC)OE6G9p5ZnbpB?iB`rwcW zoz{ThKXw~d`-4waAd_rm-=T!x%N$}Sd|K1HHhQ8Qr0O58Dp~pP?Ca%d8uLr6<`;vz zQ=fw`?tJ&;5xe|8ZC)8Eu^V;-4q=x<)61rIVQ99Ygj<^SG8uAh-^$-C8$L~{VkVz2 zx>2iu?r!yVG}0bDmx?_D`9R9?w!%j=KgiEHCiG0_l<(phq`HLnolBC&r;~E|`3_Ji z>U2F8Vl1vLWt#rDupG;2H+Cu6xFyTPu!`Xu?B0&ODtx=06l8TaM3{TV+peq|QncVm zvwvoG=Rhgw1Sc%C($wYZrI*+h*3ot07ac0yG_zqJW>#)mtbBZ$ch(C{$ieW=+V9sB zwox)e-zFO~G7&13@kxc&sb1ff5Uh(qFt#f6gzcCs zs7xuSZi9Y+NeKnwnPH}Tj=V$i-;9h|wpX0A$&65vh<<$d-XTw0B6vL~U{y(lc+TNd zgl@KP$NY>f^o*05&zFFRA-;|uOK#QiM~fA4iWD_NC2-+wb5?OOpDmRyI{J z{XEnn>)XfgW6iqTv@=&+3hKYz68VGI;G*@dPN6RDPvU#1tik>kVyW%^Wt&QQXryAF zVZT&Q*f*C$!UZnkX2{LZwz zEGK7|K^XO^23R`0ymvutG~(+?%rpB|d$eg}MQ_Cg!WG-7$+CVNeu|_6Xk5!4gStHmh%oG zcMPU*C;H<^wcz|5W^Rbhp!~62xhK1|+7q*VV|eBJ`xomQ@3Y8R^C!5E)TrV!i&y+o(rCJDV zMXUG{yB3;p_|oncx>$Kx=JtakraSDj!}d&;H3pwAcPT1g#C+X)atlSSDIhu9%$?Uf z2W(&>C2EJ;V^Uyp?yTx;8)j7|R@Yeh+7VGWUFkZPXu>%6`pMzJ&z1q_9IA%B+adeXdAZ(@$MH z&KZxaU8q+OK6EP4+ zH7p$KXGroQn}yIxwjow_gb*Ks77?PW!-oz+0R$8h69*2W_);0DAPi)a7X_3znqd&| zCWPsOfjD8U!G<(C39PEDstku32ayAj5FI`+nojgU*%+DpqyTCdh!>OThl0UaES54$ zMVUtTgdw!Fv|w-~42gsS2q+_%%ESdhsSKG7iXR+CBnE*__G6N1RPY8T4o?eUVjvKp zAN;3(6hAEX7d@5nQw2Z|SP;$+hERsXC=}T577V6wAVBidq5o*Xumf&7FdGts7C?`Ake0*vHKq5x0^Fe>72LzF=-CYCcSiv8U%Y=q{~7xx7(ijMC?gsnV8cCgBMf9?d=!yJAQMrWMGqVT zsj03?fRgZd0#sE?T@$K_RL4OH2m%tPuBJtTduaVeWlm)LT@Y|F#5-&n~@p7_`#b^ zKh~5j$^S>~N7a|Snb%>oc9_Rn_me@F&k zX{*9Xz~sS!^Z}AVLk-9$AidOyDr#El9_ly^xXRzz88i1K9m&1GX+;Cxrdl z3V-UgvFZF5e?RBpzc>Ow{qG?ENZ)_u`d6-hq`*G{|C?R^%Jq*F_($M>v+Ms&F227m zQzR;I0%8GICCFCETfjAo3vXd+1iG?u0p)gqO%6@$bqtDq?axRQecLQWQ^v20!QGH|JI5^W6GEK%u!I(|UAVHO#> z#~EK&Lnb>NCDHuZy;`HOQwz1lgR+y}Ee)3SzKbWegN)va#D2fPR^;TQa+n^B=Zd@* zR%2Y&t<+S}^;TJoL+R_s#r>qMNe4JgQ=Z%zo|nm-&9r?b8Lkbg?@m9YF9eqq$P>fe z>TDWKz_Z1Z{y293cuomU{CR*>Z7~PvX!!kUtZ4h}dk&Dm)g6i_WX;7K#CbTu=OF4< z#qGKLlYxtfca`>_=(ju-UL`*BeVBZ^OH_O;w5jWDv3Cl$x2mZZ@XssGR|-}&P1t)g z$3e--{iRsfbT?@1h6D-xROfh(%xO=gyC*&zQPgUoHcghPWsWaC6z%)N?{#%p>e=;g z%E>b3+8mY~R>PKRv1#H-&FG9R=^XCftnGWARf%wT#VaK?A*N*;51H(@54n5q0H*Un zU1${`DfkS*VeR#<&Wy#+{erYML`w9zEWI5t{1w?LmY)7?kA{x6XFdMoNi)mMCexj7 zkz3%~IYG-|I*N#A_ah3*Go^UiYHnXxZzbOcHL-oV#a5DFxskQz##Tnf2JVsn0}sKg AWdHyG diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-green.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-green.png index 8a4d83323b9b31b2c0ca5ef1064d586cac0ce349..aafe16a4f5ce6e10a8af34a10ef41fdb2b97e702 100644 GIT binary patch delta 836 zcmV-K1H1f-JlY13BYy)SNklJ!3Jm?#tcojzQ5sEZNz4 zUjFm!^Rj_|j3eHX1LnW(n5ld+kONF%o%%0rfB(*IDh74Yz(Xe*PX#^G_pt> zK4#9AA5T952@=N}9ay@O-`jr%gohLRV+1xj_V?RsdjZ^!5AHt={{n$6fbih6b(efi z;Bsi!p5X^L>Lt~x*qgc>nHHW`>HIbZ%=zA#^RZMsUSE^X5Ho7~R|*8sbh;so@b#u$T)1=UhLnaZ~9dLf)Ebj4ZPjMPN+WGv7EjV7X; zL?r=Z4AK`^?hphYVMtF#0#mjRbUuJ4J=Jx2vwQ*RhcK)RKs5nb5nFbfA;u`gT!6PB z`nK*FV}Fo6+eXyseS#n$dK6ii;3aHEux9gs7Yh;qpbtE0z;NI8fsGFE5&3dACHe(8 zdC&HT%@7#@ml|*_t~6awAQ>pvVx;fN>#2|E^I)AD%6eIA^>DHin7Kn_DG1kzuW&jl;pdYv{ zDl%G_K!WIoZKZlo+rF?9+tX&@BA{1wPNxpF1Vm)S7}U_c%XM;&0QAbqGy*u;F}0`n zB0T`W3r4uvz%=80C;nBAdey!rV@yt`4*q{m6SV0jnVrn~+}hLee}kV>{#b`{Rqq4< O0000V~ku{1$ktnhiSyI`xAlb@V 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 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-greenys.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-greenys.png index 841d29e1728554add170c84de9e969dd2806b426..87e7dd783c508bfb3a6d32980d898c1d23470114 100644 GIT binary patch delta 850 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 literal 7218 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 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-red.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-red.png index 64df1f4b60114d0cb4abca885f5613deeb4fa291..88a6f988dbfcef2f37fe276591101decb4b3008f 100644 GIT binary patch delta 931 zcmV;U16=&!H@F9oBYyw^b5ch_0Itp)=>Px&a!Eu%R9J=0SI=wHU>JTf<5wC6W^R{+ zfy1I(FOs3)X^??<7&{1s9rg!!5DGnbU-Ueo5b)q1(1R?Vq$j~hVJE@prJE9&Fq$za z%VA_Rri+LCuuYqOUq#Rd3LkuL-uHQbe+B+AI99>k#g#BQD}RKHm9cz3JwH7@VA9Pb zw7a+xntaG0`5S7$F_GX|l$m^p+DritzJKA6iN+ujgYRzM3F~~1i6|3+1c~A5whI2< z%Ftc#@c?LGx-d;kpt`L_hfjwHJ{j+~s01pa2)$wc zUPMg`Yo&Q@oNULrfV`nZ#N(D7Db{HTRL{>~lz(Nk_V!`P{~4idVKI_b3h>^Ev)36C|)bg56Q|XMs1pzgn94B z1We0l&?|j*E?q(Z@blG6AkM$-d8mlu$ZGy$0`<#YI&u|4ChmAyDxxF^X#@}#Fzs8NS@N*3d>?Q6-iXYk z8lY`LDuFDX7e!Rh&(N~FX&a7S`degQS{uD9a4R&W3S002ovPDHLk FV1mZ=yRrZP literal 7137 zcmeHKc{r4P_aFOOw2*jZnilJuIu;y*Id^<_kDlQIiK%2pYJ*6x=#%LpwR|lX<-lu zw86w!-wL>c*DgVR;GY!`@EHUW_6)E&!nPuCz+Oy-E7g+%X8U_lz!X2KD+uJ*Uz+OS zJB-~Jxyq8L;++v>9Gy2`+k$6K$S!PpN=d7E4Nj4#QWY+ET)=wpU+4Umt zPqk*~a_ zg-1r$!)t37#lV-`zP=464gVpqf=LL}VYMtI2i~4uewtt)_xM@EHAAPsM_2Nqczed2 zdPs_2V?$0Uvd29UP{+H@WWhe8p%y=%2NL|V>Zj>(K98??mfy3jofx3qklhlI-Ym4O z!{^?4HpEMW~-1y;aCnfT{E5z?BC3B$ui{&NT^3Ce~9&>+E-Ba+^)_$_aQVciWQ~1s) zK$gCEA|zgoJaWh|)Ax<3YCF0+Db+32XXtoax`eF*^hC{a6kEzT41$1xU6by;K4zG60{ z)A|lk6w%0%^s9h;GRt(~;}VuE;}S%79N%#Le3$27YNFjI)jmd=mrB@>@Zm>k{r0UY z9(!jbO;enQeFa4Ic85-4;iX?byCj6V7Lgp3)imwm{NNj>JOthyzEoiQ@l5X2L`)6! zuJ3axE(KJV%6^`$+doe3T{iW8exz-FcY^$*@f#%$w_n1)9J>EHd{>Jv@>m$^>1B6b zpPXAKY~Hag_X+l;2H=}Lu6kdV^?ph;jq!hdE5LTY@_fO_%(P<3S$cTD^-RTSvysUU zz2C;Upo11+8LSdtNy(d5FZgqqxV<{)UA|P;U9{J*{4z+DX?g2hQ+2au&PPJ2pi7k~ zCi3d*o}m=OcVkugU3K$I1`Q0+LbI>vtsVXM%lhpp*>)uY$NVi!gk z86Sf^>fc7*<&}Q;bSZ}GdVUBxgLy=*sUaRc-F4|GZ&CX$+=$F%uIx9@kTzS` zt#6H+!}jbWbSUJhrQ@Pv?H1?(xtSRnaMWn<;hRYTFrU?MgM>K7H4g8D>Oo^)1;_&d3k%8w zqA|q)xnEd5Rfw9rSM}!WNu>b{DvFVdAkyJJ=Njw2#Oit94n5JPRC|z9$wN-#HR|{ zPwZ}*+bZv*c+I=)bNuC}`exEjdSW&+Y5rlL^gE{+)qSrjTYR+~DxSunPB@xxD;BUz zKfbJY-eFvB!#(-oiL62)f{|m@6*e(fYyinv?0-+X`NUXCwc5gyhk4a}q{%g)Jy-mb zq!n#P{hhsqDxUoy7aDFnfUyrV>w_8uFnjL(wDs#e>i zZ)r}>z3bS1TC_05yvl!8yGJ7b1D7~$M_JXi;uqF~cc;5P9&6FT3wLegn_E0PJ-R94 zNED>xTS9ep(DwLG(#;F#@ekLGlG2wXv<@9_)nu9RAF+FS@bU+x*mGz1nq_#t6W`vd zRd=G;L^n{}D7W>^<^7f)w@W+iE{L2Qk}Jqv7<-@NP~>2e{7}zC006oVj%yw5qrzUv1`C%l^E`J^s##%J#(B-LYEQrwe9}Pmh#6e_>o_+~+W5VdgAp{G>ryLuZq*#ttGjSy^+x#yl;c=ebCVuruPSd!Kcky}OZ) zhOh92d}1ZxSyUkfwAmLm620+yEw4g!X;d3O?X{u7ykSJ0<{`tr5%;CS=!quZv{Tqx z*!_JapI-9urkPGU`!2EC9;Fvln3$#)JlMBENFHgbYPrd>X1r@MCI#wWBGQYm6}L*8 zJGT98K<_Q7gIpTTE#1@MJp=a}9nUQ3z>KE{wS(Q2ZsrR-+AI6jGBGc|G}N;4e}$CaKWR!DtP;3BITx$`ckWXMXV?Rm)0V|H;Rhb~g{8Klw_J?}Idu_;Vk>GFwH6`Ak9?;&0z7`D%sonoIR%Z4XbI^Jg-hLq) zAAuAL#T~FK#Gx+U+`W{4=V%#f*-z74dY^bq|Epb;jW(ePN&c*1j}ocfwYL@LpXZ&M z7zsvo`8>PfNpF<8q_F6`Glq3x!-%cD>~N*aa-+brfmm@xrMzCg9*`|dr&j4D&#N|t zB$X-D!u8bML3Dxah~BTKUwT^|JtB0qpeyep@sC?mk;N|cJMWjZgOV4%FcMpnm!-SM z2K$?FZaHbG7F(vGNdE3!$DOxjAQ^(2&UPftrQLsZ7>Bar7|?G7)o*?4?ALZIc&>x$ ztCMdU-Bi`^v2dGcj4l%*+d<8rvY6?KLs}JUMG9w#3Tn{8WqbmmDz>r?89qK<61;chz$;vD z3f@EZguoT6qi0^%h)qx7rUtomt6FWU+_>@TZN~C%h_D8w`TDyF;@r_hW8%fEb`{ql zO#Rf*@CV0uH~;LfEKvo%b6nbqey7Kx4C)SORZ+(>7GmyQQBiDq*EG!UcxEy2LuDHy z!)&97mZ~SYd$x%{tmwJV+*VbYyV-J!(bmKHK|9}7-t~{GvM;Tg$9)lr;G^xi=aZIi z>>^}R0M<2>m9Qb~G%PWUjZvF?fcv)Vw2lER)j(3r?$Rh)sN4(R54<0Bc2N5_wU;YXu+^Y*klE%cRq#s|GW# zwVmGu7!Z7^z#wqM!W>It&|m~IgGhn-(Y$~`0R+<0@$({(JSc21k>W+u4G=Ju zjDy&zTOcgF^eFCB;{YbbI^duUDZqn-AwzVug|+;!0051`CV>5Do^%%04+mMt#RAW3 z%Ww#IU4`v|gB-EIgY_6p3Rn%M217s%{HVSth_*0Ti%E9HTIn19fB@cb5O+4)3k!#H zI2;&96~ug+*dgz1UO+9lVA~AToT| zI0yugga7o8=4D~=6Q0ic!2-Ys+>hV|N5T+r8V&xd1&eLq3xND^=s#MpY=C(RZbe}+ ze3&GPfiH#5-t{X4ne@}%%ZKT?9uAoVr+89mfGP{v75TRtcUZfBY^o&+`no68T-02pk-l!)n|}=*4#7E$3fQi$C4Q&DjB=} zh(J*=C^eKSluT6DgsN$hQBVRxg9Jq&k(y{UnW%w4qke%hp|jWoI*GCd1%Sh-0FH(l z3aNoXVxX#Iq$>bH(SV{=(IhAmgG3=QXf;(LTJ;wQOC}Y_N`mLFUadiq0Vt%VIz>Yr zsRnh$kdaU|q8b`Xq>zbFO~6UAI+{#SS0k)Lkx5uX29rhrj+06wxKZF%522B>s0gTwkuWJ9Ggs3>b#(;hd*JJc zwiG4{$i+2OBm$=TU9xsCSRfpLTEbeU0s!l$!NDUj{UloHzqM-;)EaDe^2AS&W|9@$(l@D0!`=J|CS-}4O z>r3Bj%9`T+{q6g!Cw0A;z~J?=z!FH`LtqhnDdcrO0j%#Lk~@L!MgjWAkA(emo%)}W zfkbjeX<$fDRSHnvYG_R&6yxfu21Tl)5g3Xpl7LeEALuNGE1N@LQVzHQJOW$+3A)Y| zSmDP}DgNCXjyq-T901Co2vz7WltJJ>f`zYjj6b8*g8wf*wAK}VX)%D^_cfq(0i6*3 zvlaf}Ypv=07k@v_;lH>80R4NDf5h)^x_;C3j~Mtz%D=PgH(mdTfq$g@JG=hh=o0>G zn4-{uFCY#uDvbrskOCG3*d`VR z0^>a5!t$GqkAC?G4Aa9V`Uh-|+?zbVlOc0J{A7cTA>Y%m@Zy>rl)^`N&9S3*QVzd_ zE1))By%%R$yw%1JTq#`YUYYYeC#SghTbL;nEs=DqG4M(Fh;Q$0YvZU4=vB8@b^f!y zVmr_WuZWy4?=%Ubw&N=3Awv#M)Z##q=yS;8P6B~wF&ZYlm|~1Qxh#w)4VoM@2tDB+ zek$EOr5&WvxZO*st9I&(r(t1h6;cX>ro9+^#UaHHZJvjfyTmkO28%W->xP}I;LCRh zpH9f#&@i~8J~SY}Z)H)%rdVxz)(qL*gNE9m+?TI-#aS-sH7-i>O8@olMt?;9+!IlA z!fo!=Wf$FdGA`*C!kaP0Z%u)F{O diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-redws.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-redws.png index d8fd6de2cee865983010c7645a5262b6049c63e2..b564c6986620d5bb5ef4cdef58d8fdf0f98f15fa 100644 GIT binary patch delta 1122 zcmV-o1fBbvIQ9sTBYy-vNkl0=DiUdcati%!I*Z>aw_7j5+Z?wZQ`165hjVxEEB1%QT zf@N4bdjfdjh;#GXJHR4bi9Ip`(%BQ{+Dc^|LetMjPG5%DRpcM;sZ@>z{T|UqNx=r0sUw$XW$b zA`Ypt1H^;CS_NVvg5uC1$V-19P#VK}y6O|IonKOW<_>;WQ!S z6@B#=JU5aAViBOaZSM==$LxWjK_C`^ytsl5*1*xe$A7CoK2%q|^!M}t9}J-JP(zaE zAup~#5(MDY=iSqAc@?s%BFXb8kvbTL`k{44`sh%4Gk+~T78@{@qM~6OKW5nTBQH9} zQdBg?VgrV8{Fpu8%wJ2N?mwK~w+H^H)e!TUx77armpFn_ZQCi_rpfX^%;&>b^$xiX zc;-s8Nq-RV`vI%aRCdg4=z`+JNuaW0r)0qI$IHQpCjn0nFbo5PSQKVwGG{JbUPx*h z#N}1fP?s1`Gz}$=pfEd=L5M}O>75Op1PE!`Cn?O%WNziIERHjo}|u#j%d1Dobu)^~StD}Q%o!A<^D_AT2m4u&C5OryjR+^lYW zx^Du-zA$7}HAw&_kx-Y=(Fkxb3;>WPrXdLeQnhVQ0zKb>RBao@#8XgSdk1A*|A)wv zf>ZE!6?noloaA{(i8xa0zxGn^?SZAJm_{q@MjNan6~$1SnOTgXSpC1I2lW&qN@GLUw6()8Pi{YG|FR*cS%kK`0v!Abxor zn7UvJ!GHB6U>F21y?x$sDp%uE7l3%s;Q?7yF+4Zv zX&f{?Vd}u4^aoRK;+qdY!e%FqlcE(nvryCy)drg;U7@K=_M_Gwz|= z>m)I&*Myew~#(Ne=FYq|Zk z{8utjY3k)npHF1OR2P4J!>Q>ckw=$ik0+0yACFOP3kjr#p$wuwQZQKxgQ4H=wS8%8 zXk>an7oMCP_A$19G&d1Nc-t>C&Xu`caD|!XPMpg2s!okTzd9R1 zBl$eJqIJ~XYm=?5>jy=7(3I=@{Rv|x8g?H()yG%#nPffly+n5ze=%`2R#ot3V!J0w zH}|#f$w!s7d;wef!rgeMHyqR z)y(bOTkaq-iTb|;75pprwpNg<^ZC#`QzU#&ggi*2EV=!L=`;>X@P;24%iJnrQ&e11WymeYs5ONuqYIr)@=z_6J(x%kbJK=W8Zwr_NjUI6b=k^~C@%(n2oi zLYk#~NcHH&{EsbmT8|K)XzdVF>QNb256hk+pfxhw1 zoB=y(%MvlEPR}U*>Q422da(!kQy{pM;d%j=MT~hI6V%ziTHn~ml zBroa3p{`p^C5U;~`s{5DpFgZI_ZL!3~N;|&)o35aeQ*imOJNymc~L=s8us+ z5dG^1T5@z|0?hJSQ-+K841TAzA`)k510fP@67+4`BlB_lMxSQyyVt`6I`4gQ`H)ElrKq$rbx)hJY>?!HUnthd?%UHW=n`$~ zyYdFACb06IQu86Y!f6zX4?|x+S$s|sV zR8cCn;@xOwXGJ`8s8e*%<`resrM~0fV*8Spbkw>Oj;ui?G!q<>D?)DT-10GnR%&LL zpH{MyGvb)H@5u3N04k_v^PM8$l)*i|p>sAJcho9hriEx28-285*VJ}2D2S9gS=q!P zj(TkM7xaQ5y_VBYBy^R;y)a*X744bQS3kHAWowMlc$M_gf~9`(%eW)1^(rx=lX>HU zWJCz(=JotimAtZ9zKHXpj*cYt5tDydaDdauMmu6O8^K0l4{)$;YJq{1g|r-oBs zoJ-ox_d&6HYusQ6zA~Vt#FLqs=O_Yy=}}*V~RT#KazXN@$yAUJz*)X48G& zxa^*%YErMIuN9V!u3&0%VY^p5q{n%CiK7D@yoS|h|wtj;2Gm6qzvb%YY zAko9kwdd|9h)8;7VB8tZHghRS2S>+E&B;4NL!*1|IYrrwv+98lE}pg2^aQNN*UO52H&zH+-z!O*1WT<`vN^JARulFkIWv94XaX2Qe< zuQq&CR@0S9$WpT5VLp|dn@{KSuKPSMx#d~40~dJI)Mb1Rc%}m$*mj`x55s*|&RpD_ z7t|27&a`ZEDQ1V=gG20B`^L4aiuAhbzj#cl*AiFUdZksiZORsO^i13@@V(O`ouV8N zz98t3Mjk)o)FVm9Yej^rDqOWVsKt%Z&jlg1u|e=|%jaiXhV{13Y|Ge?>$ih`Zpv+2 zd)3E-f?q3!U6nW^fzy~3B~X#{^~~LkyIPORgH9Lwv^s~T!?1JxkllDs(Jy@-xa{T! zf{N|$)#9&BEDT{660~+iZL^2bPwIBN=8x5o?JlQ76! zG<_o~VUm#%rjwxJFkvbr`2Y*MuAx{|{GtL>DFeDu9t6pS6XauSt#d~dN4n?CdM9#G zYWOn#nQJ@FhL~hFmt+@Q8@ut)Jx^7vv^?sijNIYJrbqfnT~1GQ4o}(c+9IAp-oSsz zeYA3<`vWMdAmB(kduOUpkw~gz)>{z8r0&Mylc!7K+wAy;?dtC2jP#qto3R}zUrTp) z+Njdj9r!NrF6I!l(R{I zja%7`)hP{^l3*bfqx^wKZod`vP;efDj~4C64YbGi5=Cp+q^;6((r<_NL^)d)z6pFu z4v^B9v`f872Q%s``mD+*&PmEP>gk3NwSy)MZ(kB*z#Zrz3c+a>%-6$Rkdh1O!bJ~` z)~cVF5reV#EApQyzLrd!ulVLYekd+Wr|H<>hS-JH&1W9Pe26jnzPXUeXHbT?oc&0~ zQ7R7tr`0UE3HK5At+dr>A6EFH{j$TOCAO-_ReWVT)b~ZcI8Aw0c~0uawfA`x^hQ+7l=I~DVs_-EliG(2vqns2(lEK6vx)t!^33j^#n;OdM?MeaQVld-+RJvv!>YE9!1J zIfX51;WZmQQVNx1OQkW&wo`G-^(X6}taALeRb8(>7!vWuL2uuU@>O`+@|E-;W7oy} zA)~-`Lw-a9akYI~zG68x0Q5jWNnCyY!0r32uP*)TY0tKX=qy0*-e|VD;QN4yh=4U@ zF!!v=K6(1q-KoI(E)8|D6na2A`Wt^8{eBd_6MA{YYWom8qj%<%GHC6`PhMda`vBE>`TtZcCBqG-4lzG)I52O2?$yshUn?U_ie8q12SFRU|-tD63 znHv%kD6{UaGH``&_`*k!>LI9Rb;9(}E%B*IgULZY)2cS-Dhj`YcDt4OFdovlbFSfD ziUfZG-U@#)uVXj)jb6j#o42355-FVgMb;*b^{4m@)54QSL47QDMaP$vDY z&}!`KS2_1ft#vi!g!rL1mf?b5C;2Pl>Cwsqp`X@$HWG04Xz>Z)ugbrmi}rZ8VFO)2 zwqFrDC;$nId86)y*kL)-BYnbrXgRS#Q+(4P|8f9>IT4j>2yh?s|0ak+tt(V*^PUe6 z^9f_F=27S81;{$Vg>4;zzTBgKD3yD}cf;DD2~0W!M`YqjkZ^h+_X+?2=o^Ix;s^mG zHV{vuP#FfGsk%lGkV-TFxoTr!*g!LqAJr-%nB){;?@WjYAm|Z6Mux)r;b<-Zoy5ig z!|5~z3mt9%TEj(izjG%wQ4_2|+?&VDoTlC<0_C4Ac)MlF^PB zi=PnOkpak$%??CEpGnfK}>*?u1VF)M!0p?nOSsVr%7Y=5yw(}rir zLNGOuO=U8GJWL#(8NxOIfw+3$pYhQHvDjbm4AxH;xO_mvae+`c1O}zkp}%{u*yf>J zke>nlM-P@W_of7OB(azw!32_dD2c(|{yPMb@XJ3iB$&3A4v_#Q(MWWzDT_NR{BKiQ zT4No4dGIKpQ0akdUR<*OX33_K|03&evGFu(>HHoD*Zmjn->m=4ea)C_g~g&VOhO1R zJZp>rh&Ml)$RtpS=(SHc3`T~Lb+o}alokSv)P|wJI3$?}#%tr@T6hvdTTd7N8c^|jDzD~U;;`9g(Hv&yyqGek$|>f2Geoea#HCy z3JDs>psXo)griLytPMa22<$J30}aO}a~-&AfXW~;!&rZ*oT+q@6C20l6RxYPrK7E@ zgVfW}MWJ+&e;K)uf?3>N%f0h@E1CZNoI%P zf=MP6E{|NUxDC3-6;R`6sWkr{O_(2vR|hU-V3-#8H_AZJpUFabJI0^c>O=n*ANp$s zzilyGzaKL0*2Uckp})4mpL}s+{crw$*5SW71rYe}B>#xtf9d*{u7AY9KQjKeyZ)u? zA2INcjQ{Pf|2MjX|GG?(7~B(38275g$9Lx#_nIY$x3$CoGI=+yOY5nRxUY?YR_-hS zKx8xT&j-lKmE#J9*w$Ecp)mmoVGTI_Vg?of;5W0zm^iyNO}N7S28^Yn(n+TNQVO-~ z`f>Maxq4~N+?iw#aXR05725*gxKxKk3hRL0!wg#9LH3jMLRI3!?z1*3M7tx$Y!nNf z_WQT1FWP0Tn43l}uYO%wT+NS(re#(V*1f~>i)$11)V7+vX3kM_N6^6XkjSXbTeiLO zg~T{-_gnX_t>0~<-PLP|7-{2&a^6fv-}jo(&_JZz8KHvmI^#qi_03=Ca(tYJauyl> z>kYE)cq*Cm1Rw*jXSGU(Jv~L zS-fxc))@h1K!L4QMI+v~`CAY?akyAMAqGXXlctA97PrRO9Ll>0@&Fjy3aDFylo$+H zVRiYc=P4;=CUZS)>+>&P#z4{;cq3~Y;6w%ZS+ane`Rq)aZwG?dqj`N)8t|EbLiViB zx~&B8HL^}MGI*m7sOvGATUeloFqln)&+g3k^vqVjAze8p{TToRVoF8`nnq=dkLMS$ zcnGbj41`m`K!KQbaS7Kl@@$kOx}nCimbqEJVyJl+!9a@y2VesqJ~IVh(ALa1GU$F( zb$hJe&3hKTmp365J#UbQ|JWkYeFaf~yjoWo;@NTsL5`G_6DoM>t!{cQEK5)Vq4p9q ztuNUX`Dp$=Cr5a@!e_bI0%?fDT(m}y89PP4ox^qw|*X0vaVb8hOeBiQ|>yK4_ z0(-R7Qtr8dw*$0H05QFctm7eF{7EaKWyZ*r1pJaNc5ywt;K^gN!X4_o_Omv($5fj7 G9{C>!S)=j* diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-water_vapor.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-water_vapor.png index 72787349f5d2ae281414af9afc9627c74caedc1e..ed23006740e7b4a3cfe70a524d8d518651832d3c 100644 GIT binary patch delta 1013 zcmV1@j|9)AjD}Q5{mh->Amlx74Bov7z z&WpM(BI=++QG`ec5%Br$oX6umtgWryq9z(wVH@o4Po1mkCTC}FJoWoT2-vU`6ixv^Jl<2auBw|%ee@Nx z0iDN33e)d?Cx0IAK~*;agi`Q<2!ReoX}C8V&?)LVvjJWB%vA`dU~F{63A+u$wD@69 z=f`g{eEZWf-$fN-u_#}s`dM6Bd4B{NmEb_Ck0-JOT8St9Jq*O6o@Z+Dp_jnD*52{M zKTmQ8C6h0cU(nLr%pa$x&VC^9>EE9$)6(3`;?fGomVZU^<4&#g?)7hO`h`>Amn9aL zR!Ang>58=1EyJDtGVA#*{IWz*@ZEAHaIdv@Jae~`4fPPiv=~VBv9R!*4n-lE=&q7o z&u>xGb#l2Or!+HnJ7Jsgm&dQ3-G~4{(Ekb4y#GaAcg?XK0sE{QJj*Z=z6iBpT3A+v zTyChL?0?wk2$ofGnv#9i4W1EbX>DREJvcGF@?yp>ON7Hg#zsdP-T_T3QPg#&(t{H% ztxf-qz{csJxoy3X0zn}3^M!epYG$+>x+K6>b^ecv0Is3&`J zTp<(+05CZ>PcqT%`5<{l03eqeqEOhwFfE6OzwNUsS>y1vW7scC#9~nv7M{aJQ)(Ci zR|1h}Vw}se>rj-cQml6e95SZmoZ(_nX-L4e46hpW%MylZp{ko0riCOmReiBolyEqR zVSifJ&bdLT9bfe-o6Rtg>Z4HDtJ=4$3Yu0Tndq(>PQBntKvg%LC)Y1au039onh1x3 zPA}l{0^7sMjR^GJZeu;aMK+u9^r|)j*=&aO{1$z;+ip$(U|0@SdDRHD5m?V}F)W9? z)VmXSdHm|x^7byna>%&@jZhnbVL8O|_HHh2osfSr4-Cs8f?YADe||wc-gB)C?Fgh# ze0(n8xecMI*q#fxY|oK!JGG7GE1-%k+up j6(&$iq!C07|8MXY5~rSXsRM%k00000NkvXXu0mjf#;fvh literal 6443 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 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/mask-yellow.png b/Resources/Textures/Structures/Storage/canister.rsi/mask-yellow.png index dba5234233d1b1da970829d9c556291d21220c01..038a8aeaea3a8a9722bf5e6cf12299e407bc2301 100644 GIT binary patch delta 1132 zcmV-y1e5#cI)Dj~BYy-(Nkl1<6#H-`<`U?{1@R7GcpK@*8uEr$g|N%|Z{KK^uDN0A!&$T__GwJ|%pu&s%xCIUI+ z9FL8(Q`7Kn!P<-GKLX(siJcjNv5|HF4i6*_555QB*6n1;`l3I2_5_Y3jh6h@=P(kJ zGFU}D@7?%`K7WT1t?~n$9L`1P>@bvFi%A)Rp=3b@RNIpe%?d2}x;Z?MN}P`l8c0Yw zn#D$L-G=M-3w-Fb;qivgjzGB-933=pFOULstVL3c4jQWNDMn2-gPtz>f%g-M6kYGU zK}~HVnHMQ_-O^I0eqK5!Qd8TA#~Wtr∨UhWz9ip?^x}Ykmb*QIE$P#%b4Kv9z6- zhVgg=e;|uh)Dx`eRwIF)F8ZT!6E=&izHdkGI-MP1oc2h8tv`?@7)r8m=dzlb(YT48 zF1i2y17=P|AR0Fz2m&A%)c;^8S@DMX3Pj^3KrU-rF%4}NnYqa=IF9&)w9AsOyR7|^ zubYIltAD2CvM-IzQrWnafT?Nc z^aSJ`8;YW^>`Qa)hRGe1_NQYKSj8xU*eo(mdlMdS z7)7aPW3`2NJi-^>nca$_@N}gPfGbzi2!eofa*K`C7B0Q@7>{=or(H)dBqLV{gNg&g zaS>wS&SkD#O*7!yXKbXMj-G8IE1wkDzS{CF8>=mNykV@Op6m7tIC|FsICUE48j7QL zoqqwB0jsEI(H})o&hhBCi6ZMd7X4AIqMn$PVYYW&-GBc<`LbM*K<=>o=U^zwfXhI$ zSlI2Ju0TRcmsG-X^J5O0+A(=;cVfILuL=EwgtW_AM2FL^D{0NACNR6OjnjSE!<&11dt5Rf^5r*X08B6c#UGCk`1#>^?*4Ed!Iv%#5`xDJ795DiVqkl0=q@BoRtw zCrh$56|!&1@1frJ?LEKmIluFs^ZWky&Y5Ss?(6#8*Y&xt`#R5cp7WL##zOqN`MJ2b zgs>(C*1#3Cc5L1VoLNCZGhAE()F4|&wl&Tl#9-3N6mJrU9mpVoNC6Zw7gs=c{#DB5 z9yQ@JtJC5vUY`wD1{uKxbXZ;b)~v*bkKPZb9d?Ln*9+GHiNqzZu6QM{ew!@h6_Xuy z#6MX$vn?u5aLQN9cePvTYyXPR{DB{<15W~77^zcxR~G3P=U3hYE~k9ht0)tQvKpU{ zn48qwGXH(C#bCbSIJkMTOkQ!`pzpfCTv|pN)unJ~CPaXmHW=WS@Fx9TdYpF3%JAxz z2_c;1M9)mS>-V6EiyMxw-keN2HQKTCGRA%Hct88dS^diCmM=E7I zpN>k;+};y>VRdL|0TXhf&vB9c>}k8tgoE(nzVg`7F-!=mLw@2*zq3q1g}a}XeofnC zUu#j4=Eus~ZI9T-Qo1LmY1X>XJ)8gySvVZGEPUeIIO#%fgTzQ+5T#?4qV8J4X z$BUxqcw+cU!TmtU7KOTrQNtAVpYW+lr+(tNTg)Njz5633p zO%<{eJm=8FdU$p!tNvlxM=5iK!mI={g@+Hwq>+Y}q&=S0dy5h&S0#>mmM7V(f8*RR zi`AU%F)Mg3r2QdQ)OdGbqE+^F9m-h+l_tdWp(02|$rC4BeUwuElZM=aJH5D-sTJMU zuac^zGPh8-9a`0{HLFC}VMJqgZD7ecN~a-sxwH>)AKay8g@XxrzhR0$>USV&hi4EWr(MKqh-ISy3-wt3XQJ1+2p)N zy9>QnG8a$c?JiX0MXS~|*LgJ?wx?Y`0GFK|&Y6K75fOlcD?F=Pc_>@Kulj;qNww}; zPo2YAzV-#Bi>ERhrh*-vh)F`{dp|LsFO&pXhgig1UrILhrH(|N@9$)d3RNW~*Tplm8|1dY_JP0n*+g@2IN{ z$4u_pSCFa8?D%KZmiN=2!?Ze%$(i>$eA5n@(g@IuO>xy~>|?agE$%pGmkKG=njE^-UrU6QK1*HdqrYpN)A_;l=w_QHZM zB6MhwUS&2nA1P_bQXaMNbuPN7H8SLyV3}jtb7Nx;N$ic#%RQrG8N=IK$e1EzTbI7? zp?9ulI4Imb^Q#?Ful!ZK`&=3xdrjXf7oHr45oY%|K!zJslW)x z@z8W1?9Rv9L^|&BKYr(YyM&8wvBOeDW}v@b*Rf)Qp40XvN7c)74}h)BypqdPU&uW~ zcgwYgzEwFKVkAYyHv0{G)EE%n6=dlgE^@xnee>Bo?wA;x;D`Nu(7w

I!D2H1eA2 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 diff --git a/Resources/Textures/Structures/Storage/canister.rsi/meta.json b/Resources/Textures/Structures/Storage/canister.rsi/meta.json index e9f02e2a62..fa24bd0897 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