diff --git a/Content.Client/Animations/ReusableAnimations.cs b/Content.Client/Animations/ReusableAnimations.cs index 2cb7c67c43..f6314348e9 100644 --- a/Content.Client/Animations/ReusableAnimations.cs +++ b/Content.Client/Animations/ReusableAnimations.cs @@ -37,7 +37,7 @@ namespace Content.Client.Animations new AnimationTrackComponentProperty { ComponentType = typeof(ITransformComponent), - Property = nameof(ITransformComponent.WorldPosition), + Property = nameof(ITransformComponent.LocalPosition), InterpolationMode = AnimationInterpolationMode.Linear, KeyFrames = { diff --git a/Content.Client/Atmos/Overlays/GasTileOverlay.cs b/Content.Client/Atmos/Overlays/GasTileOverlay.cs index 06b5c3bc4f..5c883d015d 100644 --- a/Content.Client/Atmos/Overlays/GasTileOverlay.cs +++ b/Content.Client/Atmos/Overlays/GasTileOverlay.cs @@ -40,9 +40,7 @@ namespace Content.Client.Atmos.Overlays if (!_gasTileOverlaySystem.HasData(mapGrid.Index)) continue; - var gridBounds = new Box2(mapGrid.WorldToLocal(worldBounds.BottomLeft), mapGrid.WorldToLocal(worldBounds.TopRight)); - - foreach (var tile in mapGrid.GetTilesIntersecting(gridBounds)) + foreach (var tile in mapGrid.GetTilesIntersecting(worldBounds)) { foreach (var (texture, color) in _gasTileOverlaySystem.GetOverlays(mapGrid.Index, tile.GridIndices)) { diff --git a/Content.Client/Atmos/Visualizers/EnabledAtmosDeviceVisualizer.cs b/Content.Client/Atmos/Visualizers/EnabledAtmosDeviceVisualizer.cs index b39fb9eed8..24af950e1e 100644 --- a/Content.Client/Atmos/Visualizers/EnabledAtmosDeviceVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/EnabledAtmosDeviceVisualizer.cs @@ -9,22 +9,13 @@ namespace Content.Client.Atmos.Visualizers [UsedImplicitly] public abstract class EnabledAtmosDeviceVisualizer : AppearanceVisualizer { + [DataField("disabledState")] + private string _disabledState = string.Empty; [DataField("enabledState")] private string _enabledState = string.Empty; protected abstract object LayerMap { get; } protected abstract Enum DataKey { get; } - public override void InitializeEntity(IEntity entity) - { - base.InitializeEntity(entity); - - if (!entity.TryGetComponent(out ISpriteComponent? sprite)) - return; - - sprite.LayerMapSet(LayerMap, sprite.AddLayerState(_enabledState)); - sprite.LayerSetVisible(LayerMap, false); - } - public override void OnChangeData(AppearanceComponent component) { base.OnChangeData(component); @@ -32,8 +23,8 @@ namespace Content.Client.Atmos.Visualizers if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return; - if(component.TryGetData(DataKey, out bool enabled)) - sprite.LayerSetVisible(LayerMap, enabled); + if(component.TryGetData(DataKey, out bool enabled) && sprite.LayerMapTryGet(LayerMap, out var layer)) + sprite.LayerSetState(layer, enabled ? _enabledState : _disabledState); } } } diff --git a/Content.Client/Atmos/Visualizers/GasFilterVisualizer.cs b/Content.Client/Atmos/Visualizers/GasFilterVisualizer.cs new file mode 100644 index 0000000000..5ad5468c42 --- /dev/null +++ b/Content.Client/Atmos/Visualizers/GasFilterVisualizer.cs @@ -0,0 +1,18 @@ +using System; +using Content.Shared.Atmos.Piping; +using JetBrains.Annotations; + +namespace Content.Client.Atmos.Visualizers +{ + [UsedImplicitly] + public class GasFilterVisualizer : EnabledAtmosDeviceVisualizer + { + protected override object LayerMap => Layers.Enabled; + protected override Enum DataKey => FilterVisuals.Enabled; + + enum Layers : byte + { + Enabled, + } + } +} diff --git a/Content.Client/Atmos/Visualizers/GasPortableVisualizer.cs b/Content.Client/Atmos/Visualizers/GasPortableVisualizer.cs index 07e62b5d59..77804494ce 100644 --- a/Content.Client/Atmos/Visualizers/GasPortableVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/GasPortableVisualizer.cs @@ -46,7 +46,7 @@ namespace Content.Client.Atmos.Visualizers } } - private enum Layers + private enum Layers : byte { ConnectedToPort, } diff --git a/Content.Client/Atmos/Visualizers/OutletInjectorVisualizer.cs b/Content.Client/Atmos/Visualizers/OutletInjectorVisualizer.cs index dc1681c226..ec23d7b503 100644 --- a/Content.Client/Atmos/Visualizers/OutletInjectorVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/OutletInjectorVisualizer.cs @@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers protected override object LayerMap => Layers.Enabled; protected override Enum DataKey => OutletInjectorVisuals.Enabled; - enum Layers + enum Layers : byte { Enabled, } diff --git a/Content.Client/Atmos/Visualizers/PassiveVentVisualizer.cs b/Content.Client/Atmos/Visualizers/PassiveVentVisualizer.cs index 01991d837b..d2d08244bc 100644 --- a/Content.Client/Atmos/Visualizers/PassiveVentVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/PassiveVentVisualizer.cs @@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers protected override object LayerMap => Layers.Enabled; protected override Enum DataKey => PassiveVentVisuals.Enabled; - enum Layers + enum Layers : byte { Enabled, } diff --git a/Content.Client/Atmos/Visualizers/PipeConnectorVisualizer.cs b/Content.Client/Atmos/Visualizers/PipeConnectorVisualizer.cs index 9f7e58d31b..36244dc23d 100644 --- a/Content.Client/Atmos/Visualizers/PipeConnectorVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/PipeConnectorVisualizer.cs @@ -1,6 +1,7 @@ using System; using Content.Shared.Atmos; using Content.Shared.Atmos.Piping; +using Content.Shared.SubFloor; using JetBrains.Annotations; using Robust.Client.GameObjects; using Robust.Client.Graphics; @@ -69,13 +70,16 @@ namespace Content.Client.Atmos.Visualizers if (!component.TryGetData(PipeVisuals.VisualState, out PipeVisualState state)) return; + if(!component.TryGetData(SubFloorVisuals.SubFloor, out bool subfloor)) + subfloor = true; + foreach (Layer layerKey in Enum.GetValues(typeof(Layer))) { var dir = (PipeDirection) layerKey; var layerVisible = state.ConnectedDirections.HasDirection(dir); var layer = sprite.LayerMapGet(layerKey); - sprite.LayerSetVisible(layer, layerVisible); + sprite.LayerSetVisible(layer, layerVisible && subfloor); sprite.LayerSetColor(layer, color); } } diff --git a/Content.Client/Atmos/Visualizers/PressurePumpVisualizer.cs b/Content.Client/Atmos/Visualizers/PressurePumpVisualizer.cs index c1441da37b..e0b5db06d3 100644 --- a/Content.Client/Atmos/Visualizers/PressurePumpVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/PressurePumpVisualizer.cs @@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers protected override object LayerMap => Layers.Enabled; protected override Enum DataKey => PressurePumpVisuals.Enabled; - enum Layers + enum Layers : byte { Enabled, } diff --git a/Content.Client/Atmos/Visualizers/ScrubberVisualizer.cs b/Content.Client/Atmos/Visualizers/ScrubberVisualizer.cs index 6568fab771..e58b0bf8e7 100644 --- a/Content.Client/Atmos/Visualizers/ScrubberVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/ScrubberVisualizer.cs @@ -44,7 +44,7 @@ namespace Content.Client.Atmos.Visualizers } } - public enum ScrubberVisualLayers + public enum ScrubberVisualLayers : byte { Scrubber, } diff --git a/Content.Client/Atmos/Visualizers/ThermoMachineVisualizer.cs b/Content.Client/Atmos/Visualizers/ThermoMachineVisualizer.cs index e9d77226cc..ab7db6ae92 100644 --- a/Content.Client/Atmos/Visualizers/ThermoMachineVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/ThermoMachineVisualizer.cs @@ -10,7 +10,7 @@ namespace Content.Client.Atmos.Visualizers protected override object LayerMap => Layers.Enabled; protected override Enum DataKey => ThermoMachineVisuals.Enabled; - enum Layers + enum Layers : byte { Enabled, } diff --git a/Content.Client/Atmos/Visualizers/VentPumpVisualizer.cs b/Content.Client/Atmos/Visualizers/VentPumpVisualizer.cs index 7db4bc7d5f..cecc544b57 100644 --- a/Content.Client/Atmos/Visualizers/VentPumpVisualizer.cs +++ b/Content.Client/Atmos/Visualizers/VentPumpVisualizer.cs @@ -40,7 +40,7 @@ namespace Content.Client.Atmos.Visualizers } } - public enum VentVisualLayers + public enum VentVisualLayers : byte { Vent, } diff --git a/Content.Client/CharacterAppearance/HumanoidAppearanceComponent.cs b/Content.Client/CharacterAppearance/HumanoidAppearanceComponent.cs index d4c0c3ce4e..0d55e206d9 100644 --- a/Content.Client/CharacterAppearance/HumanoidAppearanceComponent.cs +++ b/Content.Client/CharacterAppearance/HumanoidAppearanceComponent.cs @@ -1,4 +1,4 @@ -using Content.Client.Cuffs.Components; +using Content.Client.Cuffs.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Part; using Content.Shared.CharacterAppearance; @@ -120,15 +120,10 @@ namespace Content.Client.CharacterAppearance return; } - var layer = args.Part.ToHumanoidLayer(); - - if (layer == null) - { - return; - } - + var layers = args.Part.ToHumanoidLayers(); // TODO BODY Layer color, sprite and state - sprite.LayerSetVisible(layer, true); + foreach (var layer in layers) + sprite.LayerSetVisible(layer, true); } public void BodyPartRemoved(BodyPartRemovedEventArgs args) @@ -143,15 +138,10 @@ namespace Content.Client.CharacterAppearance return; } - var layer = args.Part.ToHumanoidLayer(); - - if (layer == null) - { - return; - } - + var layers = args.Part.ToHumanoidLayers(); // TODO BODY Layer color, sprite and state - sprite.LayerSetVisible(layer, false); + foreach (var layer in layers) + sprite.LayerSetVisible(layer, false); } } } diff --git a/Content.Client/Doors/AirlockVisualizer.cs b/Content.Client/Doors/AirlockVisualizer.cs index ef05e75125..eec56e8953 100644 --- a/Content.Client/Doors/AirlockVisualizer.cs +++ b/Content.Client/Doors/AirlockVisualizer.cs @@ -17,18 +17,26 @@ namespace Content.Client.Doors { private const string AnimationKey = "airlock_animation"; - [DataField("open_sound", required: true)] - private SoundSpecifier _openSound = default!; - - [DataField("close_sound", required: true)] - private SoundSpecifier _closeSound = default!; - - [DataField("deny_sound", required: true)] - private SoundSpecifier _denySound = default!; - - [DataField("animation_time")] + [DataField("animationTime")] private float _delay = 0.8f; + [DataField("denyAnimationTime")] + private float _denyDelay = 0.3f; + + /// + /// Whether the maintenance panel is animated or stays static. + /// False for windoors. + /// + [DataField("animatedPanel")] + private bool _animatedPanel = true; + + /// + /// Whether the BaseUnlit layer should still be visible when the airlock + /// is opened. + /// + [DataField("openUnlitVisible")] + private bool _openUnlitVisible = false; + private Animation CloseAnimation = default!; private Animation OpenAnimation = default!; private Animation DenyAnimation = default!; @@ -47,15 +55,13 @@ namespace Content.Client.Doors flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit; flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing_unlit", 0f)); - var flickMaintenancePanel = new AnimationTrackSpriteFlick(); - CloseAnimation.AnimationTracks.Add(flickMaintenancePanel); - flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel; - flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f)); - - var sound = new AnimationTrackPlaySound(); - CloseAnimation.AnimationTracks.Add(sound); - - sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_closeSound.GetSound(), 0)); + if (_animatedPanel) + { + var flickMaintenancePanel = new AnimationTrackSpriteFlick(); + CloseAnimation.AnimationTracks.Add(flickMaintenancePanel); + flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel; + flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f)); + } } OpenAnimation = new Animation {Length = TimeSpan.FromSeconds(_delay)}; @@ -70,28 +76,21 @@ namespace Content.Client.Doors flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit; flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening_unlit", 0f)); - var flickMaintenancePanel = new AnimationTrackSpriteFlick(); - OpenAnimation.AnimationTracks.Add(flickMaintenancePanel); - flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel; - flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_opening", 0f)); - - var sound = new AnimationTrackPlaySound(); - OpenAnimation.AnimationTracks.Add(sound); - - sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_openSound.GetSound(), 0)); + if (_animatedPanel) + { + var flickMaintenancePanel = new AnimationTrackSpriteFlick(); + OpenAnimation.AnimationTracks.Add(flickMaintenancePanel); + flickMaintenancePanel.LayerKey = WiresVisualizer.WiresVisualLayers.MaintenancePanel; + flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_opening", 0f)); + } } - DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(0.3f)}; + DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(_denyDelay)}; { var flick = new AnimationTrackSpriteFlick(); DenyAnimation.AnimationTracks.Add(flick); flick.LayerKey = DoorVisualLayers.BaseUnlit; flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("deny_unlit", 0f)); - - var sound = new AnimationTrackPlaySound(); - DenyAnimation.AnimationTracks.Add(sound); - - sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(_denySound.GetSound(), 0, () => AudioHelpers.WithVariation(0.05f))); } } @@ -126,13 +125,16 @@ namespace Content.Client.Doors { case DoorVisualState.Open: sprite.LayerSetState(DoorVisualLayers.Base, "open"); - unlitVisible = false; + unlitVisible = _openUnlitVisible; + if (_openUnlitVisible) + { + sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "open_unlit"); + } break; case DoorVisualState.Closed: sprite.LayerSetState(DoorVisualLayers.Base, "closed"); sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit"); sprite.LayerSetState(DoorVisualLayers.BaseBolted, "bolted_unlit"); - sprite.LayerSetState(WiresVisualizer.WiresVisualLayers.MaintenancePanel, "panel_open"); break; case DoorVisualState.Opening: animPlayer.Play(OpenAnimation, AnimationKey); diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index f5d976796d..388c56ae43 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -154,7 +154,8 @@ namespace Content.Client.Entry "GasPassiveGate", "GasValve", "GasThermoMachine", - "Metabolism", + "Respirator", + "Metabolizer", "AiFactionTag", "PressureProtection", "AMEPart", diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs index 5f2e40f2e5..255581effa 100644 --- a/Content.Client/Hands/Systems/HandsSystem.cs +++ b/Content.Client/Hands/Systems/HandsSystem.cs @@ -62,7 +62,7 @@ namespace Content.Client.Hands if (!_gameTiming.IsFirstTimePredicted) return; - ReusableAnimations.AnimateEntityPickup(entity, msg.InitialPosition, msg.PickupDirection); + ReusableAnimations.AnimateEntityPickup(entity, msg.InitialPosition, msg.FinalPosition); } public HandsGuiState GetGuiState() diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs index de4e4eb7b3..9b53311e91 100644 --- a/Content.Client/Physics/Controllers/MoverController.cs +++ b/Content.Client/Physics/Controllers/MoverController.cs @@ -19,7 +19,18 @@ namespace Content.Client.Physics.Controllers !player.TryGetComponent(out IMoverComponent? mover) || !player.TryGetComponent(out PhysicsComponent? body)) return; - body.Predict = true; // TODO: equal prediction instead of true? + // Essentially we only want to set our mob to predicted so every other entity we just interpolate + // (i.e. only see what the server has sent us). + // The exception to this is joints. + body.Predict = true; + + // We set joints to predicted given these can affect how our mob moves. + // I would only recommend disabling this if you make pulling not use joints anymore (someday maybe?) + foreach (var joint in body.Joints) + { + joint.BodyA.Predict = true; + joint.BodyB.Predict = true; + } // Server-side should just be handled on its own so we'll just do this shizznit if (player.TryGetComponent(out IMobMoverComponent? mobMover)) diff --git a/Content.Client/Storage/ClientStorageComponent.cs b/Content.Client/Storage/ClientStorageComponent.cs index c194b6d296..5f158365a6 100644 --- a/Content.Client/Storage/ClientStorageComponent.cs +++ b/Content.Client/Storage/ClientStorageComponent.cs @@ -120,7 +120,7 @@ namespace Content.Client.Storage if (Owner.EntityManager.TryGetEntity(entityId, out var entity)) { - ReusableAnimations.AnimateEntityPickup(entity, initialPosition, Owner.Transform.WorldPosition); + ReusableAnimations.AnimateEntityPickup(entity, initialPosition, Owner.Transform.LocalPosition); } } } diff --git a/Content.Client/SubFloor/SubFloorShowLayerVisualizer.cs b/Content.Client/SubFloor/SubFloorShowLayerVisualizer.cs new file mode 100644 index 0000000000..e16e7b22e8 --- /dev/null +++ b/Content.Client/SubFloor/SubFloorShowLayerVisualizer.cs @@ -0,0 +1,40 @@ +using Content.Shared.SubFloor; +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +namespace Content.Client.SubFloor +{ + [UsedImplicitly] + public class SubFloorShowLayerVisualizer : AppearanceVisualizer + { + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (!component.Owner.TryGetComponent(out SpriteComponent? sprite)) + return; + + if (component.TryGetData(SubFloorVisuals.SubFloor, out bool subfloor)) + { + sprite.Visible = true; + + // Due to the way this visualizer works, you might want to specify it before any other + // visualizer that hides/shows layers depending on certain conditions, such as PipeConnectorVisualizer. + foreach (var layer in sprite.AllLayers) + { + layer.Visible = subfloor; + } + + if (sprite.LayerMapTryGet(Layers.FirstLayer, out var firstLayer)) + { + sprite.LayerSetVisible(firstLayer, true); + } + } + } + + public enum Layers : byte + { + FirstLayer, + } + } +} diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs index 580984374b..d1ac590adf 100644 --- a/Content.IntegrationTests/Tests/Body/LungTest.cs +++ b/Content.IntegrationTests/Tests/Body/LungTest.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Content.Server.Atmos; using Content.Server.Body.Behavior; using Content.Server.Body.Circulatory; -using Content.Server.Metabolism; +using Content.Server.Body.Respiratory; using Content.Shared.Atmos; using Content.Shared.Body.Components; using NUnit.Framework; @@ -32,7 +32,7 @@ namespace Content.IntegrationTests.Tests.Body template: HumanoidTemplate preset: HumanPreset centerSlot: torso - - type: Metabolism + - type: Respirator metabolismHeat: 5000 radiatedHeat: 400 implicitHeatRegulation: 5000 @@ -148,7 +148,7 @@ namespace Content.IntegrationTests.Tests.Body MapId mapId; IMapGrid grid = null; - MetabolismComponent metabolism = null; + RespiratorComponent respirator = null; IEntity human = null; var testMapName = "Maps/Test/Breathing/3by3-20oxy-80nit.yml"; @@ -169,8 +169,8 @@ namespace Content.IntegrationTests.Tests.Body Assert.True(human.TryGetComponent(out SharedBodyComponent body)); Assert.True(body.HasMechanismBehavior()); - Assert.True(human.TryGetComponent(out metabolism)); - Assert.False(metabolism.Suffocating); + Assert.True(human.TryGetComponent(out respirator)); + Assert.False(respirator.Suffocating); }); var increment = 10; @@ -178,7 +178,7 @@ namespace Content.IntegrationTests.Tests.Body for (var tick = 0; tick < 600; tick += increment) { await server.WaitRunTicks(increment); - Assert.False(metabolism.Suffocating, $"Entity {human.Name} is suffocating on tick {tick}"); + Assert.False(respirator.Suffocating, $"Entity {human.Name} is suffocating on tick {tick}"); } await server.WaitIdleAsync(); diff --git a/Content.IntegrationTests/Tests/ClickableTest.cs b/Content.IntegrationTests/Tests/ClickableTest.cs index ed29ec9d5b..7ac1f71eef 100644 --- a/Content.IntegrationTests/Tests/ClickableTest.cs +++ b/Content.IntegrationTests/Tests/ClickableTest.cs @@ -1,11 +1,14 @@ using System; using System.Threading.Tasks; using Content.Client.Clickable; +using Content.Server.GameTicking; using NUnit.Framework; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Timing; namespace Content.IntegrationTests.Tests { @@ -57,12 +60,19 @@ 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) { + Vector2? worldPos = null; EntityUid entity = default; + var clientEntManager = _client.ResolveDependency(); + var serverEntManager = _server.ResolveDependency(); + var mapManager = _server.ResolveDependency(); + var gameTicker = _server.ResolveDependency().GetEntitySystem(); await _server.WaitPost(() => { - var entMgr = IoCManager.Resolve(); - var ent = entMgr.SpawnEntity(prototype, new MapCoordinates(0, 0, new MapId(1))); + var gridEnt = mapManager.GetGrid(gameTicker.DefaultGridId).GridEntityId; + worldPos = serverEntManager.GetEntity(gridEnt).Transform.WorldPosition; + + var ent = serverEntManager.SpawnEntity(prototype, new EntityCoordinates(gridEnt, 0f, 0f)); ent.Transform.LocalRotation = angle; ent.GetComponent().Scale = (scale, scale); entity = ent.Uid; @@ -75,17 +85,15 @@ namespace Content.IntegrationTests.Tests await _client.WaitPost(() => { - var entMgr = IoCManager.Resolve(); - var ent = entMgr.GetEntity(entity); + var ent = clientEntManager.GetEntity(entity); var clickable = ent.GetComponent(); - hit = clickable.CheckClick((clickPosX, clickPosY), out _, out _); + hit = clickable.CheckClick((clickPosX, clickPosY) + worldPos!.Value, out _, out _); }); await _server.WaitPost(() => { - var entMgr = IoCManager.Resolve(); - entMgr.DeleteEntity(entity); + serverEntManager.DeleteEntity(entity); }); return hit; diff --git a/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs b/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs index 08f8a411f5..b810b42e5d 100644 --- a/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs +++ b/Content.IntegrationTests/Tests/Utility/EntitySystemExtensionsTest.cs @@ -5,6 +5,7 @@ using Content.Shared.Spawning; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.Map; +using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Broadphase; @@ -44,7 +45,11 @@ namespace Content.IntegrationTests.Tests.Utility await server.WaitAssertion(() => { + var mapId = new MapId(1); var grid = sMapManager.GetGrid(new GridId(1)); + grid.SetTile(new Vector2i(0, 0), new Tile(1)); + var gridEnt = sEntityManager.GetEntity(grid.GridEntityId); + var gridPos = gridEnt.Transform.WorldPosition; var entityCoordinates = new EntityCoordinates(grid.GridEntityId, 0, 0); // Nothing blocking it, only entity is the grid @@ -52,8 +57,7 @@ namespace Content.IntegrationTests.Tests.Utility Assert.True(sEntityManager.TrySpawnIfUnobstructed(null, entityCoordinates, CollisionGroup.Impassable, out var entity)); Assert.NotNull(entity); - var mapId = new MapId(1); - var mapCoordinates = new MapCoordinates(0, 0, mapId); + var mapCoordinates = new MapCoordinates(gridPos.X, gridPos.Y, mapId); // Nothing blocking it, only entity is the grid Assert.NotNull(sEntityManager.SpawnIfUnobstructed(null, mapCoordinates, CollisionGroup.Impassable)); diff --git a/Content.Server/Administration/Commands/AGhost.cs b/Content.Server/Administration/Commands/AGhost.cs index aff615a193..db64864eb4 100644 --- a/Content.Server/Administration/Commands/AGhost.cs +++ b/Content.Server/Administration/Commands/AGhost.cs @@ -2,6 +2,7 @@ using Content.Server.Ghost.Components; using Content.Server.Players; using Content.Shared.Administration; +using Content.Shared.Ghost; using Robust.Server.Player; using Robust.Shared.Console; using Robust.Shared.GameObjects; @@ -55,7 +56,8 @@ namespace Content.Server.Administration.Commands mind.TransferTo(ghost); } - ghost.GetComponent().CanReturnToBody = canReturn; + var comp = ghost.GetComponent(); + EntitySystem.Get().SetCanReturnToBody(comp, canReturn); } } } diff --git a/Content.Server/Atmos/Commands/AddAtmosCommand.cs b/Content.Server/Atmos/Commands/AddAtmosCommand.cs index 4759998b1a..69c85997f5 100644 --- a/Content.Server/Atmos/Commands/AddAtmosCommand.cs +++ b/Content.Server/Atmos/Commands/AddAtmosCommand.cs @@ -47,7 +47,7 @@ namespace Content.Server.Atmos.Commands return; } - if (grid.HasComponent()) + if (grid.HasComponent()) { shell.WriteLine("Grid already has an atmosphere."); return; diff --git a/Content.Server/Atmos/Commands/AddUnsimulatedAtmosCommand.cs b/Content.Server/Atmos/Commands/AddUnsimulatedAtmosCommand.cs index 67541a5361..5fb659cc59 100644 --- a/Content.Server/Atmos/Commands/AddUnsimulatedAtmosCommand.cs +++ b/Content.Server/Atmos/Commands/AddUnsimulatedAtmosCommand.cs @@ -47,7 +47,7 @@ namespace Content.Server.Atmos.Commands return; } - if (grid.HasComponent()) + if (grid.HasComponent()) { shell.WriteLine("Grid already has an atmosphere."); return; diff --git a/Content.Server/Atmos/Components/AtmosExposedComponent.cs b/Content.Server/Atmos/Components/AtmosExposedComponent.cs index e742458f10..9be43d8067 100644 --- a/Content.Server/Atmos/Components/AtmosExposedComponent.cs +++ b/Content.Server/Atmos/Components/AtmosExposedComponent.cs @@ -23,23 +23,20 @@ namespace Content.Server.Atmos.Components [ViewVariables] [ComponentDependency] private readonly FlammableComponent? _flammableComponent = null; - public void Update(TileAtmosphere tile, float frameDelta, AtmosphereSystem atmosphereSystem) + public void Update(GasMixture air, float frameDelta, AtmosphereSystem atmosphereSystem) { if (_temperatureComponent != null) { - if (tile.Air != null) - { - var temperatureDelta = tile.Air.Temperature - _temperatureComponent.CurrentTemperature; - var tileHeatCapacity = atmosphereSystem.GetHeatCapacity(tile.Air); - var heat = temperatureDelta * (tileHeatCapacity * _temperatureComponent.HeatCapacity / (tileHeatCapacity + _temperatureComponent.HeatCapacity)); - _temperatureComponent.ReceiveHeat(heat); - } + var temperatureDelta = air.Temperature - _temperatureComponent.CurrentTemperature; + var tileHeatCapacity = atmosphereSystem.GetHeatCapacity(air); + var heat = temperatureDelta * (tileHeatCapacity * _temperatureComponent.HeatCapacity / (tileHeatCapacity + _temperatureComponent.HeatCapacity)); + _temperatureComponent.ReceiveHeat(heat); _temperatureComponent.Update(); } - _barotraumaComponent?.Update(tile.Air?.Pressure ?? 0); + _barotraumaComponent?.Update(air.Pressure); - _flammableComponent?.Update(tile); + _flammableComponent?.Update(air); } } } diff --git a/Content.Server/Atmos/Components/FlammableComponent.cs b/Content.Server/Atmos/Components/FlammableComponent.cs index 99aa2402f8..3cccaba238 100644 --- a/Content.Server/Atmos/Components/FlammableComponent.cs +++ b/Content.Server/Atmos/Components/FlammableComponent.cs @@ -63,7 +63,7 @@ namespace Content.Server.Atmos.Components UpdateAppearance(); } - public void Update(TileAtmosphere tile) + public void Update(GasMixture air) { // Slowly dry ourselves off if wet. if (FireStacks < 0) @@ -104,13 +104,13 @@ namespace Content.Server.Atmos.Components } // If we're in an oxygenless environment, put the fire out. - if (tile.Air?.GetMoles(Gas.Oxygen) < 1f) + if (air.GetMoles(Gas.Oxygen) < 1f) { Extinguish(); return; } - EntitySystem.Get().HotspotExpose(tile.GridIndex, tile.GridIndices, 700f, 50f, true); + EntitySystem.Get().HotspotExpose(Owner.Transform.Coordinates, 700f, 50f, true); var physics = Owner.GetComponent(); diff --git a/Content.Server/Atmos/Components/GasTankComponent.cs b/Content.Server/Atmos/Components/GasTankComponent.cs index bd1f2ac7b9..fcd1c0d46c 100644 --- a/Content.Server/Atmos/Components/GasTankComponent.cs +++ b/Content.Server/Atmos/Components/GasTankComponent.cs @@ -1,4 +1,3 @@ -#nullable disable warnings using System; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Respiratory; diff --git a/Content.Server/Atmos/Components/GridAtmosphereComponent.cs b/Content.Server/Atmos/Components/GridAtmosphereComponent.cs index 53670f8d05..76840c37c2 100644 --- a/Content.Server/Atmos/Components/GridAtmosphereComponent.cs +++ b/Content.Server/Atmos/Components/GridAtmosphereComponent.cs @@ -14,13 +14,14 @@ using Dependency = Robust.Shared.IoC.DependencyAttribute; namespace Content.Server.Atmos.Components { /// - /// This is our SSAir equivalent. + /// Internal Atmos class. Use to interact with atmos instead. /// - [ComponentReference(typeof(IGridAtmosphereComponent))] + [ComponentReference(typeof(IAtmosphereComponent))] [RegisterComponent, Serializable] - public class GridAtmosphereComponent : Component, IGridAtmosphereComponent, ISerializationHooks + public class GridAtmosphereComponent : Component, IAtmosphereComponent, ISerializationHooks { public override string Name => "GridAtmosphere"; + public virtual bool Simulated => true; [ViewVariables] diff --git a/Content.Server/Atmos/Components/IGridAtmosphereComponent.cs b/Content.Server/Atmos/Components/IAtmosphereComponent.cs similarity index 79% rename from Content.Server/Atmos/Components/IGridAtmosphereComponent.cs rename to Content.Server/Atmos/Components/IAtmosphereComponent.cs index 11b953b2fa..8e9bfb8abb 100644 --- a/Content.Server/Atmos/Components/IGridAtmosphereComponent.cs +++ b/Content.Server/Atmos/Components/IAtmosphereComponent.cs @@ -2,7 +2,7 @@ namespace Content.Server.Atmos.Components { - public interface IGridAtmosphereComponent : IComponent + public interface IAtmosphereComponent : IComponent { /// /// Whether this atmosphere is simulated or not. diff --git a/Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs b/Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs new file mode 100644 index 0000000000..3d42d24762 --- /dev/null +++ b/Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.Atmos.Components +{ + [RegisterComponent] + [ComponentReference(typeof(IAtmosphereComponent))] + public class SpaceAtmosphereComponent : Component, IAtmosphereComponent + { + public override string Name => "SpaceAtmosphere"; + + public bool Simulated => false; + } +} diff --git a/Content.Server/Atmos/Components/SpaceGridAtmosphereComponent.cs b/Content.Server/Atmos/Components/SpaceGridAtmosphereComponent.cs deleted file mode 100644 index 6fa75f774d..0000000000 --- a/Content.Server/Atmos/Components/SpaceGridAtmosphereComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Content.Shared.Atmos; -using Robust.Shared.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Maths; - -namespace Content.Server.Atmos.Components -{ - [RegisterComponent] - [ComponentReference(typeof(IGridAtmosphereComponent))] - public class SpaceGridAtmosphereComponent : UnsimulatedGridAtmosphereComponent - { - public override string Name => "SpaceGridAtmosphere"; - } -} diff --git a/Content.Server/Atmos/Components/UnsimulatedGridAtmosphereComponent.cs b/Content.Server/Atmos/Components/UnsimulatedGridAtmosphereComponent.cs index 806164e6c1..a01d98c1da 100644 --- a/Content.Server/Atmos/Components/UnsimulatedGridAtmosphereComponent.cs +++ b/Content.Server/Atmos/Components/UnsimulatedGridAtmosphereComponent.cs @@ -8,10 +8,9 @@ using Robust.Shared.Maths; namespace Content.Server.Atmos.Components { [RegisterComponent] - [ComponentReference(typeof(IGridAtmosphereComponent))] - [ComponentReference(typeof(GridAtmosphereComponent))] + [ComponentReference(typeof(IAtmosphereComponent))] [Serializable] - public class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent, IGridAtmosphereComponent + public class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent { public override string Name => "UnsimulatedGridAtmosphere"; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs index 661b5b169f..35e06f437d 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs @@ -117,6 +117,10 @@ namespace Content.Server.Atmos.EntitySystems /// All tile mixtures in a grid. public IEnumerable GetAllTileMixtures(GridId grid, bool invalidate = false) { + // Return an array with a single space gas mixture for invalid grids. + if (!grid.IsValid()) + return new []{ GasMixture.SpaceGas }; + if (!_mapManager.TryGetGrid(grid, out var mapGrid)) return Enumerable.Empty(); @@ -666,7 +670,7 @@ namespace Content.Server.Atmos.EntitySystems public GasMixture? GetTileMixture(EntityCoordinates coordinates, bool invalidate = false) { return TryGetGridAndTile(coordinates, out var tuple) - ? GetTileMixture(tuple.Value.Grid, tuple.Value.Tile, invalidate) : null; + ? GetTileMixture(tuple.Value.Grid, tuple.Value.Tile, invalidate) : GasMixture.SpaceGas; } /// @@ -678,6 +682,10 @@ namespace Content.Server.Atmos.EntitySystems /// The tile mixture, or null public GasMixture? GetTileMixture(GridId grid, Vector2i tile, bool invalidate = false) { + // Always return space gas mixtures for invalid grids (grid 0) + if (!grid.IsValid()) + return GasMixture.SpaceGas; + if (!_mapManager.TryGetGrid(grid, out var mapGrid)) return null; @@ -686,7 +694,7 @@ namespace Content.Server.Atmos.EntitySystems return GetTileMixture(gridAtmosphere, tile, invalidate); } - if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out SpaceGridAtmosphereComponent? spaceAtmosphere)) + if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out SpaceAtmosphereComponent? _)) { // Always return a new space gas mixture in this case. return GasMixture.SpaceGas; @@ -967,6 +975,10 @@ namespace Content.Server.Atmos.EntitySystems /// All adjacent tile gas mixtures to the tile in question public IEnumerable GetAdjacentTileMixtures(GridId grid, Vector2i tile, bool includeBlocked = false, bool invalidate = false) { + // For invalid grids, return an array with a single space gas mixture in it. + if (!grid.IsValid()) + return new []{ GasMixture.SpaceGas }; + if (!_mapManager.TryGetGrid(grid, out var mapGrid)) return Enumerable.Empty(); @@ -1374,7 +1386,7 @@ namespace Content.Server.Atmos.EntitySystems return false; if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere) - && gridAtmosphere.AtmosDevices.Contains(atmosDevice)) + && gridAtmosphere.AtmosDevices.Contains(atmosDevice)) { atmosDevice.JoinedGrid = null; gridAtmosphere.AtmosDevices.Remove(atmosDevice); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs index df78bc015d..445984d021 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs @@ -1,11 +1,8 @@ -#nullable disable warnings -#nullable enable annotations using Content.Server.Atmos.Components; using Content.Server.Atmos.Reactions; using Content.Server.Coordinates.Helpers; using Content.Shared.Atmos; using Content.Shared.Maps; -using Robust.Shared.Map; namespace Content.Server.Atmos.EntitySystems { diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs index 2629bb4d33..52f4e926f4 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -1,5 +1,3 @@ -#nullable disable warnings -#nullable enable annotations using Content.Server.Atmos.Components; using Content.Shared.Atmos; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs index 30b4df6473..6f4ed1bd48 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs @@ -1,16 +1,14 @@ -#nullable disable warnings -#nullable enable annotations using System; -using System.Buffers; using System.Collections.Generic; using Content.Server.Atmos.Components; -using Content.Server.Coordinates.Helpers; +using Content.Server.Doors.Components; using Content.Shared.Atmos; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Server.Atmos.EntitySystems { @@ -20,7 +18,7 @@ namespace Content.Server.Atmos.EntitySystems private readonly TileAtmosphereComparer _monstermosComparer = new(); - private readonly TileAtmosphere[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit]; + private readonly TileAtmosphere?[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit]; private readonly TileAtmosphere[] _equalizeGiverTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit]; private readonly TileAtmosphere[] _equalizeTakerTiles = new TileAtmosphere[Atmospherics.MonstermosTileLimit]; private readonly TileAtmosphere[] _equalizeQueue = new TileAtmosphere[Atmospherics.MonstermosTileLimit]; @@ -28,7 +26,7 @@ namespace Content.Server.Atmos.EntitySystems private readonly TileAtmosphere[] _depressurizeSpaceTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit]; private readonly TileAtmosphere[] _depressurizeProgressionOrder = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit * 2]; - public void EqualizePressureInZone(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum) + private void EqualizePressureInZone(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum) { if (tile.Air == null || (tile.MonstermosInfo.LastCycle >= cycleNum)) return; // Already done. @@ -65,11 +63,12 @@ namespace Content.Server.Atmos.EntitySystems for (var i = 0; i < tileCount; i++) { if (i > Atmospherics.MonstermosHardTileLimit) break; - var exploring = _equalizeTiles[i]; + var exploring = _equalizeTiles[i]!; if (i < Atmospherics.MonstermosTileLimit) { - var tileMoles = exploring.Air.TotalMoles; + // Tiles in the _equalizeTiles array cannot have null air. + var tileMoles = exploring.Air!.TotalMoles; exploring.MonstermosInfo.MoleDelta = tileMoles; totalMoles += tileMoles; } @@ -106,7 +105,7 @@ namespace Content.Server.Atmos.EntitySystems if (otherTile == null) continue; - _equalizeTiles[i].MonstermosInfo.LastQueueCycle = 0; + otherTile.MonstermosInfo.LastQueueCycle = 0; } tileCount = Atmospherics.MonstermosTileLimit; @@ -118,7 +117,7 @@ namespace Content.Server.Atmos.EntitySystems for (var i = 0; i < tileCount; i++) { - var otherTile = _equalizeTiles[i]; + var otherTile = _equalizeTiles[i]!; otherTile.MonstermosInfo.LastCycle = cycleNum; otherTile.MonstermosInfo.MoleDelta -= averageMoles; if (otherTile.MonstermosInfo.MoleDelta > 0) @@ -133,7 +132,7 @@ namespace Content.Server.Atmos.EntitySystems var logN = MathF.Log2(tileCount); - // Optimization - try to spread gases using an O(nlogn) algorithm that has a chance of not working first to avoid O(n^2) + // Optimization - try to spread gases using an O(n log n) algorithm that has a chance of not working first to avoid O(n^2) if (giverTilesLength > logN && takerTilesLength > logN) { // Even if it fails, it will speed up the next part. @@ -141,7 +140,7 @@ namespace Content.Server.Atmos.EntitySystems for (var i = 0; i < tileCount; i++) { - var otherTile = _equalizeTiles[i]; + var otherTile = _equalizeTiles[i]!; otherTile.MonstermosInfo.FastDone = true; if (!(otherTile.MonstermosInfo.MoleDelta > 0)) continue; var eligibleDirections = AtmosDirection.Invalid; @@ -150,7 +149,7 @@ namespace Content.Server.Atmos.EntitySystems { var direction = (AtmosDirection) (1 << j); if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; - var tile2 = otherTile.AdjacentTiles[j]; + var tile2 = otherTile.AdjacentTiles[j]!; // skip anything that isn't part of our current processing block. if (tile2.MonstermosInfo.FastDone || tile2.MonstermosInfo.LastQueueCycle != queueCycle) @@ -171,7 +170,7 @@ namespace Content.Server.Atmos.EntitySystems AdjustEqMovement(otherTile, direction, molesToMove); otherTile.MonstermosInfo.MoleDelta -= molesToMove; - otherTile.AdjacentTiles[j].MonstermosInfo.MoleDelta += molesToMove; + otherTile.AdjacentTiles[j]!.MonstermosInfo.MoleDelta += molesToMove; } } @@ -180,7 +179,7 @@ namespace Content.Server.Atmos.EntitySystems for (var i = 0; i < tileCount; i++) { - var otherTile = _equalizeTiles[i]; + var otherTile = _equalizeTiles[i]!; if (otherTile.MonstermosInfo.MoleDelta > 0) { _equalizeGiverTiles[giverTilesLength++] = otherTile; @@ -252,7 +251,7 @@ namespace Content.Server.Atmos.EntitySystems if (otherTile.MonstermosInfo.CurrentTransferAmount != 0 && otherTile.MonstermosInfo.CurrentTransferDirection != AtmosDirection.Invalid) { AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount); - otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()] + otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()]! .MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount; otherTile.MonstermosInfo.CurrentTransferAmount = 0; } @@ -319,7 +318,7 @@ namespace Content.Server.Atmos.EntitySystems AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount); - otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()] + otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()]! .MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount; otherTile.MonstermosInfo.CurrentTransferAmount = 0; } @@ -328,19 +327,19 @@ namespace Content.Server.Atmos.EntitySystems for (var i = 0; i < tileCount; i++) { - var otherTile = _equalizeTiles[i]; + var otherTile = _equalizeTiles[i]!; FinalizeEq(gridAtmosphere, otherTile); } for (var i = 0; i < tileCount; i++) { - var otherTile = _equalizeTiles[i]; + var otherTile = _equalizeTiles[i]!; for (var j = 0; j < Atmospherics.Directions; j++) { var direction = (AtmosDirection) (1 << j); if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; - var otherTile2 = otherTile.AdjacentTiles[j]; - if (otherTile2?.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue; + var otherTile2 = otherTile.AdjacentTiles[j]!; + if (otherTile2.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue; AddActiveTile(gridAtmosphere, otherTile2); break; } @@ -353,7 +352,7 @@ namespace Content.Server.Atmos.EntitySystems Array.Clear(_equalizeQueue, 0, Atmospherics.MonstermosTileLimit); } - public void ExplosivelyDepressurize(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum) + private void ExplosivelyDepressurize(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum) { // Check if explosive depressurization is enabled and if the tile is valid. if (!MonstermosDepressurization || tile.Air == null) @@ -376,7 +375,8 @@ namespace Content.Server.Atmos.EntitySystems var otherTile = _depressurizeTiles[i]; otherTile.MonstermosInfo.LastCycle = cycleNum; otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; - if (otherTile.Air.Immutable) + // Tiles in the _depressurizeTiles array cannot have null air. + if (otherTile.Air!.Immutable) { _depressurizeSpaceTiles[spaceTileCount++] = otherTile; otherTile.PressureSpecificTarget = otherTile; @@ -388,7 +388,7 @@ namespace Content.Server.Atmos.EntitySystems var direction = (AtmosDirection) (1 << j); if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; var otherTile2 = otherTile.AdjacentTiles[j]; - if (otherTile2.Air == null) continue; + if (otherTile2?.Air == null) continue; if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue; ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2); @@ -421,8 +421,8 @@ namespace Content.Server.Atmos.EntitySystems for (var j = 0; j < Atmospherics.Directions; j++) { var direction = (AtmosDirection) (1 << j); - // TODO ATMOS This is a terrible hack that accounts for the mess that are space TileAtmospheres. - if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air.Immutable) continue; + // Tiles in _depressurizeProgressionOrder cannot have null air. + if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air!.Immutable) continue; var tile2 = otherTile.AdjacentTiles[j]; if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue; if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; @@ -509,7 +509,7 @@ namespace Content.Server.Atmos.EntitySystems InvalidateVisuals(other.GridIndex, other.GridIndices); } - public void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) + private void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) { Span transferDirections = stackalloc float[Atmospherics.Directions]; var hasTransferDirs = false; @@ -533,7 +533,8 @@ namespace Content.Server.Atmos.EntitySystems if (otherTile?.Air == null) continue; if (amount > 0) { - if (tile.Air.TotalMoles < amount) + // Everything that calls this method already ensures that Air will not be null. + if (tile.Air!.TotalMoles < amount) FinalizeEqNeighbors(gridAtmosphere, tile, transferDirections); otherTile.MonstermosInfo[direction.GetOpposite()] = 0; @@ -551,15 +552,19 @@ namespace Content.Server.Atmos.EntitySystems { var direction = (AtmosDirection) (1 << i); var amount = transferDirs[i]; + // Since AdjacentBits is set, AdjacentTiles[i] wouldn't be null, and neither would its air. if(amount < 0 && tile.AdjacentBits.IsFlagSet(direction)) - FinalizeEq(gridAtmosphere, tile.AdjacentTiles[i]); // A bit of recursion if needed. + FinalizeEq(gridAtmosphere, tile.AdjacentTiles[i]!); // A bit of recursion if needed. } } private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, float amount) { + DebugTools.Assert(tile.AdjacentBits.HasFlag(direction)); + DebugTools.Assert(tile.AdjacentTiles[direction.ToIndex()] != null); tile.MonstermosInfo[direction] += amount; - tile.AdjacentTiles[direction.ToIndex()].MonstermosInfo[direction.GetOpposite()] -= amount; + // Every call to this method already ensures that the adjacent tile won't be null. + tile.AdjacentTiles[direction.ToIndex()]!.MonstermosInfo[direction.GetOpposite()] -= amount; } private void HandleDecompressionFloorRip(IMapGrid mapGrid, TileAtmosphere tile, float sum) @@ -573,9 +578,9 @@ namespace Content.Server.Atmos.EntitySystems PryTile(mapGrid, tile.GridIndices); } - private class TileAtmosphereComparer : IComparer + private class TileAtmosphereComparer : IComparer { - public int Compare(TileAtmosphere a, TileAtmosphere b) + public int Compare(TileAtmosphere? a, TileAtmosphere? b) { if (a == null && b == null) return 0; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs index db87ed7c10..e02f81e0ca 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs @@ -14,6 +14,7 @@ namespace Content.Server.Atmos.EntitySystems { [Dependency] private readonly IGameTiming _gameTiming = default!; + private readonly AtmosDeviceUpdateEvent _updateEvent = new(); private readonly Stopwatch _simulationStopwatch = new(); /// @@ -204,11 +205,10 @@ namespace Content.Server.Atmos.EntitySystems atmosphere.CurrentRunAtmosDevices = new Queue(atmosphere.AtmosDevices); var time = _gameTiming.CurTime; - var updateEvent = new AtmosDeviceUpdateEvent(); var number = 0; while (atmosphere.CurrentRunAtmosDevices.TryDequeue(out var device)) { - EntityManager.EventBus.RaiseLocalEvent(device.Owner.Uid, updateEvent, false); + RaiseLocalEvent(device.Owner.Uid, _updateEvent, false); device.LastProcess = time; if (number++ < LagCheckIterations) continue; @@ -241,7 +241,7 @@ namespace Content.Server.Atmos.EntitySystems { var atmosphere = _currentRunAtmosphere[_currentRunAtmosphereIndex]; - if (atmosphere.Paused || atmosphere.LifeStage >= ComponentLifeStage.Stopping) + if (atmosphere.Paused || !atmosphere.Simulated || atmosphere.LifeStage >= ComponentLifeStage.Stopping) continue; atmosphere.Timer += frameTime; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs index 910ae05a51..966a3192f5 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs @@ -8,6 +8,9 @@ using Robust.Shared.Map; namespace Content.Server.Atmos.EntitySystems { + /// + /// This is our SSAir equivalent, if you need to interact with or query atmos in any way, go through this. + /// [UsedImplicitly] public partial class AtmosphereSystem : SharedAtmosphereSystem { @@ -29,7 +32,6 @@ namespace Content.Server.Atmos.EntitySystems #region Events // Map events. - _mapManager.MapCreated += OnMapCreated; _mapManager.TileChanged += OnTileChanged; #endregion @@ -39,7 +41,6 @@ namespace Content.Server.Atmos.EntitySystems { base.Shutdown(); - _mapManager.MapCreated -= OnMapCreated; _mapManager.TileChanged -= OnTileChanged; } @@ -57,17 +58,6 @@ namespace Content.Server.Atmos.EntitySystems InvalidateTile(eventArgs.NewTile.GridIndex, eventArgs.NewTile.GridIndices); } - private void OnMapCreated(object? sender, MapEventArgs e) - { - if (e.Map == MapId.Nullspace) - return; - - var map = _mapManager.GetMapEntity(e.Map); - - if (!map.HasComponent()) - map.AddComponent(); - } - public override void Update(float frameTime) { base.Update(frameTime); @@ -81,7 +71,7 @@ namespace Content.Server.Atmos.EntitySystems foreach (var exposed in EntityManager.ComponentManager.EntityQuery()) { // TODO ATMOS: Kill this with fire. - var tile = GetTileAtmosphereOrCreateSpace(exposed.Owner.Transform.Coordinates); + var tile = GetTileMixture(exposed.Owner.Transform.Coordinates); if (tile == null) continue; exposed.Update(tile, _exposedTimer, this); } diff --git a/Content.Server/Atmos/IGasMixtureHolder.cs b/Content.Server/Atmos/IGasMixtureHolder.cs index c8bcb60391..e7a55e8048 100644 --- a/Content.Server/Atmos/IGasMixtureHolder.cs +++ b/Content.Server/Atmos/IGasMixtureHolder.cs @@ -6,20 +6,5 @@ namespace Content.Server.Atmos public interface IGasMixtureHolder { public GasMixture Air { get; set; } - - public virtual void AssumeAir(GasMixture giver) - { - EntitySystem.Get().Merge(Air, giver); - } - - public GasMixture RemoveAir(float amount) - { - return Air.Remove(amount); - } - - public GasMixture RemoveAirVolume(float ratio) - { - return Air.RemoveRatio(ratio); - } } } diff --git a/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs b/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs index 9b535e7e01..00c36df4e0 100644 --- a/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs +++ b/Content.Server/Atmos/Piping/Components/AtmosDeviceComponent.cs @@ -8,7 +8,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.Atmos.Piping.Components { /// - /// Adds itself to a to be updated by. + /// Adds itself to a to be updated by. /// [RegisterComponent] public class AtmosDeviceComponent : Component @@ -22,6 +22,19 @@ namespace Content.Server.Atmos.Piping.Components [DataField("requireAnchored")] public bool RequireAnchored { get; private set; } = true; + /// + /// Whether this device will join an entity system to process when not in a grid. + /// + [ViewVariables] + [DataField("joinSystem")] + public bool JoinSystem { get; } = false; + + /// + /// Whether we have joined an entity system to process. + /// + [ViewVariables] + public bool JoinedSystem { get; set; } = false; + [ViewVariables] public TimeSpan LastProcess { get; set; } = TimeSpan.Zero; diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs index 166a4d07b8..12d98e8ff2 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs @@ -1,10 +1,10 @@ using System; +using System.Collections.Generic; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Components; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.IoC; -using Robust.Shared.Physics; using Robust.Shared.Timing; namespace Content.Server.Atmos.Piping.EntitySystems @@ -12,9 +12,14 @@ namespace Content.Server.Atmos.Piping.EntitySystems [UsedImplicitly] public class AtmosDeviceSystem : EntitySystem { - [Dependency] private IGameTiming _gameTiming = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + private readonly AtmosDeviceUpdateEvent _updateEvent = new(); + + private float _timer = 0f; + private readonly HashSet _joinedDevices = new(); + public override void Initialize() { base.Initialize(); @@ -33,11 +38,24 @@ namespace Content.Server.Atmos.Piping.EntitySystems public void JoinAtmosphere(AtmosDeviceComponent component) { if (!CanJoinAtmosphere(component)) + { return; + } - // We try to add the device to a valid atmosphere. + // We try to add the device to a valid atmosphere, and if we can't, try to add it to the entity system. if (!_atmosphereSystem.AddAtmosDevice(component)) - return; + { + if (component.JoinSystem) + { + _joinedDevices.Add(component); + component.JoinedSystem = true; + } + else + { + return; + } + } + component.LastProcess = _gameTiming.CurTime; @@ -46,8 +64,19 @@ namespace Content.Server.Atmos.Piping.EntitySystems public void LeaveAtmosphere(AtmosDeviceComponent component) { - if (!_atmosphereSystem.RemoveAtmosDevice(component)) + // Try to remove the component from an atmosphere, and if not + if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component)) + { + // The grid might have been removed but not us... This usually shouldn't happen. + component.JoinedGrid = null; return; + } + + if (component.JoinedSystem) + { + _joinedDevices.Remove(component); + component.JoinedSystem = false; + } component.LastProcess = TimeSpan.Zero; RaiseLocalEvent(component.Owner.Uid, new AtmosDeviceDisabledEvent(), false); @@ -85,5 +114,22 @@ namespace Content.Server.Atmos.Piping.EntitySystems { RejoinAtmosphere(component); } + + public override void Update(float frameTime) + { + _timer += frameTime; + + if (_timer < _atmosphereSystem.AtmosTime) + return; + + _timer -= _atmosphereSystem.AtmosTime; + + var time = _gameTiming.CurTime; + foreach (var device in _joinedDevices) + { + RaiseLocalEvent(device.Owner.Uid, _updateEvent, false); + device.LastProcess = time; + } + } } } diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index a2db87f7bc..2d24c80fdf 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -3,7 +3,9 @@ using Content.Server.Atmos.Piping.Trinary.Components; using Content.Server.NodeContainer; using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos; +using Content.Shared.Atmos.Piping; using JetBrains.Annotations; +using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Timing; @@ -24,33 +26,35 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems private void OnFilterUpdated(EntityUid uid, GasFilterComponent filter, AtmosDeviceUpdateEvent args) { - if (!filter.Enabled) - return; + var appearance = filter.Owner.GetComponentOrNull(); - if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) + if (!filter.Enabled + || !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) + || !ComponentManager.TryGetComponent(uid, out AtmosDeviceComponent? device) + || !nodeContainer.TryGetNode(filter.InletName, out PipeNode? inletNode) + || !nodeContainer.TryGetNode(filter.FilterName, out PipeNode? filterNode) + || !nodeContainer.TryGetNode(filter.OutletName, out PipeNode? outletNode) + || outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure) // No need to transfer if target is full. + { + appearance?.SetData(FilterVisuals.Enabled, false); return; - - if (!ComponentManager.TryGetComponent(uid, out AtmosDeviceComponent? device)) - return; - - if (!nodeContainer.TryGetNode(filter.InletName, out PipeNode? inletNode) - || !nodeContainer.TryGetNode(filter.FilterName, out PipeNode? filterNode) - || !nodeContainer.TryGetNode(filter.OutletName, out PipeNode? outletNode)) - return; - - if (outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure) - return; // No need to transfer if target is full. + } // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. var transferRatio = (float)(filter.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds) / inletNode.Air.Volume; if (transferRatio <= 0) + { + appearance?.SetData(FilterVisuals.Enabled, false); return; + } var removed = inletNode.Air.RemoveRatio(transferRatio); if (filter.FilteredGas.HasValue) { + appearance?.SetData(FilterVisuals.Enabled, true); + var filteredOut = new GasMixture() {Temperature = removed.Temperature}; filteredOut.SetMoles(filter.FilteredGas.Value, removed.GetMoles(filter.FilteredGas.Value)); diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs index b90ced4c02..49447e012a 100644 --- a/Content.Server/Atmos/TileAtmosphere.cs +++ b/Content.Server/Atmos/TileAtmosphere.cs @@ -1,5 +1,4 @@ -#nullable disable warnings -#nullable enable annotations +using Content.Server.Atmos.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Maps; using Robust.Shared.Map; @@ -10,6 +9,7 @@ namespace Content.Server.Atmos { /// /// Internal Atmos class that stores data about the atmosphere in a grid. + /// You shouldn't use this directly, use instead. /// public class TileAtmosphere : IGasMixtureHolder { @@ -71,6 +71,12 @@ namespace Content.Server.Atmos [ViewVariables] public GasMixture? Air { get; set; } + GasMixture IGasMixtureHolder.Air + { + get => Air ?? new GasMixture(Atmospherics.CellVolume){ Temperature = Temperature }; + set => Air = value; + } + [ViewVariables] public float MaxFireTemperatureSustained { get; set; } diff --git a/Content.Server/Body/Behavior/LiverBehavior.cs b/Content.Server/Body/Behavior/LiverBehavior.cs index 95897b320a..aa40d1b142 100644 --- a/Content.Server/Body/Behavior/LiverBehavior.cs +++ b/Content.Server/Body/Behavior/LiverBehavior.cs @@ -1,7 +1,6 @@ using System.Linq; using Content.Server.Body.Circulatory; using Content.Shared.Body.Networks; -using Content.Shared.Chemistry.Metabolizable; using Content.Shared.Chemistry.Reagent; using Robust.Shared.IoC; using Robust.Shared.Prototypes; @@ -13,52 +12,8 @@ namespace Content.Server.Body.Behavior /// public class LiverBehavior : MechanismBehavior { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - private float _accumulatedFrameTime; - /// - /// Delay time that determines how often to metabolise blood contents (in seconds). - /// - private float _updateIntervalSeconds = 1.0f; - - /// - /// Whether the liver is functional. - /// - //[ViewVariables] private bool _liverFailing = false; - - /// - /// Modifier for alcohol damage. - /// - //[DataField("alcoholLethality")] - //[ViewVariables] private float _alcoholLethality = 0.005f; - - /// - /// Modifier for alcohol damage. - /// - //[DataField("alcoholExponent")] - //[ViewVariables] private float _alcoholExponent = 1.6f; - - /// - /// Toxin volume that can be purged without damage. - /// - //[DataField("toxinTolerance")] - //[ViewVariables] private float _toxinTolerance = 3f; - - /// - /// Toxin damage modifier. - /// - //[DataField("toxinLethality")] - //[ViewVariables] private float _toxinLethality = 0.01f; - - /// - /// Loops through each reagent in _internalSolution, - /// and calls for each of them. - /// Also handles toxins and alcohol. - /// - /// - /// The time since the last update in seconds. - /// public override void Update(float frameTime) { if (Body == null) @@ -68,51 +23,13 @@ namespace Content.Server.Body.Behavior _accumulatedFrameTime += frameTime; - // Update at most once every _updateIntervalSeconds - if (_accumulatedFrameTime < _updateIntervalSeconds) + // Update at most once per second + if (_accumulatedFrameTime < 1) { return; } - _accumulatedFrameTime -= _updateIntervalSeconds; - - if (!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream)) - { - return; - } - - if (bloodstream.Solution.CurrentVolume <= ReagentUnit.Zero) - { - return; - } - - // Run metabolism for each reagent, remove metabolized reagents - // Using ToList here lets us edit reagents while iterating - foreach (var reagent in bloodstream.Solution.ReagentList.ToList()) - { - if (!_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype? prototype)) - { - continue; - } - - // How much reagent is available to metabolise? - // This needs to be passed to other functions that have metabolism rate information, such that they don't "overmetabolise" a reagent. - var availableReagent = bloodstream.Solution.Solution.GetReagentQuantity(reagent.ReagentId); - - //TODO BODY Check if it's a Toxin. If volume < _toxinTolerance, just remove it. If greater, add damage = volume * _toxinLethality - //TODO BODY Check if it has BoozePower > 0. Affect drunkenness, apply damage. Proposed formula (SS13-derived): damage = sqrt(volume) * BoozePower^_alcoholExponent * _alcoholLethality / 10 - //TODO BODY Liver failure. - - //TODO Make sure reagent prototypes actually have the toxin and boozepower vars set. - - // Run metabolism code for each reagent - foreach (var metabolizable in prototype.Metabolism) - { - var reagentDelta = metabolizable.Metabolize(Body.Owner, reagent.ReagentId, _updateIntervalSeconds, availableReagent); - bloodstream.Solution.TryRemoveReagent(reagent.ReagentId, reagentDelta); - availableReagent -= reagentDelta; - } - } + _accumulatedFrameTime -= 1; } } } diff --git a/Content.Server/Body/Circulatory/BloodstreamComponent.cs b/Content.Server/Body/Circulatory/BloodstreamComponent.cs index df90cd6fc6..998e3d97bb 100644 --- a/Content.Server/Body/Circulatory/BloodstreamComponent.cs +++ b/Content.Server/Body/Circulatory/BloodstreamComponent.cs @@ -2,8 +2,8 @@ using System; using System.Linq; using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; +using Content.Server.Body.Respiratory; using Content.Server.Chemistry.Components; -using Content.Server.Metabolism; using Content.Shared.Atmos; using Content.Shared.Body.Networks; using Content.Shared.Chemistry.Reagent; @@ -72,7 +72,7 @@ namespace Content.Server.Body.Circulatory { var atmosphereSystem = EntitySystem.Get(); - if (!Owner.TryGetComponent(out MetabolismComponent? metabolism)) + if (!Owner.TryGetComponent(out RespiratorComponent? metabolism)) { atmosphereSystem.Merge(to, Air); Air.Clear(); diff --git a/Content.Server/Body/Metabolism/MetabolizerComponent.cs b/Content.Server/Body/Metabolism/MetabolizerComponent.cs new file mode 100644 index 0000000000..b74926ff57 --- /dev/null +++ b/Content.Server/Body/Metabolism/MetabolizerComponent.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; + +namespace Content.Server.Body.Metabolism +{ + /// + /// Handles metabolizing various reagents with given effects. + /// + [RegisterComponent] + public class MetabolizerComponent : Component + { + public override string Name => "Metabolizer"; + + public float AccumulatedFrametime = 0.0f; + + /// + /// How often to metabolize reagents, in seconds. + /// + /// + [DataField("updateFrequency")] + public float UpdateFrequency = 1.0f; + + /// + /// Whether this metabolizer should attempt to metabolize chemicals in its parent bodies' bloodstream, + /// as opposed to a solution container on the metabolizing entity itself. + /// + [DataField("takeFromBloodstream")] + public bool TakeFromBloodstream = true; + + /// + /// A dictionary mapping reagent string IDs to a list of effects & associated metabolism rate. + /// + /// + [DataField("metabolisms", required: true, customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] + public Dictionary Metabolisms = default!; + } + + [DataDefinition] + public class ReagentEffectsEntry + { + /// + /// Amount of reagent to metabolize, per metabolism cycle. + /// + [DataField("metabolismRate")] + public ReagentUnit MetabolismRate = ReagentUnit.New(1.0f); + + /// + /// A list of effects to apply when these reagents are metabolized. + /// + [DataField("effects", required: true)] + public ReagentEffect[] Effects = default!; + } +} diff --git a/Content.Server/Body/Metabolism/MetabolizerSystem.cs b/Content.Server/Body/Metabolism/MetabolizerSystem.cs new file mode 100644 index 0000000000..249ca13391 --- /dev/null +++ b/Content.Server/Body/Metabolism/MetabolizerSystem.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Server.Body.Circulatory; +using Content.Server.Chemistry.Components; +using Content.Shared.Body.Components; +using Content.Shared.Body.Mechanism; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Chemistry.Solution; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; + +namespace Content.Server.Body.Metabolism +{ + // TODO mirror in the future working on mechanisms move updating here to BodySystem so it can be ordered? + public class MetabolizerSystem : EntitySystem + { + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var metab in ComponentManager.EntityQuery(false)) + { + metab.AccumulatedFrametime += frameTime; + + // Only update as frequently as it should + if (metab.AccumulatedFrametime >= metab.UpdateFrequency) + { + metab.AccumulatedFrametime = 0.0f; + TryMetabolize(metab); + } + } + } + + private void TryMetabolize(MetabolizerComponent comp) + { + var owner = comp.Owner; + var reagentList = new List(); + SolutionContainerComponent? solution = null; + SharedBodyComponent? body = null; + + // if this field is passed we should try and take from the bloodstream over anything else + if (comp.TakeFromBloodstream && owner.TryGetComponent(out var mech)) + { + body = mech.Body; + if (body != null) + { + if (body.Owner.TryGetComponent(out var bloodstream) + && bloodstream.Solution.CurrentVolume >= ReagentUnit.Zero) + { + solution = bloodstream.Solution; + reagentList = bloodstream.Solution.ReagentList.ToList(); + } + } + } + else if (owner.TryGetComponent(out var sol)) + { + // if we have no mechanism/body but a solution container instead, + // we'll just use that to metabolize from + solution = sol; + reagentList = sol.ReagentList.ToList(); + } + if (solution == null || reagentList.Count == 0) + { + // We're all outta ideas on where to metabolize from + return; + } + + // Run metabolism for each reagent, remove metabolized reagents + foreach (var reagent in reagentList) + { + if (!comp.Metabolisms.ContainsKey(reagent.ReagentId)) + continue; + + var metabolism = comp.Metabolisms[reagent.ReagentId]; + // Run metabolism code for each reagent + foreach (var effect in metabolism.Effects) + { + var ent = body != null ? body.Owner : owner; + var conditionsMet = true; + if (effect.Conditions != null) + { + // yes this is 3 nested for loops, but all of these lists are + // basically guaranteed to be small or empty + foreach (var condition in effect.Conditions) + { + if (!condition.Condition(ent, reagent)) + { + conditionsMet = false; + break; + } + } + } + + if (!conditionsMet) + return; + + // If we're part of a body, pass that entity to Metabolize + // Otherwise, just pass our owner entity, maybe we're a plant or something + effect.Metabolize(ent, reagent); + } + + solution.TryRemoveReagent(reagent.ReagentId, metabolism.MetabolismRate); + } + } + } +} diff --git a/Content.Server/Metabolism/MetabolismComponent.cs b/Content.Server/Body/Respiratory/RespiratorComponent.cs similarity index 98% rename from Content.Server/Metabolism/MetabolismComponent.cs rename to Content.Server/Body/Respiratory/RespiratorComponent.cs index 4898692ab9..2aec76e351 100644 --- a/Content.Server/Metabolism/MetabolismComponent.cs +++ b/Content.Server/Body/Respiratory/RespiratorComponent.cs @@ -13,7 +13,6 @@ using Content.Shared.Atmos; using Content.Shared.Body.Components; using Content.Shared.Damage; using Content.Shared.Damage.Components; -using Content.Shared.Metabolism.Events; using Content.Shared.MobState; using Content.Shared.Notification.Managers; using Robust.Shared.GameObjects; @@ -21,14 +20,14 @@ using Robust.Shared.Localization; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; -namespace Content.Server.Metabolism +namespace Content.Server.Body.Respiratory { [RegisterComponent] - public class MetabolismComponent : Component + public class RespiratorComponent : Component { [ComponentDependency] private readonly SharedBodyComponent? _body = default!; - public override string Name => "Metabolism"; + public override string Name => "Respirator"; private float _accumulatedFrameTime; diff --git a/Content.Server/Metabolism/MetabolismSystem.cs b/Content.Server/Body/Respiratory/RespiratorSystem.cs similarity index 50% rename from Content.Server/Metabolism/MetabolismSystem.cs rename to Content.Server/Body/Respiratory/RespiratorSystem.cs index 6616d013d5..424b1e4dce 100644 --- a/Content.Server/Metabolism/MetabolismSystem.cs +++ b/Content.Server/Body/Respiratory/RespiratorSystem.cs @@ -1,18 +1,18 @@ using JetBrains.Annotations; using Robust.Shared.GameObjects; -namespace Content.Server.Metabolism +namespace Content.Server.Body.Respiratory { [UsedImplicitly] - public class MetabolismSystem : EntitySystem + public class RespiratorSystem : EntitySystem { public override void Update(float frameTime) { base.Update(frameTime); - foreach (var metabolism in ComponentManager.EntityQuery(true)) + foreach (var respirator in ComponentManager.EntityQuery(false)) { - metabolism.Update(frameTime); + respirator.Update(frameTime); } } } diff --git a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs b/Content.Server/Chemistry/Metabolism/DefaultDrink.cs deleted file mode 100644 index 255200f103..0000000000 --- a/Content.Server/Chemistry/Metabolism/DefaultDrink.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Content.Server.Nutrition.Components; -using Content.Shared.Chemistry; -using Content.Shared.Chemistry.Metabolizable; -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Content.Server.Chemistry.Metabolism -{ - /// - /// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target, - /// and to update it's thirst values. Inherits metabolisation rate logic from DefaultMetabolizable. - /// - [DataDefinition] - public class DefaultDrink : DefaultMetabolizable - { - //How much thirst is satiated when 1u of the reagent is metabolized - [DataField("hydrationFactor")] - public float HydrationFactor { get; set; } = 30.0f; - - //Remove reagent at set rate, satiate thirst if a ThirstComponent can be found - public override ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent) - { - // use DefaultMetabolism to determine how much reagent we should metabolize - var amountMetabolized = base.Metabolize(solutionEntity, reagentId, tickTime, availableReagent); - - // If metabolizing entity has a ThirstComponent, hydrate them. - if (solutionEntity.TryGetComponent(out ThirstComponent? thirst)) - thirst.UpdateThirst(amountMetabolized.Float() * HydrationFactor); - - //Return amount of reagent to be removed, remove reagent regardless of ThirstComponent presence - return amountMetabolized; - } - } -} diff --git a/Content.Server/Chemistry/Metabolism/DefaultFood.cs b/Content.Server/Chemistry/Metabolism/DefaultFood.cs deleted file mode 100644 index fafcf58dd1..0000000000 --- a/Content.Server/Chemistry/Metabolism/DefaultFood.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Content.Server.Nutrition.Components; -using Content.Shared.Chemistry; -using Content.Shared.Chemistry.Metabolizable; -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Content.Server.Chemistry.Metabolism -{ - /// - /// Default metabolism for food reagents. Attempts to find a HungerComponent on the target, - /// and to update it's hunger values. Inherits metabolisation rate logic from DefaultMetabolizable. - /// - [DataDefinition] - public class DefaultFood : DefaultMetabolizable - { - - /// - /// How much hunger is satiated when 1u of the reagent is metabolized - /// - [DataField("nutritionFactor")] public float NutritionFactor { get; set; } = 30.0f; - - - //Remove reagent at set rate, satiate hunger if a HungerComponent can be found - public override ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent) - { - // use DefaultMetabolism to determine how much reagent we should metabolize - var amountMetabolized = base.Metabolize(solutionEntity, reagentId, tickTime, availableReagent); - - // If metabolizing entity has a HungerComponent, feed them. - if (solutionEntity.TryGetComponent(out HungerComponent? hunger)) - hunger.UpdateFood(amountMetabolized.Float() * NutritionFactor); - - //Return amount of reagent to be removed. Reagent is removed regardless of HungerComponent presence - return amountMetabolized; - } - } -} diff --git a/Content.Server/Chemistry/Metabolism/HealthChangeMetabolism.cs b/Content.Server/Chemistry/Metabolism/HealthChangeMetabolism.cs deleted file mode 100644 index bbf2534505..0000000000 --- a/Content.Server/Chemistry/Metabolism/HealthChangeMetabolism.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Content.Shared.Chemistry; -using Content.Shared.Chemistry.Metabolizable; -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; -using Content.Shared.Damage; -using Content.Shared.Damage.Components; - -namespace Content.Server.Chemistry.Metabolism -{ - /// - /// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target, - /// and to update its damage values. Inherits metabolisation rate logic from DefaultMetabolizable. - /// - [DataDefinition] - public class HealthChangeMetabolism : DefaultMetabolizable - { - - /// - /// How much damage is changed when 1u of the reagent is metabolized. - /// - [DataField("healthChange")] - public float HealthChange { get; set; } = 1.0f; - - /// - /// Class of damage changed, Brute, Burn, Toxin, Airloss. - /// - [DataField("damageClass")] - public DamageClass DamageType { get; set; } = DamageClass.Brute; - - private float _accumulatedHealth; - - /// - /// Remove reagent at set rate, changes damage if a DamageableComponent can be found. - /// - /// - /// - /// - /// Reagent available to be metabolized. - /// - public override ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent) - { - // use DefaultMetabolism to determine how much reagent we should metabolize - var amountMetabolized = base.Metabolize(solutionEntity, reagentId, tickTime, availableReagent); - - // how much does this much reagent heal for - var healthChangeAmount = HealthChange * amountMetabolized.Float(); - - if (solutionEntity.TryGetComponent(out IDamageableComponent? health)) - { - // Heal damage by healthChangeAmmount, rounding down to nearest integer - health.ChangeDamage(DamageType, (int) healthChangeAmount, true); - - // Store decimal remainder of healthChangeAmmount in _accumulatedHealth - _accumulatedHealth += (healthChangeAmount - (int) healthChangeAmount); - - if (_accumulatedHealth >= 1) - { - health.ChangeDamage(DamageType, 1, true); - _accumulatedHealth -= 1; - } - - else if(_accumulatedHealth <= -1) - { - health.ChangeDamage(DamageType, -1, true); - _accumulatedHealth += 1; - } - } - return amountMetabolized; - } - } -} diff --git a/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs new file mode 100644 index 0000000000..cfc0177d41 --- /dev/null +++ b/Content.Server/Chemistry/ReagentEffectConditions/ReagentThreshold.cs @@ -0,0 +1,26 @@ +using Content.Shared.Body.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Chemistry.Solution; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Chemistry.ReagentEffectConditions +{ + /// + /// Used for implementing reagent effects that require a certain amount of reagent before it should be applied. + /// For instance, overdoses. + /// + public class ReagentThreshold : ReagentEffectCondition + { + [DataField("min")] + public ReagentUnit Min = ReagentUnit.Zero; + + [DataField("max")] + public ReagentUnit Max = ReagentUnit.MaxValue; + + public override bool Condition(IEntity solutionEntity, Solution.ReagentQuantity reagent) + { + return reagent.Quantity >= Min && reagent.Quantity < Max; + } + } +} diff --git a/Content.Server/Chemistry/ReagentEffects/HealthChange.cs b/Content.Server/Chemistry/ReagentEffects/HealthChange.cs new file mode 100644 index 0000000000..47b640cdfc --- /dev/null +++ b/Content.Server/Chemistry/ReagentEffects/HealthChange.cs @@ -0,0 +1,56 @@ +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Chemistry.Solution; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Content.Shared.Damage; +using Content.Shared.Damage.Components; + +namespace Content.Server.Chemistry.ReagentEffects +{ + /// + /// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target, + /// and to update its damage values. + /// + public class HealthChange : ReagentEffect + { + /// + /// How much damage is changed when 1u of the reagent is metabolized. + /// + [DataField("healthChange")] + public float AmountToChange { get; set; } = 1.0f; + + /// + /// Class of damage changed, Brute, Burn, Toxin, Airloss. + /// + [DataField("damageClass")] + public DamageClass DamageType { get; set; } = DamageClass.Brute; + + private float _accumulatedHealth; + + /// + /// Changes damage if a DamageableComponent can be found. + /// + public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount) + { + if (solutionEntity.TryGetComponent(out IDamageableComponent? health)) + { + health.ChangeDamage(DamageType, (int)AmountToChange, true); + float decHealthChange = (float) (AmountToChange - (int) AmountToChange); + _accumulatedHealth += decHealthChange; + + if (_accumulatedHealth >= 1) + { + health.ChangeDamage(DamageType, 1, true); + _accumulatedHealth -= 1; + } + + else if(_accumulatedHealth <= -1) + { + health.ChangeDamage(DamageType, -1, true); + _accumulatedHealth += 1; + } + } + } + } +} diff --git a/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs new file mode 100644 index 0000000000..9031737443 --- /dev/null +++ b/Content.Server/Chemistry/ReagentEffects/MovespeedModifier.cs @@ -0,0 +1,69 @@ +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Content.Shared.Movement.Components; +using Content.Shared.Chemistry.Components; +using Robust.Shared.Timing; +using Robust.Shared.IoC; +using System; +using Content.Shared.Chemistry.Solution; + +namespace Content.Server.Chemistry.ReagentEffects +{ + /// + /// Default metabolism for stimulants and tranqs. Attempts to find a MovementSpeedModifier on the target, + /// adding one if not there and to change the movespeed + /// + public class MovespeedModifier : ReagentEffect + { + /// + /// How much the entities' walk speed is multiplied by. + /// + [DataField("walkSpeedModifier")] + public float WalkSpeedModifier { get; set; } = 1; + + /// + /// How much the entities' run speed is multiplied by. + /// + [DataField("sprintSpeedModifier")] + public float SprintSpeedModifier { get; set; } = 1; + + /// + /// How long the modifier applies (in seconds) when metabolized. + /// + [DataField("statusLifetime")] + public float StatusLifetime = 2f; + + /// + /// Remove reagent at set rate, changes the movespeed modifiers and adds a MovespeedModifierMetabolismComponent if not already there. + /// + public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount) + { + if (!solutionEntity.TryGetComponent(out MovementSpeedModifierComponent? movement)) return; + + solutionEntity.EnsureComponent(out MovespeedModifierMetabolismComponent status); + + // Only refresh movement if we need to. + var modified = !status.WalkSpeedModifier.Equals(WalkSpeedModifier) || + !status.SprintSpeedModifier.Equals(SprintSpeedModifier); + + status.WalkSpeedModifier = WalkSpeedModifier; + status.SprintSpeedModifier = SprintSpeedModifier; + + IncreaseTimer(status, StatusLifetime * amount.Quantity.Float()); + + if (modified) + movement.RefreshMovementSpeedModifiers(); + + } + public void IncreaseTimer(MovespeedModifierMetabolismComponent status, float time) + { + var gameTiming = IoCManager.Resolve(); + + var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds); + + status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time); + status.Dirty(); + } + } +} diff --git a/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs b/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs new file mode 100644 index 0000000000..983d30b565 --- /dev/null +++ b/Content.Server/Chemistry/ReagentEffects/SatiateHunger.cs @@ -0,0 +1,28 @@ +using Content.Server.Nutrition.Components; +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Chemistry.Solution; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Chemistry.ReagentEffects +{ + /// + /// Attempts to find a HungerComponent on the target, + /// and to update it's hunger values. + /// + public class SatiateHunger : ReagentEffect + { + /// + /// How much hunger is satiated when 1u of the reagent is metabolized + /// + [DataField("nutritionFactor")] public float NutritionFactor { get; set; } = 3.0f; + + //Remove reagent at set rate, satiate hunger if a HungerComponent can be found + public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount) + { + if (solutionEntity.TryGetComponent(out HungerComponent? hunger)) + hunger.UpdateFood(NutritionFactor); + } + } +} diff --git a/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs new file mode 100644 index 0000000000..637c56f235 --- /dev/null +++ b/Content.Server/Chemistry/ReagentEffects/SatiateThirst.cs @@ -0,0 +1,28 @@ +using Content.Server.Nutrition.Components; +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Chemistry.Solution; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Chemistry.ReagentEffects +{ + /// + /// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target, + /// and to update it's thirst values. + /// + public class SatiateThirst : ReagentEffect + { + /// How much thirst is satiated each metabolism tick. Not currently tied to + /// rate or anything. + [DataField("hydrationFactor")] + public float HydrationFactor { get; set; } = 3.0f; + + /// Satiate thirst if a ThirstComponent can be found + public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount) + { + if (solutionEntity.TryGetComponent(out ThirstComponent? thirst)) + thirst.UpdateThirst(HydrationFactor); + } + } +} diff --git a/Content.Server/Doors/Components/AirlockComponent.cs b/Content.Server/Doors/Components/AirlockComponent.cs index eee426e02e..d9bc2e2f4d 100644 --- a/Content.Server/Doors/Components/AirlockComponent.cs +++ b/Content.Server/Doors/Components/AirlockComponent.cs @@ -25,30 +25,47 @@ namespace Content.Server.Doors.Components /// Companion component to ServerDoorComponent that handles airlock-specific behavior -- wires, requiring power to operate, bolts, and allowing automatic closing. /// [RegisterComponent] - [ComponentReference(typeof(IDoorCheck))] - public class AirlockComponent : Component, IWires, IDoorCheck + public class AirlockComponent : Component, IWires { public override string Name => "Airlock"; [ComponentDependency] - private readonly ServerDoorComponent? _doorComponent = null; + public readonly ServerDoorComponent? DoorComponent = null; [ComponentDependency] - private readonly SharedAppearanceComponent? _appearanceComponent = null; + public readonly SharedAppearanceComponent? AppearanceComponent = null; [ComponentDependency] - private readonly ApcPowerReceiverComponent? _receiverComponent = null; + public readonly ApcPowerReceiverComponent? ReceiverComponent = null; [ComponentDependency] - private readonly WiresComponent? _wiresComponent = null; + public readonly WiresComponent? WiresComponent = null; + + /// + /// Sound to play when the bolts on the airlock go up. + /// + [DataField("boltUpSound")] + public SoundSpecifier BoltUpSound = new SoundPathSpecifier("/Audio/Machines/boltsup.ogg"); + + /// + /// Sound to play when the bolts on the airlock go down. + /// + [DataField("boltDownSound")] + public SoundSpecifier BoltDownSound = new SoundPathSpecifier("/Audio/Machines/boltsdown.ogg"); /// /// Duration for which power will be disabled after pulsing either power wire. /// - private static readonly TimeSpan PowerWiresTimeout = TimeSpan.FromSeconds(5.0); + [DataField("powerWiresTimeout")] + public float PowerWiresTimeout = 5.0f; + + /// + /// Whether the maintenance panel should be visible even if the airlock is opened. + /// + [DataField("openPanelVisible")] + public bool OpenPanelVisible = false; private CancellationTokenSource _powerWiresPulsedTimerCancel = new(); - private bool _powerWiresPulsed; /// @@ -85,7 +102,7 @@ namespace Content.Server.Doors.Components private bool BoltLightsVisible { get => _boltLightsWirePulsed && BoltsDown && IsPowered() - && _doorComponent != null && _doorComponent.State == SharedDoorComponent.DoorState.Closed; + && DoorComponent != null && DoorComponent.State == SharedDoorComponent.DoorState.Closed; set { _boltLightsWirePulsed = value; @@ -93,124 +110,53 @@ namespace Content.Server.Doors.Components } } - [DataField("setBoltsDownSound")] private SoundSpecifier _setBoltsDownSound = new SoundPathSpecifier("/Audio/Machines/boltsdown.ogg"); - - [DataField("setBoltsUpSound")] private SoundSpecifier _setBoltsUpSound = new SoundPathSpecifier("/Audio/Machines/boltsup.ogg"); - - private static readonly TimeSpan AutoCloseDelayFast = TimeSpan.FromSeconds(1); + [ViewVariables(VVAccess.ReadWrite)] + [DataField("autoClose")] + public bool AutoClose = true; [ViewVariables(VVAccess.ReadWrite)] - private bool _autoClose = true; + [DataField("autoCloseDelayModifier")] + public float AutoCloseDelayModifier = 1.0f; [ViewVariables(VVAccess.ReadWrite)] - private bool _normalCloseSpeed = true; - - [ViewVariables(VVAccess.ReadWrite)] - private bool _safety = true; + public bool Safety = true; protected override void Initialize() { base.Initialize(); - if (_receiverComponent != null && _appearanceComponent != null) + if (ReceiverComponent != null && AppearanceComponent != null) { - _appearanceComponent.SetData(DoorVisuals.Powered, _receiverComponent.Powered); + AppearanceComponent.SetData(DoorVisuals.Powered, ReceiverComponent.Powered); } } - public override void HandleMessage(ComponentMessage message, IComponent? component) - { - base.HandleMessage(message, component); - switch (message) - { - case PowerChangedMessage powerChanged: - PowerDeviceOnOnPowerStateChanged(powerChanged); - break; - } - } - - void IDoorCheck.OnStateChange(SharedDoorComponent.DoorState doorState) - { - // Only show the maintenance panel if the airlock is closed - if (_wiresComponent != null) - { - _wiresComponent.IsPanelVisible = doorState != SharedDoorComponent.DoorState.Open; - } - // If the door is closed, we should look if the bolt was locked while closing - UpdateBoltLightStatus(); - } - - bool IDoorCheck.OpenCheck() => CanChangeState(); - - bool IDoorCheck.CloseCheck() => CanChangeState(); - - bool IDoorCheck.DenyCheck() => CanChangeState(); - - bool IDoorCheck.SafetyCheck() => _safety; - - bool IDoorCheck.AutoCloseCheck() => _autoClose; - - TimeSpan? IDoorCheck.GetCloseSpeed() - { - if (_normalCloseSpeed) - { - return null; - } - return AutoCloseDelayFast; - } - - bool IDoorCheck.BlockActivate(ActivateEventArgs eventArgs) - { - if (_wiresComponent != null && _wiresComponent.IsPanelOpen && - eventArgs.User.TryGetComponent(out ActorComponent? actor)) - { - _wiresComponent.OpenInterface(actor.PlayerSession); - return true; - } - return false; - } - - bool IDoorCheck.CanPryCheck(InteractUsingEventArgs eventArgs) - { - if (IsBolted()) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("airlock-component-cannot-pry-is-bolted-message ")); - return false; - } - if (IsPowered()) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("airlock-component-cannot-pry-is-powered-message")); - return false; - } - return true; - } - - private bool CanChangeState() + public bool CanChangeState() { return IsPowered() && !IsBolted(); } - private bool IsBolted() + public bool IsBolted() { return _boltsDown; } - private bool IsPowered() + public bool IsPowered() { - return _receiverComponent == null || _receiverComponent.Powered; + return ReceiverComponent == null || ReceiverComponent.Powered; } - private void UpdateBoltLightStatus() + public void UpdateBoltLightStatus() { - if (_appearanceComponent != null) + if (AppearanceComponent != null) { - _appearanceComponent.SetData(DoorVisuals.BoltLights, BoltLightsVisible); + AppearanceComponent.SetData(DoorVisuals.BoltLights, BoltLightsVisible); } } - private void UpdateWiresStatus() + public void UpdateWiresStatus() { - if (_doorComponent == null) + if (DoorComponent == null) { return; } @@ -220,9 +166,9 @@ namespace Content.Server.Doors.Components { powerLight = new StatusLightData(Color.Yellow, StatusLightState.BlinkingFast, "POWR"); } - else if (_wiresComponent != null && - _wiresComponent.IsWireCut(Wires.MainPower) && - _wiresComponent.IsWireCut(Wires.BackupPower)) + else if (WiresComponent != null && + WiresComponent.IsWireCut(Wires.MainPower) && + WiresComponent.IsWireCut(Wires.BackupPower)) { powerLight = new StatusLightData(Color.Red, StatusLightState.On, "POWR"); } @@ -232,63 +178,59 @@ namespace Content.Server.Doors.Components var boltLightsStatus = new StatusLightData(Color.Lime, _boltLightsWirePulsed ? StatusLightState.On : StatusLightState.Off, "BLTL"); + var ev = new DoorGetCloseTimeModifierEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); + var timingStatus = - new StatusLightData(Color.Orange, !_autoClose ? StatusLightState.Off : - !_normalCloseSpeed ? StatusLightState.BlinkingSlow : + new StatusLightData(Color.Orange, !AutoClose ? StatusLightState.Off : + !MathHelper.CloseTo(ev.CloseTimeModifier, 1.0f) ? StatusLightState.BlinkingSlow : StatusLightState.On, "TIME"); var safetyStatus = - new StatusLightData(Color.Red, _safety ? StatusLightState.On : StatusLightState.Off, "SAFE"); + new StatusLightData(Color.Red, Safety ? StatusLightState.On : StatusLightState.Off, "SAFE"); - if (_wiresComponent == null) + if (WiresComponent == null) { return; } - _wiresComponent.SetStatus(AirlockWireStatus.PowerIndicator, powerLight); - _wiresComponent.SetStatus(AirlockWireStatus.BoltIndicator, boltStatus); - _wiresComponent.SetStatus(AirlockWireStatus.BoltLightIndicator, boltLightsStatus); - _wiresComponent.SetStatus(AirlockWireStatus.AIControlIndicator, new StatusLightData(Color.Purple, StatusLightState.BlinkingSlow, "AICT")); - _wiresComponent.SetStatus(AirlockWireStatus.TimingIndicator, timingStatus); - _wiresComponent.SetStatus(AirlockWireStatus.SafetyIndicator, safetyStatus); - /* - _wires.SetStatus(6, powerLight); - _wires.SetStatus(7, powerLight); - _wires.SetStatus(8, powerLight); - _wires.SetStatus(9, powerLight); - _wires.SetStatus(10, powerLight); - _wires.SetStatus(11, powerLight);*/ + WiresComponent.SetStatus(AirlockWireStatus.PowerIndicator, powerLight); + WiresComponent.SetStatus(AirlockWireStatus.BoltIndicator, boltStatus); + WiresComponent.SetStatus(AirlockWireStatus.BoltLightIndicator, boltLightsStatus); + WiresComponent.SetStatus(AirlockWireStatus.AIControlIndicator, new StatusLightData(Color.Purple, StatusLightState.BlinkingSlow, "AICT")); + WiresComponent.SetStatus(AirlockWireStatus.TimingIndicator, timingStatus); + WiresComponent.SetStatus(AirlockWireStatus.SafetyIndicator, safetyStatus); } private void UpdatePowerCutStatus() { - if (_receiverComponent == null) + if (ReceiverComponent == null) { return; } if (PowerWiresPulsed) { - _receiverComponent.PowerDisabled = true; + ReceiverComponent.PowerDisabled = true; return; } - if (_wiresComponent == null) + if (WiresComponent == null) { return; } - _receiverComponent.PowerDisabled = - _wiresComponent.IsWireCut(Wires.MainPower) || - _wiresComponent.IsWireCut(Wires.BackupPower); + ReceiverComponent.PowerDisabled = + WiresComponent.IsWireCut(Wires.MainPower) || + WiresComponent.IsWireCut(Wires.BackupPower); } private void PowerDeviceOnOnPowerStateChanged(PowerChangedMessage e) { - if (_appearanceComponent != null) + if (AppearanceComponent != null) { - _appearanceComponent.SetData(DoorVisuals.Powered, e.Powered); + AppearanceComponent.SetData(DoorVisuals.Powered, e.Powered); } // BoltLights also got out @@ -347,19 +289,13 @@ namespace Content.Server.Doors.Components builder.CreateWire(Wires.BoltLight); builder.CreateWire(Wires.Timing); builder.CreateWire(Wires.Safety); - /* - builder.CreateWire(6); - builder.CreateWire(7); - builder.CreateWire(8); - builder.CreateWire(9); - builder.CreateWire(10); - builder.CreateWire(11);*/ + UpdateWiresStatus(); } public void WiresUpdate(WiresUpdateEventArgs args) { - if (_doorComponent == null) + if (DoorComponent == null) { return; } @@ -373,7 +309,7 @@ namespace Content.Server.Doors.Components PowerWiresPulsed = true; _powerWiresPulsedTimerCancel.Cancel(); _powerWiresPulsedTimerCancel = new CancellationTokenSource(); - Owner.SpawnTimer(PowerWiresTimeout, + Owner.SpawnTimer(TimeSpan.FromSeconds(PowerWiresTimeout), () => PowerWiresPulsed = false, _powerWiresPulsedTimerCancel.Token); break; @@ -396,11 +332,11 @@ namespace Content.Server.Doors.Components BoltLightsVisible = !_boltLightsWirePulsed; break; case Wires.Timing: - _normalCloseSpeed = !_normalCloseSpeed; - _doorComponent.RefreshAutoClose(); + AutoCloseDelayModifier = 0.5f; + DoorComponent.RefreshAutoClose(); break; case Wires.Safety: - _safety = !_safety; + Safety = !Safety; break; } } @@ -419,11 +355,11 @@ namespace Content.Server.Doors.Components BoltLightsVisible = true; break; case Wires.Timing: - _autoClose = true; - _doorComponent.RefreshAutoClose(); + AutoClose = true; + DoorComponent.RefreshAutoClose(); break; case Wires.Safety: - _safety = true; + Safety = true; break; } } @@ -439,11 +375,11 @@ namespace Content.Server.Doors.Components BoltLightsVisible = false; break; case Wires.Timing: - _autoClose = false; - _doorComponent.RefreshAutoClose(); + AutoClose = false; + DoorComponent.RefreshAutoClose(); break; case Wires.Safety: - _safety = false; + Safety = false; break; } } @@ -461,14 +397,7 @@ namespace Content.Server.Doors.Components BoltsDown = newBolts; - if (newBolts) - { - SoundSystem.Play(Filter.Broadcast(), _setBoltsDownSound.GetSound(), Owner); - } - else - { - SoundSystem.Play(Filter.Broadcast(), _setBoltsUpSound.GetSound(), Owner); - } + SoundSystem.Play(Filter.Broadcast(), newBolts ? BoltDownSound.GetSound() : BoltUpSound.GetSound(), Owner); } } } diff --git a/Content.Server/Atmos/Components/FirelockComponent.cs b/Content.Server/Doors/Components/FirelockComponent.cs similarity index 59% rename from Content.Server/Atmos/Components/FirelockComponent.cs rename to Content.Server/Doors/Components/FirelockComponent.cs index b1849cd757..ee7154bb28 100644 --- a/Content.Server/Atmos/Components/FirelockComponent.cs +++ b/Content.Server/Doors/Components/FirelockComponent.cs @@ -1,3 +1,4 @@ +using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Doors; using Content.Server.Doors.Components; @@ -6,26 +7,34 @@ using Content.Shared.Interaction; using Content.Shared.Notification.Managers; using Robust.Shared.GameObjects; using Robust.Shared.Localization; +using Robust.Shared.Serialization.Manager.Attributes; -namespace Content.Server.Atmos.Components +namespace Content.Server.Doors.Components { /// - /// Companion component to ServerDoorComponent that handles firelock-specific behavior -- primarily prying, and not being openable on open-hand click. + /// Companion component to ServerDoorComponent that handles firelock-specific behavior -- primarily prying, + /// and not being openable on open-hand click. /// [RegisterComponent] - [ComponentReference(typeof(IDoorCheck))] - public class FirelockComponent : Component, IDoorCheck + public class FirelockComponent : Component { public override string Name => "Firelock"; [ComponentDependency] - private readonly ServerDoorComponent? _doorComponent = null; + public readonly ServerDoorComponent? DoorComponent = null; + + /// + /// Pry time modifier to be used when the firelock is currently closed due to fire or pressure. + /// + /// + [DataField("lockedPryTimeModifier")] + public float LockedPryTimeModifier = 1.5f; public bool EmergencyPressureStop() { - if (_doorComponent != null && _doorComponent.State == SharedDoorComponent.DoorState.Open && _doorComponent.CanCloseGeneric()) + if (DoorComponent != null && DoorComponent.State == SharedDoorComponent.DoorState.Open && DoorComponent.CanCloseGeneric()) { - _doorComponent.Close(); + DoorComponent.Close(); if (Owner.TryGetComponent(out AirtightComponent? airtight)) { EntitySystem.Get().SetAirblocked(airtight, true); @@ -35,41 +44,6 @@ namespace Content.Server.Atmos.Components return false; } - bool IDoorCheck.OpenCheck() - { - return !IsHoldingFire() && !IsHoldingPressure(); - } - - bool IDoorCheck.DenyCheck() => false; - - float? IDoorCheck.GetPryTime() - { - if (IsHoldingFire() || IsHoldingPressure()) - { - return 1.5f; - } - return null; - } - - bool IDoorCheck.BlockActivate(ActivateEventArgs eventArgs) => true; - - void IDoorCheck.OnStartPry(InteractUsingEventArgs eventArgs) - { - if (_doorComponent == null || _doorComponent.State != SharedDoorComponent.DoorState.Closed) - { - return; - } - - if (IsHoldingPressure()) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("firelock-component-is-holding-pressure-message")); - } - else if (IsHoldingFire()) - { - Owner.PopupMessage(eventArgs.User, Loc.GetString("firelock-component-is-holding-fire-message")); - } - } - public bool IsHoldingPressure(float threshold = 20) { var atmosphereSystem = EntitySystem.Get(); diff --git a/Content.Server/Doors/Components/ServerDoorComponent.cs b/Content.Server/Doors/Components/ServerDoorComponent.cs index 23ee6c3778..ba526c78c4 100644 --- a/Content.Server/Doors/Components/ServerDoorComponent.cs +++ b/Content.Server/Doors/Components/ServerDoorComponent.cs @@ -38,9 +38,6 @@ namespace Content.Server.Doors.Components [ComponentReference(typeof(SharedDoorComponent))] public class ServerDoorComponent : SharedDoorComponent, IActivate, IInteractUsing, IMapInit { - [ComponentDependency] - private readonly IDoorCheck? _doorCheck = null; - [ViewVariables] [DataField("board")] private string? _boardPrototype; @@ -67,11 +64,8 @@ namespace Content.Server.Doors.Components _ => throw new ArgumentOutOfRangeException(), }; - if (_doorCheck != null) - { - _doorCheck.OnStateChange(State); - RefreshAutoClose(); - } + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new DoorStateChangedEvent(State), false); + _autoCloseCancelTokenSource?.Cancel(); Dirty(); } @@ -109,7 +103,7 @@ namespace Content.Server.Doors.Components /// Handled in Startup(). /// [ViewVariables(VVAccess.ReadWrite)] [DataField("startOpen")] - private bool _startOpen; + private bool _startOpen = false; /// /// Whether the airlock is welded shut. Can be set by the prototype, although this will fail if the door isn't weldable. @@ -143,6 +137,41 @@ namespace Content.Server.Doors.Components [DataField("weldable")] private bool _weldable = true; + /// + /// Sound to play when the door opens. + /// + [DataField("openSound")] + public SoundSpecifier? OpenSound; + + /// + /// Sound to play when the door closes. + /// + [DataField("closeSound")] + public SoundSpecifier? CloseSound; + + /// + /// Sound to play if the door is denied. + /// + [DataField("denySound")] + public SoundSpecifier? DenySound; + + /// + /// Default time that the door should take to pry open. + /// + [DataField("pryTime")] + public float PryTime = 0.5f; + + /// + /// Minimum interval allowed between deny sounds in milliseconds. + /// + [DataField("denySoundMinimumInterval")] + public float DenySoundMinimumInterval = 250.0f; + + /// + /// Used to stop people from spamming the deny sound. + /// + private TimeSpan LastDenySoundTime = TimeSpan.Zero; + /// /// Whether the door can currently be welded. /// @@ -153,6 +182,7 @@ namespace Content.Server.Doors.Components /// private bool _beingWelded; + //[ViewVariables(VVAccess.ReadWrite)] //[DataField("canCrush")] //private bool _canCrush = true; // TODO implement door crushing @@ -191,7 +221,7 @@ namespace Content.Server.Doors.Components Logger.Warning("{0} prototype loaded with incompatible flags: 'welded' and 'startOpen' are both true.", Owner.Name); return; } - QuickOpen(); + QuickOpen(false); } CreateDoorElectronicsBoard(); @@ -199,10 +229,10 @@ namespace Content.Server.Doors.Components void IActivate.Activate(ActivateEventArgs eventArgs) { - if (_doorCheck != null && _doorCheck.BlockActivate(eventArgs)) - { + DoorClickShouldActivateEvent ev = new DoorClickShouldActivateEvent(eventArgs); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); + if (ev.Handled) return; - } if (State == DoorState.Open) { @@ -282,12 +312,10 @@ namespace Content.Server.Doors.Components { return false; } - if(_doorCheck != null) - { - return _doorCheck.OpenCheck(); - } - return true; + var ev = new BeforeDoorOpenedEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); + return !ev.Cancelled; } /// @@ -304,12 +332,19 @@ namespace Content.Server.Doors.Components _stateChangeCancelTokenSource?.Cancel(); _stateChangeCancelTokenSource = new(); + if (OpenSound != null) + { + SoundSystem.Play(Filter.Pvs(Owner), OpenSound.GetSound(), + AudioParams.Default.WithVolume(-5)); + } + Owner.SpawnTimer(OpenTimeOne, async () => { OnPartialOpen(); await Timer.Delay(OpenTimeTwo, _stateChangeCancelTokenSource.Token); State = DoorState.Open; + RefreshAutoClose(); }, _stateChangeCancelTokenSource.Token); } @@ -323,7 +358,7 @@ namespace Content.Server.Doors.Components Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner, false)); } - private void QuickOpen() + private void QuickOpen(bool refresh) { if (Occludes && Owner.TryGetComponent(out OccluderComponent? occluder)) { @@ -331,6 +366,8 @@ namespace Content.Server.Doors.Components } OnPartialOpen(); State = DoorState.Open; + if(refresh) + RefreshAutoClose(); } #endregion @@ -369,17 +406,19 @@ namespace Content.Server.Doors.Components /// Boolean describing whether this door can close. public bool CanCloseGeneric() { - if (_doorCheck != null && !_doorCheck.CloseCheck()) - { + var ev = new BeforeDoorClosedEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); + if (ev.Cancelled) return false; - } return !IsSafetyColliding(); } private bool SafetyCheck() { - return (_doorCheck != null && _doorCheck.SafetyCheck()) || _inhibitCrush; + var ev = new DoorSafetyEnabledEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); + return ev.Safety || _inhibitCrush; } /// @@ -415,6 +454,13 @@ namespace Content.Server.Doors.Components _stateChangeCancelTokenSource?.Cancel(); _stateChangeCancelTokenSource = new(); + + if (CloseSound != null) + { + SoundSystem.Play(Filter.Pvs(Owner), CloseSound.GetSound(), + AudioParams.Default.WithVolume(-10)); + } + Owner.SpawnTimer(CloseTimeOne, async () => { // if somebody walked into the door as it was closing, and we don't crush things @@ -507,10 +553,10 @@ namespace Content.Server.Doors.Components public void Deny() { - if (_doorCheck != null && !_doorCheck.DenyCheck()) - { + var ev = new BeforeDoorDeniedEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); + if (ev.Cancelled) return; - } if (State == DoorState.Open || IsWeldedShut) return; @@ -518,6 +564,25 @@ namespace Content.Server.Doors.Components _stateChangeCancelTokenSource?.Cancel(); _stateChangeCancelTokenSource = new(); SetAppearance(DoorVisualState.Deny); + + if (DenySound != null) + { + if (LastDenySoundTime == TimeSpan.Zero) + { + LastDenySoundTime = _gameTiming.CurTime; + } + else + { + var difference = _gameTiming.CurTime - LastDenySoundTime; + if (difference < TimeSpan.FromMilliseconds(DenySoundMinimumInterval)) + return; + } + + LastDenySoundTime = _gameTiming.CurTime; + SoundSystem.Play(Filter.Pvs(Owner), DenySound.GetSound(), + AudioParams.Default.WithVolume(-3)); + } + Owner.SpawnTimer(DenyTime, () => { SetAppearance(DoorVisualState.Closed); @@ -525,19 +590,24 @@ namespace Content.Server.Doors.Components } /// - /// Stops the current auto-close timer if there is one. Starts a new one if this is appropriate (i.e. entity has an IDoorCheck component that allows auto-closing). + /// Starts a new auto close timer if this is appropriate + /// (i.e. event raised is not cancelled). /// public void RefreshAutoClose() { - _autoCloseCancelTokenSource?.Cancel(); - - if (State != DoorState.Open || _doorCheck == null || !_doorCheck.AutoCloseCheck()) - { + if (State != DoorState.Open) return; - } + + var autoev = new BeforeDoorAutoCloseEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, autoev, false); + if (autoev.Cancelled) + return; + _autoCloseCancelTokenSource = new(); - var realCloseTime = _doorCheck.GetCloseSpeed() ?? AutoCloseDelay; + var ev = new DoorGetCloseTimeModifierEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); + var realCloseTime = AutoCloseDelay * ev.CloseTimeModifier; Owner.SpawnRepeatingTimer(realCloseTime, async () => { @@ -559,21 +629,18 @@ namespace Content.Server.Doors.Components // for prying doors if (tool.HasQuality(ToolQuality.Prying) && !IsWeldedShut) { - var successfulPry = false; + var ev = new DoorGetPryTimeModifierEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, ev, false); - if (_doorCheck != null) - { - _doorCheck.OnStartPry(eventArgs); - successfulPry = await tool.UseTool(eventArgs.User, Owner, - _doorCheck.GetPryTime() ?? 0.5f, ToolQuality.Prying, () => _doorCheck.CanPryCheck(eventArgs)); - } - else - { - successfulPry = await tool.UseTool(eventArgs.User, Owner, 0.5f, ToolQuality.Prying); - } + var canEv = new BeforeDoorPryEvent(eventArgs); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, canEv, false); + + var successfulPry = await tool.UseTool(eventArgs.User, Owner, + ev.PryTimeModifier * PryTime, ToolQuality.Prying, () => !canEv.Cancelled); if (successfulPry && !IsWeldedShut) { + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new OnDoorPryEvent(eventArgs), false); if (State == DoorState.Closed) { Open(); diff --git a/Content.Server/Doors/DoorEvents.cs b/Content.Server/Doors/DoorEvents.cs new file mode 100644 index 0000000000..73a0986664 --- /dev/null +++ b/Content.Server/Doors/DoorEvents.cs @@ -0,0 +1,141 @@ +#nullable enable +using System; +using Content.Shared.Doors; +using Content.Shared.Interaction; +using Robust.Shared.GameObjects; + +namespace Content.Server.Doors +{ + /// + /// Raised when the door's State variable is changed to a new variable that it was not equal to before. + /// + public class DoorStateChangedEvent : EntityEventArgs + { + public SharedDoorComponent.DoorState State; + + public DoorStateChangedEvent(SharedDoorComponent.DoorState state) + { + State = state; + } + } + + /// + /// Raised when the door is determining whether it is able to open. + /// Cancel to stop the door from being opened. + /// + public class BeforeDoorOpenedEvent : CancellableEntityEventArgs + { + } + + /// + /// Raised when the door is successfully opened. + /// + public class OnDoorOpenedEvent : HandledEntityEventArgs + { + } + + /// + /// Raised when the door is determining whether it is able to close. + /// Cancel to stop the door from being closed. + /// + public class BeforeDoorClosedEvent : CancellableEntityEventArgs + { + } + + /// + /// Raised when the door is successfully closed. + /// + public class OnDoorClosedEvent : HandledEntityEventArgs + { + } + + /// + /// Called when the door is determining whether it is able to deny. + /// Cancel to stop the door from being able to deny. + /// + public class BeforeDoorDeniedEvent : CancellableEntityEventArgs + { + } + + /// + /// Raised when access to the door is denied. + /// + public class OnDoorDeniedEvent : HandledEntityEventArgs + { + } + + /// + /// Raised to determine whether the door's safety is on. + /// Modify Safety to set the door's safety. + /// + public class DoorSafetyEnabledEvent : HandledEntityEventArgs + { + public bool Safety = false; + } + + /// + /// Raised to determine whether the door should automatically close. + /// Cancel to stop it from automatically closing. + /// + public class BeforeDoorAutoCloseEvent : CancellableEntityEventArgs + { + } + + /// + /// Raised to determine how long the door's pry time should be modified by. + /// Multiply PryTimeModifier by the desired amount. + /// + public class DoorGetPryTimeModifierEvent : EntityEventArgs + { + public float PryTimeModifier = 1.0f; + } + + /// + /// Raised to determine how long the door's close time should be modified by. + /// Multiply CloseTimeModifier by the desired amount. + /// + public class DoorGetCloseTimeModifierEvent : EntityEventArgs + { + public float CloseTimeModifier = 1.0f; + } + + /// + /// Raised to determine whether clicking the door should open/close it. + /// + public class DoorClickShouldActivateEvent : HandledEntityEventArgs + { + public ActivateEventArgs Args; + + public DoorClickShouldActivateEvent(ActivateEventArgs args) + { + Args = args; + } + } + + /// + /// Raised when an attempt to pry open the door is made. + /// Cancel to stop the door from being pried open. + /// + public class BeforeDoorPryEvent : CancellableEntityEventArgs + { + public InteractUsingEventArgs Args; + + public BeforeDoorPryEvent(InteractUsingEventArgs args) + { + Args = args; + } + } + + /// + /// Raised when a door is successfully pried open. + /// + public class OnDoorPryEvent : EntityEventArgs + { + public InteractUsingEventArgs Args; + + public OnDoorPryEvent(InteractUsingEventArgs args) + { + Args = args; + } + } +} diff --git a/Content.Server/Doors/IDoorCheck.cs b/Content.Server/Doors/IDoorCheck.cs deleted file mode 100644 index 3c5989a5d8..0000000000 --- a/Content.Server/Doors/IDoorCheck.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using Content.Shared.Doors; -using Content.Shared.Interaction; - -namespace Content.Server.Doors -{ - public interface IDoorCheck - { - /// - /// Called when the door's State variable is changed to a new variable that it was not equal to before. - /// - void OnStateChange(SharedDoorComponent.DoorState doorState) { } - - /// - /// Called when the door is determining whether it is able to open. - /// - /// True if the door should open, false if it should not. - bool OpenCheck() => true; - - /// - /// Called when the door is determining whether it is able to close. - /// - /// True if the door should close, false if it should not. - bool CloseCheck() => true; - - /// - /// Called when the door is determining whether it is able to deny. - /// - /// True if the door should deny, false if it should not. - bool DenyCheck() => true; - - /// - /// Whether the door's safety is on. - /// - /// True if safety is on, false if it is not. - bool SafetyCheck() => false; - - /// - /// Whether the door should close automatically. - /// - /// True if the door should close automatically, false if it should not. - bool AutoCloseCheck() => false; - - /// - /// Gets an override for the amount of time to pry open the door, or null if there is no override. - /// - /// Float if there is an override, null otherwise. - float? GetPryTime() => null; - - /// - /// Gets an override for the amount of time before the door automatically closes, or null if there is no override. - /// - /// TimeSpan if there is an override, null otherwise. - TimeSpan? GetCloseSpeed() => null; - - /// - /// A check to determine whether or not a click on the door should interact with it with the intent to open/close. - /// - /// True if the door's IActivate should not run, false otherwise. - bool BlockActivate(ActivateEventArgs eventArgs) => false; - - /// - /// Called when somebody begins to pry open the door. - /// - /// The eventArgs of the InteractUsing method that called this function. - void OnStartPry(InteractUsingEventArgs eventArgs) { } - - /// - /// Check representing whether or not the door can be pried open. - /// - /// The eventArgs of the InteractUsing method that called this function. - /// True if the door can be pried open, false if it cannot. - bool CanPryCheck(InteractUsingEventArgs eventArgs) => true; - - } -} diff --git a/Content.Server/Doors/Systems/AirlockSystem.cs b/Content.Server/Doors/Systems/AirlockSystem.cs new file mode 100644 index 0000000000..4645319b53 --- /dev/null +++ b/Content.Server/Doors/Systems/AirlockSystem.cs @@ -0,0 +1,111 @@ +using Content.Server.Doors.Components; +using Content.Server.Power.Components; +using Content.Shared.Doors; +using Content.Shared.Notification.Managers; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Localization; + +namespace Content.Server.Doors.Systems +{ + public class AirlockSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnStateChanged); + SubscribeLocalEvent(OnBeforeDoorOpened); + SubscribeLocalEvent(OnBeforeDoorClosed); + SubscribeLocalEvent(OnBeforeDoorDenied); + SubscribeLocalEvent(OnDoorSafetyCheck); + SubscribeLocalEvent(OnDoorAutoCloseCheck); + SubscribeLocalEvent(OnDoorCloseTimeModifier); + SubscribeLocalEvent(OnDoorClickShouldActivate); + SubscribeLocalEvent(OnDoorPry); + } + + private void OnPowerChanged(EntityUid uid, AirlockComponent component, PowerChangedEvent args) + { + if (component.AppearanceComponent != null) + { + component.AppearanceComponent.SetData(DoorVisuals.Powered, args.Powered); + } + + // BoltLights also got out + component.UpdateBoltLightStatus(); + } + + private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args) + { + // Only show the maintenance panel if the airlock is closed + if (component.WiresComponent != null) + { + component.WiresComponent.IsPanelVisible = + component.OpenPanelVisible + || args.State != SharedDoorComponent.DoorState.Open; + } + // If the door is closed, we should look if the bolt was locked while closing + component.UpdateBoltLightStatus(); + } + + private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args) + { + if (!component.CanChangeState()) + args.Cancel(); + } + + private void OnBeforeDoorClosed(EntityUid uid, AirlockComponent component, BeforeDoorClosedEvent args) + { + if (!component.CanChangeState()) + args.Cancel(); + } + + private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args) + { + if (!component.CanChangeState()) + args.Cancel(); + } + + private void OnDoorSafetyCheck(EntityUid uid, AirlockComponent component, DoorSafetyEnabledEvent args) + { + args.Safety = component.Safety; + } + + private void OnDoorAutoCloseCheck(EntityUid uid, AirlockComponent component, BeforeDoorAutoCloseEvent args) + { + if (!component.AutoClose) + args.Cancel(); + } + + private void OnDoorCloseTimeModifier(EntityUid uid, AirlockComponent component, DoorGetCloseTimeModifierEvent args) + { + args.CloseTimeModifier *= component.AutoCloseDelayModifier; + } + + private void OnDoorClickShouldActivate(EntityUid uid, AirlockComponent component, DoorClickShouldActivateEvent args) + { + if (component.WiresComponent != null && component.WiresComponent.IsPanelOpen && + args.Args.User.TryGetComponent(out ActorComponent? actor)) + { + component.WiresComponent.OpenInterface(actor.PlayerSession); + args.Handled = true; + } + } + + private void OnDoorPry(EntityUid uid, AirlockComponent component, BeforeDoorPryEvent args) + { + if (component.IsBolted()) + { + component.Owner.PopupMessage(args.Args.User, Loc.GetString("airlock-component-cannot-pry-is-bolted-message")); + args.Cancel(); + } + if (component.IsPowered()) + { + component.Owner.PopupMessage(args.Args.User, Loc.GetString("airlock-component-cannot-pry-is-powered-message")); + args.Cancel(); + } + } + } +} diff --git a/Content.Server/Doors/DoorSystem.cs b/Content.Server/Doors/Systems/DoorSystem.cs similarity index 100% rename from Content.Server/Doors/DoorSystem.cs rename to Content.Server/Doors/Systems/DoorSystem.cs diff --git a/Content.Server/Doors/Systems/FirelockSystem.cs b/Content.Server/Doors/Systems/FirelockSystem.cs new file mode 100644 index 0000000000..49ab584471 --- /dev/null +++ b/Content.Server/Doors/Systems/FirelockSystem.cs @@ -0,0 +1,69 @@ +using Content.Server.Doors.Components; +using Content.Shared.Doors; +using Content.Shared.Notification.Managers; +using Robust.Shared.GameObjects; +using Robust.Shared.Localization; + +namespace Content.Server.Doors.Systems +{ + public class FirelockSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBeforeDoorOpened); + SubscribeLocalEvent(OnBeforeDoorDenied); + SubscribeLocalEvent(OnDoorGetPryTimeModifier); + SubscribeLocalEvent(OnDoorClickShouldActivate); + SubscribeLocalEvent(OnBeforeDoorPry); + SubscribeLocalEvent(OnBeforeDoorAutoclose); + } + + private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args) + { + if (component.IsHoldingFire() || component.IsHoldingPressure()) + args.Cancel(); + } + + private void OnBeforeDoorDenied(EntityUid uid, FirelockComponent component, BeforeDoorDeniedEvent args) + { + args.Cancel(); + } + + private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, DoorGetPryTimeModifierEvent args) + { + if (component.IsHoldingFire() || component.IsHoldingPressure()) + args.PryTimeModifier *= component.LockedPryTimeModifier; + } + + private void OnDoorClickShouldActivate(EntityUid uid, FirelockComponent component, DoorClickShouldActivateEvent args) + { + // We're a firelock, you can't click to open it + args.Handled = true; + } + + private void OnBeforeDoorPry(EntityUid uid, FirelockComponent component, BeforeDoorPryEvent args) + { + if (component.DoorComponent == null || component.DoorComponent.State != SharedDoorComponent.DoorState.Closed) + { + return; + } + + if (component.IsHoldingPressure()) + { + component.Owner.PopupMessage(args.Args.User, Loc.GetString("firelock-component-is-holding-pressure-message")); + } + else if (component.IsHoldingFire()) + { + component.Owner.PopupMessage(args.Args.User, Loc.GetString("firelock-component-is-holding-fire-message")); + } + } + + private void OnBeforeDoorAutoclose(EntityUid uid, FirelockComponent component, BeforeDoorAutoCloseEvent args) + { + // Firelocks can't autoclose, they must be manually closed + args.Cancel(); + } + } +} diff --git a/Content.Server/Explosion/TriggerSystem.cs b/Content.Server/Explosion/TriggerSystem.cs index 8f1a03fef9..bc598de1e2 100644 --- a/Content.Server/Explosion/TriggerSystem.cs +++ b/Content.Server/Explosion/TriggerSystem.cs @@ -112,6 +112,7 @@ namespace Content.Server.Explosion Timer.Spawn(delay, () => { + if (triggered.Deleted) return; Trigger(triggered, user); }); } diff --git a/Content.Server/GameTicking/GameTicker.CVars.cs b/Content.Server/GameTicking/GameTicker.CVars.cs index f8cd9faea6..a00299df4b 100644 --- a/Content.Server/GameTicking/GameTicker.CVars.cs +++ b/Content.Server/GameTicking/GameTicker.CVars.cs @@ -21,14 +21,22 @@ namespace Content.Server.GameTicking [ViewVariables] public bool DisallowLateJoin { get; private set; } = false; + [ViewVariables] + public bool StationOffset { get; private set; } = false; + + [ViewVariables] + public float MaxStationOffset { get; private set; } = 0f; + private void InitializeCVars() { _configurationManager.OnValueChanged(CCVars.GameLobbyEnabled, value => LobbyEnabled = value, true); _configurationManager.OnValueChanged(CCVars.GameDummyTicker, value => DummyTicker = value, true); _configurationManager.OnValueChanged(CCVars.GameMap, value => ChosenMap = value, true); _configurationManager.OnValueChanged(CCVars.GameLobbyDuration, value => LobbyDuration = TimeSpan.FromSeconds(value), true); - _configurationManager.OnValueChanged(CCVars.GameDisallowLateJoins, invokeImmediately:true, - onValueChanged:value => { DisallowLateJoin = value; UpdateLateJoinStatus(); UpdateJobsAvailable(); }); + _configurationManager.OnValueChanged(CCVars.GameDisallowLateJoins, + value => { DisallowLateJoin = value; UpdateLateJoinStatus(); UpdateJobsAvailable(); }, true); + _configurationManager.OnValueChanged(CCVars.StationOffset, value => StationOffset = value, true); + _configurationManager.OnValueChanged(CCVars.MaxStationOffset, value => MaxStationOffset = value, true); } } } diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index e47c36c58a..e65c191f1c 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -11,6 +11,7 @@ using Robust.Server.Player; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Log; +using Robust.Shared.Maths; using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -62,6 +63,14 @@ namespace Content.Server.GameTicking throw new InvalidOperationException($"No grid found for map {map}"); } + if (StationOffset) + { + // Apply a random offset to the station grid entity. + var x = _robustRandom.NextFloat() * MaxStationOffset * 2 - MaxStationOffset; + var y = _robustRandom.NextFloat() * MaxStationOffset * 2 - MaxStationOffset; + EntityManager.GetEntity(grid.GridEntityId).Transform.LocalPosition = new Vector2(x, y); + } + DefaultGridId = grid.Index; _spawnPoint = grid.ToCoordinates(); @@ -280,7 +289,7 @@ namespace Content.Server.GameTicking } // Delete all entities. - foreach (var entity in _entityManager.GetEntities().ToList()) + foreach (var entity in EntityManager.GetEntities().ToList()) { // TODO: Maybe something less naive here? // FIXME: Actually, definitely. diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 840af56302..4d24d6249a 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -13,6 +13,7 @@ using Content.Server.Roles; using Content.Server.Spawners.Components; using Content.Server.Speech.Components; using Content.Shared.GameTicking; +using Content.Shared.Ghost; using Content.Shared.Inventory; using Content.Shared.Preferences; using Content.Shared.Roles; @@ -144,7 +145,8 @@ namespace Content.Server.GameTicking var mob = SpawnObserverMob(); mob.Name = name; - mob.GetComponent().CanReturnToBody = false; + var ghost = mob.GetComponent(); + EntitySystem.Get().SetCanReturnToBody(ghost, false); data.Mind.TransferTo(mob); _playersInLobby[player] = LobbyPlayerStatus.Observer; @@ -155,7 +157,7 @@ namespace Content.Server.GameTicking private IEntity SpawnPlayerMob(Job job, HumanoidCharacterProfile? profile, bool lateJoin = true) { var coordinates = lateJoin ? GetLateJoinSpawnPoint() : GetJobSpawnPoint(job.Prototype.ID); - var entity = _entityManager.SpawnEntity(PlayerPrototypeName, coordinates); + var entity = EntityManager.SpawnEntity(PlayerPrototypeName, coordinates); if (job.StartingGear != null) { @@ -175,7 +177,7 @@ namespace Content.Server.GameTicking private IEntity SpawnObserverMob() { var coordinates = GetObserverSpawnPoint(); - return _entityManager.SpawnEntity(ObserverPrototypeName, coordinates); + return EntityManager.SpawnEntity(ObserverPrototypeName, coordinates); } #endregion @@ -189,7 +191,7 @@ namespace Content.Server.GameTicking var equipmentStr = startingGear.GetGear(slot, profile); if (equipmentStr != string.Empty) { - var equipmentEntity = _entityManager.SpawnEntity(equipmentStr, entity.Transform.Coordinates); + var equipmentEntity = EntityManager.SpawnEntity(equipmentStr, entity.Transform.Coordinates); inventory.Equip(slot, equipmentEntity.GetComponent()); } } @@ -200,7 +202,7 @@ namespace Content.Server.GameTicking var inhand = startingGear.Inhand; foreach (var (hand, prototype) in inhand) { - var inhandEntity = _entityManager.SpawnEntity(prototype, entity.Transform.Coordinates); + var inhandEntity = EntityManager.SpawnEntity(prototype, entity.Transform.Coordinates); handsComponent.TryPickupEntity(hand, inhandEntity, checkActionBlocker: false); } } diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 5400e0348a..9ed3eda9da 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -72,7 +72,6 @@ namespace Content.Server.GameTicking UpdateRoundFlow(frameTime); } - [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapLoader _mapLoader = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; diff --git a/Content.Server/GameTicking/Presets/GamePreset.cs b/Content.Server/GameTicking/Presets/GamePreset.cs index e76d442ba5..922ce47c02 100644 --- a/Content.Server/GameTicking/Presets/GamePreset.cs +++ b/Content.Server/GameTicking/Presets/GamePreset.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Content.Server.Ghost.Components; using Content.Shared.Damage; using Content.Shared.Damage.Components; +using Content.Shared.Ghost; using Content.Shared.MobState; using Content.Shared.Preferences; using Robust.Server.Player; @@ -75,7 +76,7 @@ namespace Content.Server.GameTicking.Presets ghost.Name = mind.CharacterName ?? string.Empty; var ghostComponent = ghost.GetComponent(); - ghostComponent.CanReturnToBody = canReturn; + EntitySystem.Get().SetCanReturnToBody(ghostComponent, canReturn); if (canReturn) mind.Visit(ghost); diff --git a/Content.Server/Hands/Components/HandsComponent.cs b/Content.Server/Hands/Components/HandsComponent.cs index daf034ddb6..ccd22ab623 100644 --- a/Content.Server/Hands/Components/HandsComponent.cs +++ b/Content.Server/Hands/Components/HandsComponent.cs @@ -90,19 +90,15 @@ namespace Content.Server.Hands.Components protected override void HandlePickupAnimation(IEntity entity) { - var pickupDirection = Owner.Transform.WorldPosition; + var initialPosition = EntityCoordinates.FromMap(Owner.Transform.Coordinates.GetParent(Owner.EntityManager), entity.Transform.MapPosition); - var outermostEntity = entity; - while (outermostEntity.TryGetContainer(out var container)) //TODO: Use WorldPosition instead of this loop - outermostEntity = container.Owner; + var finalPosition = Owner.Transform.Coordinates.Position; - var initialPosition = outermostEntity.Transform.Coordinates; - - if (pickupDirection == initialPosition.ToMapPos(Owner.EntityManager)) + if (finalPosition.EqualsApprox(initialPosition.Position)) return; Owner.EntityManager.EntityNetManager!.SendSystemNetworkMessage( - new PickupAnimationMessage(entity.Uid, pickupDirection, initialPosition)); + new PickupAnimationMessage(entity.Uid, finalPosition, initialPosition)); } #region Pull/Disarm diff --git a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs index 74a128539c..c13d5574fa 100644 --- a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs @@ -6,7 +6,9 @@ using Content.Server.Hands.Components; using Content.Server.Items; using Content.Server.Kitchen.Components; using Content.Server.Power.Components; +using Content.Server.Stack; using Content.Server.UserInterface; +using Content.Server.Kitchen.Events; using Content.Shared.Chemistry.Solution; using Content.Shared.Interaction; using Content.Shared.Kitchen.Components; @@ -30,7 +32,7 @@ namespace Content.Server.Kitchen.EntitySystems { [Dependency] private readonly IEntityManager _entityManager = default!; - private Queue _uiUpdateQueue = new (); + private Queue _uiUpdateQueue = new(); public override void Initialize() { @@ -40,11 +42,17 @@ namespace Content.Server.Kitchen.EntitySystems SubscribeLocalEvent((_, component, _) => EnqueueUiUpdate(component)); SubscribeLocalEvent(OnInteractHand); SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(JuiceableScaling); + } + + private void JuiceableScaling(EntityUid uid, StackComponent component, JuiceableScalingEvent args) + { + args.Scalar *= component.Count; // multiply scalar by amount of items in stack } private void OnInteractUsing(EntityUid uid, ReagentGrinderComponent component, InteractUsingEvent args) { - if(args.Handled) return; + if (args.Handled) return; if (!args.User.TryGetComponent(out IHandsComponent? hands)) { @@ -58,7 +66,7 @@ namespace Content.Server.Kitchen.EntitySystems //First, check if user is trying to insert a beaker. //No promise it will be a beaker right now, but whatever. //Maybe this should whitelist "beaker" in the prototype id of heldEnt? - if(heldEnt.TryGetComponent(out SolutionContainerComponent? beaker) && beaker.Capabilities.HasFlag(SolutionContainerCaps.FitsInDispenser)) + if (heldEnt.TryGetComponent(out SolutionContainerComponent? beaker) && beaker.Capabilities.HasFlag(SolutionContainerCaps.FitsInDispenser)) { component.BeakerContainer.Insert(heldEnt); component.HeldBeaker = beaker; @@ -74,7 +82,7 @@ namespace Content.Server.Kitchen.EntitySystems } //Next, see if the user is trying to insert something they want to be ground/juiced. - if(!heldEnt.HasTag("Grindable") && !heldEnt.TryGetComponent(out JuiceableComponent? juice)) + if (!heldEnt.HasTag("Grindable") && !heldEnt.TryGetComponent(out JuiceableComponent? juice)) { //Entity did NOT pass the whitelist for grind/juice. //Wouldn't want the clown grinding up the Captain's ID card now would you? @@ -113,7 +121,7 @@ namespace Content.Server.Kitchen.EntitySystems private void EnqueueUiUpdate(ReagentGrinderComponent component) { - if(!_uiUpdateQueue.Contains(component)) _uiUpdateQueue.Enqueue(component); + if (!_uiUpdateQueue.Contains(component)) _uiUpdateQueue.Enqueue(component); } private void OnComponentInit(EntityUid uid, ReagentGrinderComponent component, ComponentInit args) @@ -138,12 +146,12 @@ namespace Content.Server.Kitchen.EntitySystems private void OnUIMessageReceived(EntityUid uid, ReagentGrinderComponent component, ServerBoundUserInterfaceMessage message) { - if(component.Busy) + if (component.Busy) { return; } - switch(message.Message) + switch (message.Message) { case SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage msg: if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered) break; @@ -158,7 +166,7 @@ namespace Content.Server.Kitchen.EntitySystems break; case SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage msg: - if(component.Chamber.ContainedEntities.Count > 0) + if (component.Chamber.ContainedEntities.Count > 0) { ClickSound(component); for (var i = component.Chamber.ContainedEntities.Count - 1; i >= 0; i--) @@ -234,7 +242,7 @@ namespace Content.Server.Kitchen.EntitySystems return; var beaker = component.BeakerContainer.ContainedEntity; - if(beaker is null) + if (beaker is null) return; component.BeakerContainer.Remove(beaker); @@ -258,7 +266,7 @@ namespace Content.Server.Kitchen.EntitySystems private void DoWork(ReagentGrinderComponent component, IEntity user, SharedReagentGrinderComponent.GrinderProgram program) { //Have power, are we busy, chamber has anything to grind, a beaker for the grounds to go? - if(!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered || component.Busy || component.Chamber.ContainedEntities.Count <= 0 || component.BeakerContainer.ContainedEntity == null || component.HeldBeaker == null) + if (!component.Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) || !receiver.Powered || component.Busy || component.Chamber.ContainedEntities.Count <= 0 || component.BeakerContainer.ContainedEntity == null || component.HeldBeaker == null) { return; } @@ -278,12 +286,14 @@ namespace Content.Server.Kitchen.EntitySystems { if (!item.HasTag("Grindable")) continue; if (!item.TryGetComponent(out var solution)) continue; - if (component.HeldBeaker.CurrentVolume + solution.CurrentVolume > component.HeldBeaker.MaxVolume) continue; + var juiceEvent = new JuiceableScalingEvent(); // default of scalar is always 1.0 + RaiseLocalEvent(item.Uid, juiceEvent, false); + if (component.HeldBeaker.CurrentVolume + solution.CurrentVolume * juiceEvent.Scalar > component.HeldBeaker.MaxVolume) continue; + solution.Solution.ScaleSolution(juiceEvent.Scalar); component.HeldBeaker.TryAddSolution(solution.Solution); solution.RemoveAllSolution(); item.Delete(); } - component.Busy = false; EnqueueUiUpdate(component); bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage()); @@ -297,7 +307,13 @@ namespace Content.Server.Kitchen.EntitySystems foreach (var item in component.Chamber.ContainedEntities.ToList()) { if (!item.TryGetComponent(out var juiceMe)) continue; - if (component.HeldBeaker.CurrentVolume + juiceMe.JuiceResultSolution.TotalVolume > component.HeldBeaker.MaxVolume) continue; + var juiceEvent = new JuiceableScalingEvent(); // default of scalar is always 1.0 + if (item.HasComponent()) + { + RaiseLocalEvent(item.Uid, juiceEvent); + } + if (component.HeldBeaker.CurrentVolume + juiceMe.JuiceResultSolution.TotalVolume * juiceEvent.Scalar > component.HeldBeaker.MaxVolume) continue; + juiceMe.JuiceResultSolution.ScaleSolution(juiceEvent.Scalar); component.HeldBeaker.TryAddSolution(juiceMe.JuiceResultSolution); item.Delete(); } diff --git a/Content.Server/Kitchen/Events/JuiceableScalingEvent.cs b/Content.Server/Kitchen/Events/JuiceableScalingEvent.cs new file mode 100644 index 0000000000..f904097e0a --- /dev/null +++ b/Content.Server/Kitchen/Events/JuiceableScalingEvent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.Kitchen.Events +{ + /// + /// Used in scaling amount of solution to extract in juicing + /// + public class JuiceableScalingEvent : EntityEventArgs + { + + public JuiceableScalingEvent() + { + Scalar = 1f; + } + + public float Scalar + { + get; + set; + } + + } +} diff --git a/Content.Server/Mind/Components/MindComponent.cs b/Content.Server/Mind/Components/MindComponent.cs index b724e8a10c..987e04db63 100644 --- a/Content.Server/Mind/Components/MindComponent.cs +++ b/Content.Server/Mind/Components/MindComponent.cs @@ -1,6 +1,7 @@ using Content.Server.GameTicking; using Content.Server.Ghost.Components; using Content.Shared.Examine; +using Content.Shared.Ghost; using Content.Shared.MobState; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -86,7 +87,7 @@ namespace Content.Server.Mind.Components { if (visiting.TryGetComponent(out GhostComponent? ghost)) { - ghost.CanReturnToBody = false; + EntitySystem.Get().SetCanReturnToBody(ghost, false); } Mind!.TransferTo(visiting); @@ -108,7 +109,7 @@ namespace Content.Server.Mind.Components var ghost = Owner.EntityManager.SpawnEntity("MobObserver", spawnPosition); var ghostComponent = ghost.GetComponent(); - ghostComponent.CanReturnToBody = false; + EntitySystem.Get().SetCanReturnToBody(ghostComponent, false); if (Mind != null) { diff --git a/Content.Server/Morgue/Components/MorgueEntityStorageComponent.cs b/Content.Server/Morgue/Components/MorgueEntityStorageComponent.cs index d61e8683bb..76317cd299 100644 --- a/Content.Server/Morgue/Components/MorgueEntityStorageComponent.cs +++ b/Content.Server/Morgue/Components/MorgueEntityStorageComponent.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Content.Server.Storage.Components; using Content.Shared.Body.Components; using Content.Shared.Directions; @@ -13,6 +14,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Maths; using Robust.Shared.Player; @@ -141,6 +143,20 @@ namespace Content.Server.Morgue.Components } } + protected override IEnumerable DetermineCollidingEntities() + { + if (_tray == null) + { + yield break; + } + + var entityLookup = IoCManager.Resolve(); + foreach (var entity in entityLookup.GetEntitiesIntersecting(_tray)) + { + yield return entity; + } + } + //Called every 10 seconds public void Update() { diff --git a/Content.Server/Placeable/PlaceableSurfaceComponent.cs b/Content.Server/Placeable/PlaceableSurfaceComponent.cs index e1ab83f897..1d83c21e0c 100644 --- a/Content.Server/Placeable/PlaceableSurfaceComponent.cs +++ b/Content.Server/Placeable/PlaceableSurfaceComponent.cs @@ -95,9 +95,9 @@ namespace Content.Server.Placeable } handComponent.Drop(eventArgs.Using); if (_placeCentered) - eventArgs.Using.Transform.WorldPosition = eventArgs.Target.Transform.WorldPosition + _positionOffset; + eventArgs.Using.Transform.LocalPosition = eventArgs.Target.Transform.LocalPosition + _positionOffset; else - eventArgs.Using.Transform.WorldPosition = eventArgs.ClickLocation.Position; + eventArgs.Using.Transform.Coordinates = eventArgs.ClickLocation; return true; } } diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index e855c9270c..007e931658 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -89,6 +89,7 @@ namespace Content.Server.Pointing.EntitySystems public bool TryPoint(ICommonSession? session, EntityCoordinates coords, EntityUid uid) { + var mapCoords = coords.ToMap(EntityManager); var player = (session as IPlayerSession)?.ContentData()?.Mind?.CurrentEntity; if (player == null) { @@ -115,14 +116,14 @@ namespace Content.Server.Pointing.EntitySystems if (_actionBlockerSystem.CanChangeDirection(player)) { - var diff = coords.ToMapPos(EntityManager) - player.Transform.MapPosition.Position; + var diff = mapCoords.Position - player.Transform.MapPosition.Position; if (diff.LengthSquared > 0.01f) { player.Transform.LocalRotation = new Angle(diff); } } - var arrow = EntityManager.SpawnEntity("pointingarrow", coords); + var arrow = EntityManager.SpawnEntity("pointingarrow", mapCoords); var layer = (int) VisibilityFlags.Normal; if (player.TryGetComponent(out VisibilityComponent? playerVisibility)) @@ -160,8 +161,14 @@ namespace Content.Server.Pointing.EntitySystems } else { - var tileRef = _mapManager.GetGrid(coords.GetGridId(EntityManager)).GetTileRef(coords); - var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId]; + TileRef? tileRef = null; + + if (_mapManager.TryFindGridAt(mapCoords, out var grid)) + { + tileRef = grid.GetTileRef(grid.WorldToTile(mapCoords.Position)); + } + + var tileDef = _tileDefinitionManager[tileRef?.Tile.TypeId ?? 0]; selfMessage = Loc.GetString("pointing-system-point-at-tile", ("tileName", tileDef.DisplayName)); diff --git a/Content.Server/PowerCell/Components/PowerCellComponent.cs b/Content.Server/PowerCell/Components/PowerCellComponent.cs index 5b0378bea7..34c76aa4d4 100644 --- a/Content.Server/PowerCell/Components/PowerCellComponent.cs +++ b/Content.Server/PowerCell/Components/PowerCellComponent.cs @@ -93,7 +93,7 @@ namespace Content.Server.PowerCell.Components { if (inDetailsRange) { - message.AddMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{CurrentCharge / MaxCharge * 100}:F0"))); + message.AddMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{CurrentCharge / MaxCharge * 100:F0}"))); } } diff --git a/Content.Server/Storage/Components/EntityStorageComponent.cs b/Content.Server/Storage/Components/EntityStorageComponent.cs index 451d189d59..c67fce33ec 100644 --- a/Content.Server/Storage/Components/EntityStorageComponent.cs +++ b/Content.Server/Storage/Components/EntityStorageComponent.cs @@ -444,7 +444,7 @@ namespace Content.Server.Storage.Components EmptyContents(); } - protected IEnumerable DetermineCollidingEntities() + protected virtual IEnumerable DetermineCollidingEntities() { var entityLookup = IoCManager.Resolve(); return entityLookup.GetEntitiesIntersecting(Owner); diff --git a/Content.Server/Throwing/ThrowHelper.cs b/Content.Server/Throwing/ThrowHelper.cs index 7230c6b534..ae4ec3d96d 100644 --- a/Content.Server/Throwing/ThrowHelper.cs +++ b/Content.Server/Throwing/ThrowHelper.cs @@ -69,7 +69,9 @@ namespace Content.Server.Throwing EntitySystem.Get().ThrownInteraction(user, entity); } - physicsComponent.ApplyLinearImpulse(direction.Normalized * strength * physicsComponent.Mass); + var impulseVector = direction.Normalized * strength * physicsComponent.Mass; + physicsComponent.ApplyLinearImpulse(impulseVector); + // Estimate time to arrival so we can apply OnGround status and slow it much faster. var time = (direction / strength).Length; @@ -96,7 +98,7 @@ namespace Content.Server.Throwing if (!msg.Cancelled) { - body.ApplyLinearImpulse(-direction * pushbackRatio); + body.ApplyLinearImpulse(-impulseVector * pushbackRatio); } } } diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index a4f3080583..253475235b 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -4,7 +4,7 @@ using Content.Shared.Emoting; using Content.Shared.Interaction.Events; using Content.Shared.Inventory.Events; using Content.Shared.Item; -using Content.Shared.Metabolism.Events; +using Content.Shared.Body.Metabolism; using Content.Shared.Movement; using Content.Shared.Speech; using Content.Shared.Throwing; diff --git a/Content.Shared/Atmos/Piping/EnabledAtmosDeviceVisuals.cs b/Content.Shared/Atmos/Piping/EnabledAtmosDeviceVisuals.cs index e95188cc0b..5f3883a25c 100644 --- a/Content.Shared/Atmos/Piping/EnabledAtmosDeviceVisuals.cs +++ b/Content.Shared/Atmos/Piping/EnabledAtmosDeviceVisuals.cs @@ -4,31 +4,37 @@ using Robust.Shared.Serialization; namespace Content.Shared.Atmos.Piping { [Serializable, NetSerializable] - public enum OutletInjectorVisuals + public enum OutletInjectorVisuals : byte { Enabled, } [Serializable, NetSerializable] - public enum PassiveVentVisuals + public enum PassiveVentVisuals : byte { Enabled, } [Serializable, NetSerializable] - public enum VentScrubberVisuals + public enum VentScrubberVisuals : byte { Enabled, } [Serializable, NetSerializable] - public enum ThermoMachineVisuals + public enum ThermoMachineVisuals : byte { Enabled, } [Serializable, NetSerializable] - public enum PressurePumpVisuals + public enum PressurePumpVisuals : byte + { + Enabled, + } + + [Serializable, NetSerializable] + public enum FilterVisuals : byte { Enabled, } diff --git a/Content.Shared/Metabolism/Events/ShiverAttemptEvent.cs b/Content.Shared/Body/Metabolism/ShiverAttemptEvent.cs similarity index 85% rename from Content.Shared/Metabolism/Events/ShiverAttemptEvent.cs rename to Content.Shared/Body/Metabolism/ShiverAttemptEvent.cs index da05d74ffd..d84630fa3c 100644 --- a/Content.Shared/Metabolism/Events/ShiverAttemptEvent.cs +++ b/Content.Shared/Body/Metabolism/ShiverAttemptEvent.cs @@ -1,6 +1,6 @@ using Robust.Shared.GameObjects; -namespace Content.Shared.Metabolism.Events +namespace Content.Shared.Body.Metabolism { public class ShiverAttemptEvent : CancellableEntityEventArgs { diff --git a/Content.Shared/Metabolism/Events/SweatAttemptEvent.cs b/Content.Shared/Body/Metabolism/SweatAttemptEvent.cs similarity index 85% rename from Content.Shared/Metabolism/Events/SweatAttemptEvent.cs rename to Content.Shared/Body/Metabolism/SweatAttemptEvent.cs index b920639aff..6c6180eb00 100644 --- a/Content.Shared/Metabolism/Events/SweatAttemptEvent.cs +++ b/Content.Shared/Body/Metabolism/SweatAttemptEvent.cs @@ -1,6 +1,6 @@ using Robust.Shared.GameObjects; -namespace Content.Shared.Metabolism.Events +namespace Content.Shared.Body.Metabolism { public class SweatAttemptEvent : CancellableEntityEventArgs { diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 3b2091492c..7491e4c63b 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -55,6 +55,19 @@ namespace Content.Shared.CCVar public static readonly CVarDef GameMap = CVarDef.Create("game.map", "Maps/saltern.yml", CVar.SERVERONLY); + /// + /// Whether a random position offset will be applied to the station on roundstart. + /// + public static readonly CVarDef StationOffset = + CVarDef.Create("game.station_offset", true); + + /// + /// When the default blueprint is loaded what is the maximum amount it can be offset from 0,0. + /// Does nothing without as true. + /// + public static readonly CVarDef MaxStationOffset = + CVarDef.Create("game.maxstationoffset", 1000.0f); + /// /// When enabled, guests will be assigned permanent UIDs and will have their preferences stored. /// diff --git a/Content.Shared/CharacterAppearance/HumanoidVisualLayersExtension.cs b/Content.Shared/CharacterAppearance/HumanoidVisualLayersExtension.cs index bec83f47df..7416420837 100644 --- a/Content.Shared/CharacterAppearance/HumanoidVisualLayersExtension.cs +++ b/Content.Shared/CharacterAppearance/HumanoidVisualLayersExtension.cs @@ -1,47 +1,90 @@ -using System; +using System; +using System.Collections.Generic; using Content.Shared.Body.Part; namespace Content.Shared.CharacterAppearance { public static class HumanoidVisualLayersExtension { - public static HumanoidVisualLayers? ToHumanoidLayer(this SharedBodyPartComponent part) + public static IEnumerable ToHumanoidLayers(this SharedBodyPartComponent part) { - return part.PartType switch + switch (part.PartType) { - BodyPartType.Other => null, - BodyPartType.Torso => HumanoidVisualLayers.Chest, - BodyPartType.Head => HumanoidVisualLayers.Head, - BodyPartType.Arm => part.Symmetry switch - { - BodyPartSymmetry.None => null, - BodyPartSymmetry.Left => HumanoidVisualLayers.LArm, - BodyPartSymmetry.Right => HumanoidVisualLayers.RArm, - _ => throw new ArgumentOutOfRangeException() - }, - BodyPartType.Hand => part.Symmetry switch - { - BodyPartSymmetry.None => null, - BodyPartSymmetry.Left => HumanoidVisualLayers.LHand, - BodyPartSymmetry.Right => HumanoidVisualLayers.RHand, - _ => throw new ArgumentOutOfRangeException() - }, - BodyPartType.Leg => part.Symmetry switch - { - BodyPartSymmetry.None => null, - BodyPartSymmetry.Left => HumanoidVisualLayers.LLeg, - BodyPartSymmetry.Right => HumanoidVisualLayers.RLeg, - _ => throw new ArgumentOutOfRangeException() - }, - BodyPartType.Foot => part.Symmetry switch - { - BodyPartSymmetry.None => null, - BodyPartSymmetry.Left => HumanoidVisualLayers.LFoot, - BodyPartSymmetry.Right => HumanoidVisualLayers.RFoot, - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new ArgumentOutOfRangeException() - }; + case BodyPartType.Other: + yield break; + case BodyPartType.Torso: + yield return HumanoidVisualLayers.Chest; + break; + case BodyPartType.Head: + yield return HumanoidVisualLayers.Head; + yield return HumanoidVisualLayers.Eyes; + yield return HumanoidVisualLayers.FacialHair; + yield return HumanoidVisualLayers.Hair; + yield return HumanoidVisualLayers.StencilMask; + break; + case BodyPartType.Arm: + switch (part.Symmetry) + { + case BodyPartSymmetry.None: + yield break; + case BodyPartSymmetry.Left: + yield return HumanoidVisualLayers.LArm; + break; + case BodyPartSymmetry.Right: + yield return HumanoidVisualLayers.RArm; + break; + default: + yield break; + } + yield break; + case BodyPartType.Hand: + switch (part.Symmetry) + { + case BodyPartSymmetry.None: + yield break; + case BodyPartSymmetry.Left: + yield return HumanoidVisualLayers.LHand; + break; + case BodyPartSymmetry.Right: + yield return HumanoidVisualLayers.RHand; + break; + default: + yield break; + } + yield break; + case BodyPartType.Leg: + switch (part.Symmetry) + { + case BodyPartSymmetry.None: + yield break; + case BodyPartSymmetry.Left: + yield return HumanoidVisualLayers.LLeg; + break; + case BodyPartSymmetry.Right: + yield return HumanoidVisualLayers.RLeg; + break; + default: + yield break; + } + yield break; + case BodyPartType.Foot: + switch (part.Symmetry) + { + case BodyPartSymmetry.None: + yield break; + case BodyPartSymmetry.Left: + yield return HumanoidVisualLayers.LFoot; + break; + case BodyPartSymmetry.Right: + yield return HumanoidVisualLayers.RFoot; + break; + default: + yield break; + } + yield break; + default: + yield break; + } } } } diff --git a/Content.Shared/Chemistry/Components/MovespeedModifierMetabolismComponent.cs b/Content.Shared/Chemistry/Components/MovespeedModifierMetabolismComponent.cs new file mode 100644 index 0000000000..afcd0f3f42 --- /dev/null +++ b/Content.Shared/Chemistry/Components/MovespeedModifierMetabolismComponent.cs @@ -0,0 +1,54 @@ +using Content.Shared.Movement.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Players; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using System; +using Robust.Shared.GameStates; +using Robust.Shared.Timing; +using Robust.Shared.IoC; + +namespace Content.Shared.Chemistry.Components +{ + //TODO: refactor movement modifier component because this is a pretty poor solution + [RegisterComponent] + [NetworkedComponent] + public sealed class MovespeedModifierMetabolismComponent : Component, IMoveSpeedModifier + { + [ViewVariables] + public override string Name => "MovespeedModifierMetabolism"; + + [ViewVariables] + public float WalkSpeedModifier { get; set; } + + [ViewVariables] + public float SprintSpeedModifier { get; set; } + + /// + /// When the current modifier is expected to end. + /// + [ViewVariables] + public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero; + + public override ComponentState GetComponentState(ICommonSession player) + { + return new MovespeedModifierMetabolismComponentState(WalkSpeedModifier, SprintSpeedModifier, ModifierTimer); + } + + [Serializable, NetSerializable] + public class MovespeedModifierMetabolismComponentState : ComponentState + { + public float WalkSpeedModifier { get; } + public float SprintSpeedModifier { get; } + public TimeSpan ModifierTimer { get; } + + public MovespeedModifierMetabolismComponentState(float walkSpeedModifier, float sprintSpeedModifier, TimeSpan modifierTimer) + { + WalkSpeedModifier = walkSpeedModifier; + SprintSpeedModifier = sprintSpeedModifier; + ModifierTimer = modifierTimer; + } + } + } +} + diff --git a/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs b/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs new file mode 100644 index 0000000000..e67f5ae7da --- /dev/null +++ b/Content.Shared/Chemistry/MetabolismMovespeedModifierSystem.cs @@ -0,0 +1,77 @@ +using Content.Shared.Chemistry.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.IoC; +using Robust.Shared.Timing; +using System.Collections.Generic; +using System.Linq; +using Content.Shared.Movement.Components; +using static Content.Shared.Chemistry.Components.MovespeedModifierMetabolismComponent; + +namespace Content.Shared.Chemistry +{ + public class MetabolismMovespeedModifierSystem : EntitySystem + { + [Dependency] private readonly IGameTiming _gameTiming = default!; + + private readonly List _components = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMovespeedHandleState); + SubscribeLocalEvent(AddComponent); + } + + private void OnMovespeedHandleState(EntityUid uid, MovespeedModifierMetabolismComponent component, ComponentHandleState args) + { + if (args.Current is not MovespeedModifierMetabolismComponentState cast) + return; + + if (ComponentManager.TryGetComponent(uid, out var modifier) && + (!component.WalkSpeedModifier.Equals(cast.WalkSpeedModifier) || + !component.SprintSpeedModifier.Equals(cast.SprintSpeedModifier))) + { + modifier.RefreshMovementSpeedModifiers(); + } + + component.WalkSpeedModifier = cast.WalkSpeedModifier; + component.SprintSpeedModifier = cast.SprintSpeedModifier; + component.ModifierTimer = cast.ModifierTimer; + + } + private void AddComponent(EntityUid uid, MovespeedModifierMetabolismComponent component, ComponentStartup args) + { + _components.Add(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var currentTime = _gameTiming.CurTime; + + for (var i = _components.Count - 1; i >= 0; i--) + { + var component = _components[i]; + + if (component.Deleted) + { + _components.RemoveAt(i); + continue; + } + + if (component.ModifierTimer > currentTime) continue; + + _components.RemoveAt(i); + ComponentManager.RemoveComponent(component.Owner.Uid); + + if (component.Owner.TryGetComponent(out MovementSpeedModifierComponent? modifier)) + { + modifier.RefreshMovementSpeedModifiers(); + } + } + } + } +} diff --git a/Content.Shared/Chemistry/Metabolizable/DefaultMetabolizable.cs b/Content.Shared/Chemistry/Metabolizable/DefaultMetabolizable.cs deleted file mode 100644 index 939acdd21a..0000000000 --- a/Content.Shared/Chemistry/Metabolizable/DefaultMetabolizable.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Content.Shared.Chemistry.Metabolizable -{ - /// - /// Default metabolization for reagents. Returns the amount of reagents metabolized without applying effects. - /// Metabolizes reagents at a constant rate, limited by how much is available. Other classes are derived from - /// this class, so that they do not need their own metabolization quantity calculation. - /// - [DataDefinition] - public class DefaultMetabolizable : IMetabolizable - { - /// - /// Rate of metabolism in units / second - /// - [DataField("rate")] public ReagentUnit MetabolismRate { get; set; } = ReagentUnit.New(1); - - public virtual ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent) - { - - // How much reagent should we metabolize - // The default behaviour is to metabolize at a constant rate, independent of the quantity of reagents. - var amountMetabolized = MetabolismRate * tickTime; - - // is that much reagent actually available? - if (availableReagent < amountMetabolized) - { - return availableReagent; - } - - return amountMetabolized; - } - } -} diff --git a/Content.Shared/Chemistry/Metabolizable/IMetabolizable.cs b/Content.Shared/Chemistry/Metabolizable/IMetabolizable.cs deleted file mode 100644 index f8b2a888cc..0000000000 --- a/Content.Shared/Chemistry/Metabolizable/IMetabolizable.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.GameObjects; - -namespace Content.Shared.Chemistry.Metabolizable -{ - /// - /// Metabolism behavior for a reagent. - /// - public interface IMetabolizable - { - /// - /// Metabolize the attached reagent. Return the amount of reagent to be removed from the solution. - /// You shouldn't remove the reagent yourself to avoid invalidating the iterator of the metabolism - /// organ that is processing it's reagents. - /// - /// The entity containing the solution. - /// The reagent id - /// The time since the last metabolism tick in seconds. - /// Reagent available to be metabolized. - /// The amount of reagent to be removed. The metabolizing organ should handle removing the reagent. - ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent); - } -} diff --git a/Content.Shared/Chemistry/Reagent/ReagentEffect.cs b/Content.Shared/Chemistry/Reagent/ReagentEffect.cs new file mode 100644 index 0000000000..78ca370df0 --- /dev/null +++ b/Content.Shared/Chemistry/Reagent/ReagentEffect.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Content.Shared.Chemistry.Reagent; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Shared.Chemistry.Reagent +{ + /// + /// Reagent effects describe behavior that occurs when a reagent is ingested and metabolized by some + /// organ. They only trigger when their conditions ( + /// + [ImplicitDataDefinitionForInheritors] + public abstract class ReagentEffect + { + /// + /// The list of conditions required for the effect to activate. Not required. + /// + [DataField("conditions")] + public ReagentEffectCondition[]? Conditions; + + public abstract void Metabolize(IEntity solutionEntity, Solution.Solution.ReagentQuantity amount); + } +} diff --git a/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs b/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs new file mode 100644 index 0000000000..54253d1c6d --- /dev/null +++ b/Content.Shared/Chemistry/Reagent/ReagentEffectCondition.cs @@ -0,0 +1,13 @@ +using Content.Shared.Body.Components; +using Content.Shared.Chemistry.Solution; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Shared.Chemistry.Reagent +{ + [ImplicitDataDefinitionForInheritors] + public abstract class ReagentEffectCondition + { + public abstract bool Condition(IEntity solutionEntity, Solution.Solution.ReagentQuantity reagent); + } +} diff --git a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs index 30de264604..321dacd497 100644 --- a/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs +++ b/Content.Shared/Chemistry/Reagent/ReagentPrototype.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Content.Shared.Botany; -using Content.Shared.Chemistry.Metabolizable; using Content.Shared.Chemistry.Reaction; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -16,9 +15,6 @@ namespace Content.Shared.Chemistry.Reagent [DataDefinition] public class ReagentPrototype : IPrototype { - [DataField("metabolism", serverOnly: true)] - private readonly List _metabolism = new() {new DefaultMetabolizable()}; - [DataField("tileReactions", serverOnly: true)] private readonly List _tileReactions = new(0); @@ -60,7 +56,6 @@ namespace Content.Shared.Chemistry.Reagent public string SpriteReplacementPath { get; } = string.Empty; //List of metabolism effects this reagent has, should really only be used server-side. - public IReadOnlyList Metabolism => _metabolism; public IReadOnlyList TileReactions => _tileReactions; public IReadOnlyList PlantMetabolism => _plantMetabolism; diff --git a/Content.Shared/Chemistry/Solution/Solution.cs b/Content.Shared/Chemistry/Solution/Solution.cs index ceae2a17f7..8b89f0631a 100644 --- a/Content.Shared/Chemistry/Solution/Solution.cs +++ b/Content.Shared/Chemistry/Solution/Solution.cs @@ -115,6 +115,27 @@ namespace Content.Shared.Chemistry.Solution TotalVolume += quantity; } + /// + /// Scales the amount of solution. + /// + /// The scalar to modify the solution by. + public void ScaleSolution(float scale) + { + if (scale == 1) return; + var tempContents = new List(_contents); + foreach(ReagentQuantity current in tempContents) + { + if(scale > 1) + { + AddReagent(current.ReagentId, current.Quantity * scale - current.Quantity); + } + else + { + RemoveReagent(current.ReagentId, current.Quantity - current.Quantity * scale); + } + } + } + /// /// Returns the amount of a single reagent inside the solution. /// diff --git a/Content.Shared/Doors/SharedDoorComponent.cs b/Content.Shared/Doors/SharedDoorComponent.cs index db6902c5c6..e85da53ff9 100644 --- a/Content.Shared/Doors/SharedDoorComponent.cs +++ b/Content.Shared/Doors/SharedDoorComponent.cs @@ -22,6 +22,9 @@ namespace Content.Shared.Doors [ComponentDependency] protected readonly IPhysBody? PhysicsComponent = null; + [Dependency] + protected readonly IGameTiming _gameTiming = default!; + [ViewVariables] private DoorState _state = DoorState.Closed; /// diff --git a/Content.Shared/Ghost/SharedGhostComponent.cs b/Content.Shared/Ghost/SharedGhostComponent.cs index 77806356ac..e335225911 100644 --- a/Content.Shared/Ghost/SharedGhostComponent.cs +++ b/Content.Shared/Ghost/SharedGhostComponent.cs @@ -16,8 +16,9 @@ namespace Content.Shared.Ghost public override string Name => "Ghost"; /// - /// Changed by + /// Changed by /// + // TODO MIRROR change this to use friend classes when thats merged [DataField("canReturnToBody")] [ViewVariables(VVAccess.ReadWrite)] public bool CanReturnToBody { get; set; } diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index 0099d34f37..7b54432150 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -10,36 +10,20 @@ namespace Content.Shared.Ghost public override void Initialize() { base.Initialize(); - - SubscribeLocalEvent(OnGhostChangeCanReturnToBody); } - private void OnGhostChangeCanReturnToBody(EntityUid uid, SharedGhostComponent component, GhostChangeCanReturnToBodyEvent args) + public void SetCanReturnToBody(SharedGhostComponent component, bool canReturn) { - if (component.CanReturnToBody == args.CanReturnToBody) + if (component.CanReturnToBody == canReturn) { return; } - component.CanReturnToBody = args.CanReturnToBody; + component.CanReturnToBody = canReturn; component.Dirty(); } } - /// - /// Raised to change the value of - /// - [Serializable, NetSerializable] - public class GhostChangeCanReturnToBodyEvent : EntityEventArgs - { - public GhostChangeCanReturnToBodyEvent(bool canReturnToBody) - { - CanReturnToBody = canReturnToBody; - } - - public bool CanReturnToBody { get; } - } - [Serializable, NetSerializable] public class GhostWarpsRequestEvent : EntityEventArgs { diff --git a/Content.Shared/Hands/Components/SharedHandsComponent.cs b/Content.Shared/Hands/Components/SharedHandsComponent.cs index 9e1978b06f..e98459a464 100644 --- a/Content.Shared/Hands/Components/SharedHandsComponent.cs +++ b/Content.Shared/Hands/Components/SharedHandsComponent.cs @@ -924,12 +924,12 @@ namespace Content.Shared.Hands.Components { public EntityUid EntityUid { get; } public EntityCoordinates InitialPosition { get; } - public Vector2 PickupDirection { get; } + public Vector2 FinalPosition { get; } - public PickupAnimationMessage(EntityUid entityUid, Vector2 pickupDirection, EntityCoordinates initialPosition) + public PickupAnimationMessage(EntityUid entityUid, Vector2 finalPosition, EntityCoordinates initialPosition) { EntityUid = entityUid; - PickupDirection = pickupDirection; + FinalPosition = finalPosition; InitialPosition = initialPosition; } } diff --git a/Content.Shared/Spawning/EntitySystemExtensions.cs b/Content.Shared/Spawning/EntitySystemExtensions.cs index 6ec0226358..a096d0f4d9 100644 --- a/Content.Shared/Spawning/EntitySystemExtensions.cs +++ b/Content.Shared/Spawning/EntitySystemExtensions.cs @@ -32,7 +32,7 @@ namespace Content.Shared.Spawning in Box2? box = null, SharedBroadphaseSystem? collision = null) { - var boxOrDefault = box.GetValueOrDefault(Box2.UnitCentered); + var boxOrDefault = box.GetValueOrDefault(Box2.UnitCentered).Translated(coordinates.Position); collision ??= EntitySystem.Get(); foreach (var body in collision.GetCollidingEntities(coordinates.MapId, in boxOrDefault)) diff --git a/Content.Shared/SubFloor/SubFloorHideComponent.cs b/Content.Shared/SubFloor/SubFloorHideComponent.cs index 93587e6f8c..100903c855 100644 --- a/Content.Shared/SubFloor/SubFloorHideComponent.cs +++ b/Content.Shared/SubFloor/SubFloorHideComponent.cs @@ -1,4 +1,8 @@ +using System; using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Players; +using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -10,6 +14,7 @@ namespace Content.Shared.SubFloor /// (plating). /// /// + [NetworkedComponent] [RegisterComponent] public sealed class SubFloorHideComponent : Component { @@ -17,10 +22,35 @@ namespace Content.Shared.SubFloor public override string Name => "SubFloorHide"; /// - /// This entity needs to be anchored to be hid in the subfloor. + /// Whether the entity will be hid when not in subfloor. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("enabled")] + public bool Enabled { get; set; } = true; + + /// + /// This entity needs to be anchored to be hid when not in subfloor. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("requireAnchored")] public bool RequireAnchored { get; set; } = true; + + public override ComponentState GetComponentState(ICommonSession player) + { + return new SubFloorHideComponentState(Enabled, RequireAnchored); + } + } + + [Serializable, NetSerializable] + public class SubFloorHideComponentState : ComponentState + { + public bool Enabled { get; } + public bool RequireAnchored { get; } + + public SubFloorHideComponentState(bool enabled, bool requireAnchored) + { + Enabled = enabled; + RequireAnchored = requireAnchored; + } } } diff --git a/Content.Shared/SubFloor/SubFloorHideSystem.cs b/Content.Shared/SubFloor/SubFloorHideSystem.cs index 6f3f5bd9f2..d729e2b31a 100644 --- a/Content.Shared/SubFloor/SubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SubFloorHideSystem.cs @@ -1,9 +1,12 @@ +using System; using Content.Shared.Maps; using JetBrains.Annotations; using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; +using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Shared.SubFloor @@ -42,6 +45,7 @@ namespace Content.Shared.SubFloor SubscribeLocalEvent(OnSubFloorStarted); SubscribeLocalEvent(OnSubFloorTerminating); SubscribeLocalEvent(HandleAnchorChanged); + SubscribeLocalEvent(HandleComponentState); } public override void Shutdown() @@ -52,6 +56,20 @@ namespace Content.Shared.SubFloor _mapManager.TileChanged -= MapManagerOnTileChanged; } + public void SetEnabled(SubFloorHideComponent subFloor, bool enabled) + { + subFloor.Enabled = enabled; + subFloor.Dirty(); + UpdateEntity(subFloor.Owner.Uid); + } + + public void SetRequireAnchoring(SubFloorHideComponent subFloor, bool requireAnchored) + { + subFloor.RequireAnchored = requireAnchored; + subFloor.Dirty(); + UpdateEntity(subFloor.Owner.Uid); + } + private void OnSubFloorStarted(EntityUid uid, SubFloorHideComponent component, ComponentStartup _) { UpdateEntity(uid); @@ -71,6 +89,16 @@ namespace Content.Shared.SubFloor UpdateEntity(uid); } + private void HandleComponentState(EntityUid uid, SubFloorHideComponent component, ComponentHandleState args) + { + if (args.Current is not SubFloorHideComponentState state) + return; + + component.Enabled = state.Enabled; + component.RequireAnchored = state.RequireAnchored; + UpdateEntity(uid); + } + private void MapManagerOnTileChanged(object? sender, TileChangedEventArgs e) { UpdateTile(_mapManager.GetGrid(e.NewTile.GridIndex), e.NewTile.GridIndices); @@ -134,22 +162,36 @@ namespace Content.Shared.SubFloor if (subFloorHideEvent.Handled) return; - // This might look weird, but basically we only need to query the SubFloorHide and Transform components - // if we are gonna hide the entity and we require it to be anchored to be hidden. Because getting components - // is "expensive", we have a slow path where we query them, and a fast path where we don't. - if (!subFloor - && ComponentManager.TryGetComponent(uid, out SubFloorHideComponent? subFloorHideComponent) && - subFloorHideComponent.RequireAnchored - && ComponentManager.TryGetComponent(uid, out ITransformComponent? transformComponent)) + // We only need to query the subfloor component to check if it's enabled or not when we're not on subfloor. + // Getting components is expensive, after all. + if (!subFloor && ComponentManager.TryGetComponent(uid, out SubFloorHideComponent? subFloorHideComponent)) { - // If we require the entity to be anchored but it's not, this will set subfloor to true, unhiding it. - subFloor = !transformComponent.Anchored; + // If the component isn't enabled, then subfloor will always be true, and the entity will be shown. + if (!subFloorHideComponent.Enabled) + { + subFloor = true; + } + // We only need to query the TransformComp if the SubfloorHide is enabled and requires anchoring. + else if (subFloorHideComponent.RequireAnchored && ComponentManager.TryGetComponent(uid, out ITransformComponent? transformComponent)) + { + // If we require the entity to be anchored but it's not, this will set subfloor to true, unhiding it. + subFloor = !transformComponent.Anchored; + } } + // Whether to show this entity as visible, visually. + var subFloorVisible = ShowAll || subFloor; + // Show sprite if (ComponentManager.TryGetComponent(uid, out SharedSpriteComponent? spriteComponent)) { - spriteComponent.Visible = ShowAll || subFloor; + spriteComponent.Visible = subFloorVisible; + } + + // Set an appearance data value so visualizers can use this as needed. + if (ComponentManager.TryGetComponent(uid, out SharedAppearanceComponent? appearanceComponent)) + { + appearanceComponent.SetData(SubFloorVisuals.SubFloor, subFloorVisible); } // So for collision all we care about is that the component is running. @@ -169,4 +211,10 @@ namespace Content.Shared.SubFloor SubFloor = subFloor; } } + + [Serializable, NetSerializable] + public enum SubFloorVisuals : byte + { + SubFloor, + } } diff --git a/Resources/Audio/Machines/windoor_open.ogg b/Resources/Audio/Machines/windoor_open.ogg new file mode 100644 index 0000000000..26f094b597 Binary files /dev/null and b/Resources/Audio/Machines/windoor_open.ogg differ diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 085c58872c..38639aee97 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1620,3 +1620,122 @@ Entries: pulling in your hand!, type: Tweak} id: 288 time: '2021-07-31T01:14:00.0000000+00:00' +- author: plinyvic + changes: + - {message: Fixed reagent grinder not giving correct output with stacked items, + type: Fix} + id: 289 + time: '2021-07-31T12:06:25.0000000+00:00' +- author: michaelcooke + changes: + - {message: Throwing items in space will now push you in the opposite direction + to the thrown object, type: Fix} + id: 290 + time: '2021-07-31T12:53:18.0000000+00:00' +- author: Seth + changes: + - {message: Made plasma Grindable, type: Tweak} + id: 291 + time: '2021-08-01T19:05:02.0000000+00:00' +- author: mirrorcult + changes: + - {message: Door sounds no longer play twice sometimes., type: Fix} + id: 292 + time: '2021-08-02T11:57:06.0000000+00:00' +- author: scaly-chimp + changes: + - {message: Added everyone's favorite stimulant meth!, type: Add} + id: 293 + time: '2021-08-03T03:32:08.0000000+00:00' +- author: metalgearsloth + changes: + - {message: 'Station will spawn at a random position rather than 0,0.', type: Tweak} + id: 294 + time: '2021-08-03T08:49:25.0000000+00:00' +- author: metalgearsloth + changes: + - {message: Fix lerping for other physics bodies., type: Fix} + - {message: Fix joint prediction., type: Fix} + id: 295 + time: '2021-08-03T15:02:44.0000000+00:00' +- author: Zumorica + changes: + - {message: Fixes mobs not taking pressure damage on space., type: Fix} + id: 296 + time: '2021-08-04T07:49:53.509340+00:00' +- author: SweptWasTaken + changes: + - {message: Added an alternate jumpsuit for Botany, type: Add} + id: 297 + time: '2021-08-05T10:18:05.0000000+00:00' +- author: Seth + changes: + - {message: Added two new maintenance areas, type: Add} + id: 298 + time: '2021-08-05T22:20:58.0000000+00:00' +- author: mirrorcult + changes: + - {message: Windoors and secure windoors have been added and are constructible., + type: Add} + id: 299 + time: '2021-08-06T06:09:39.0000000+00:00' +- author: mirrorcult + changes: + - {message: Ghosts can now properly return to their body if applicable., type: Fix} + id: 300 + time: '2021-08-06T07:02:36.0000000+00:00' +- author: Zumorica + changes: + - {message: 'Fixes a bug where items would disappear when placed in tables, in certain + situations.', type: Fix} + id: 301 + time: '2021-08-06T15:53:48.856573+00:00' +- author: Zumorica + changes: + - {message: Fixes pickup animation not showing under certain conditions., type: Fix} + id: 302 + time: '2021-08-06T16:13:07.405459+00:00' +- author: Zumorica + changes: + - {message: Fix pointing at an item in your inventory resulting in the pointing + arrow playing a weird animation., type: Fix} + id: 303 + time: '2021-08-06T16:28:43.947928+00:00' +- author: Zumorica + changes: + - {message: Fix bug where you couldn't point at space., type: Fix} + id: 304 + time: '2021-08-06T16:28:43.948705+00:00' +- author: Seth + changes: + - {message: Added a hardsuit into CMO's Locker, type: Add} + id: 305 + time: '2021-08-06T23:11:18.0000000+00:00' +- author: SweptWasTaken + changes: + - {message: Maps in windoors, type: Add} + id: 306 + time: '2021-08-07T02:47:43.0000000+00:00' +- author: scrato + changes: + - {message: 'When the head is detached from the body, the hair, eyes and ears will + not remain at the body.', type: Add} + id: 307 + time: '2021-08-07T21:18:43.0000000+00:00' +- author: TimrodDX + changes: + - {message: Security windoors for planned mapping rework., type: Add} + id: 308 + time: '2021-08-07T21:29:47.0000000+00:00' +- author: TimrodDX + changes: + - {message: Partially re-mapped Security and Chemistry., type: Add} + id: 309 + time: '2021-08-08T00:32:18.0000000+00:00' +- author: Seth + changes: + - {message: Added cloning pod and medical scanner circuit boards into the protolathe, + type: Add} + - {message: Made the cloning pod and medical scanner constructible, type: Add} + id: 310 + time: '2021-08-08T20:21:19.0000000+00:00' diff --git a/Resources/Locale/en-US/chemistry/solution/components/shared-solution-container-component.ftl b/Resources/Locale/en-US/chemistry/solution/components/shared-solution-container-component.ftl index 0c836a6b11..4581dcc762 100644 --- a/Resources/Locale/en-US/chemistry/solution/components/shared-solution-container-component.ftl +++ b/Resources/Locale/en-US/chemistry/solution/components/shared-solution-container-component.ftl @@ -1,4 +1,4 @@ shared-solution-container-component-on-examine-empty-container = Contains no chemicals. -shared-solution-container-component-on-examine-main-text = It contains a [color={$color}]{$desc}[/color] {wordedAmount} +shared-solution-container-component-on-examine-main-text = It contains a [color={$color}]{$desc}[/color] {$wordedAmount} shared-solution-container-component-on-examine-worded-amount-one-reagent = chemical. -shared-solution-container-component-on-examine-worded-amount-multiple-reagents = mixture of chemicals. \ No newline at end of file +shared-solution-container-component-on-examine-worded-amount-multiple-reagents = mixture of chemicals. diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index c7ccddd253..d892095b3e 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -46,27 +46,27 @@ grids: - ind: "-1,-1" tiles: GAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAIQAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAYAAAAGAAAACEAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAhAAAAIQAAABgAAAAhAAAAIQAAACEAAAAgAAAAGAAAABgAAAAhAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGAAAABgAAAAYAAAAGAAAABgAAAAhAAAAIAAAABgAAAAYAAAAIQAAABsAAAAbAAAAGwAAACEAAAAhAAAAIQAAABgAAAAYAAAAGAAAABgAAAAYAAAAIQAAACAAAAAYAAAAGAAAACEAAAAhAAAAGwAAACEAAAAhAAAAIAAAACAAAAAYAAAAGAAAABgAAAAYAAAAGAAAACEAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAGAAAABgAAAAYAAAAGAAAABgAAAAhAAAAIAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAIQAAABgAAAAhAAAAIQAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAGAAAABgAAAAYAAAAGAAAABgAAAAeAAAAHgAAAB4AAAAeAAAAGAAAAA8AAAAPAAAADwAAAA8AAAAPAAAAIQAAABgAAAAYAAAAIAAAABgAAAAgAAAAHgAAAB4AAAAeAAAAHgAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAACEAAAAYAAAAGAAAACAAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAADwAAAA8AAAAPAAAADwAAAA8AAAAhAAAAIQAAACEAAAAgAAAAFgAAAAoAAAAKAAAACgAAAAoAAAAKAAAAIQAAAA8AAAAPAAAADwAAAA8AAAAPAAAAHgAAAB4AAAAeAAAAIQAAACEAAAAKAAAACgAAAAoAAAAKAAAACgAAACEAAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAACEAAAAhAAAAIQAAABYAAAAhAAAAIQAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAGwAAABsAAAAbAAAAGwAAABsAAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAIQAAABsAAAAbAAAAGwAAABsAAAAWAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAA== - ind: "-1,0" - tiles: FgAAABsAAAAbAAAAGwAAABsAAAAWAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAACEAAAAbAAAAGwAAABsAAAAbAAAAFgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAhAAAAFgAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAACAAAAAhAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAIQAAABYAAAAgAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAACEAAAAWAAAAIAAAACEAAAAWAAAAFgAAABYAAAAhAAAAFgAAACEAAAAhAAAAFgAAABYAAAAhAAAAIQAAABYAAAAhAAAAIQAAACAAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAgAAAAGAAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAAA== + tiles: FgAAABsAAAAbAAAAGwAAABsAAAAWAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAACEAAAAbAAAAGwAAABsAAAAbAAAAFgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAABYAAAAWAAAAIAAAABYAAAAWAAAAIAAAACAAAAAgAAAAFgAAABYAAAAgAAAAFgAAACAAAAAWAAAAFgAAACAAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAgAAAAIAAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAgAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIAAAACAAAAAgAAAAFgAAACAAAAAWAAAAIAAAABYAAAAgAAAAIAAAABYAAAAWAAAAIAAAACAAAAAgAAAAIAAAACAAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAgAAAAGAAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAAA== - ind: "0,-1" tiles: GwAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABsAAAAbAAAAGwAAABsAAAAhAAAAGwAAABsAAAAbAAAAGwAAABsAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAABsAAAAbAAAAGwAAABsAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAGwAAABsAAAAbAAAAGwAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAACEAAAAbAAAAIQAAACEAAAAhAAAAGwAAABsAAAAbAAAAIQAAACAAAAAhAAAAFgAAABYAAAAWAAAAIQAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAgAAAAIQAAABYAAAAWAAAAFgAAACEAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAIAAAACEAAAAWAAAAFgAAABYAAAAhAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAACAAAAAhAAAAFgAAABYAAAAWAAAAIQAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAgAAAAGAAAABYAAAAWAAAAFgAAACEAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGAAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAABsAAAAhAAAAIQAAABsAAAAbAAAAIQAAACEAAAAbAAAAGwAAACEAAAAhAAAAFgAAABYAAAAWAAAAIQAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAhAAAAGwAAABsAAAAeAAAAIQAAABYAAAAWAAAAFgAAACEAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAIQAAABsAAAAbAAAAHgAAACEAAAAWAAAAFgAAABYAAAAhAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAACEAAAAbAAAAGwAAAB4AAAAWAAAAFgAAABYAAAAWAAAAFgAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAeAAAAFgAAABYAAAAWAAAAFgAAABYAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAHgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAAA== - ind: "0,0" - tiles: HgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAAB4AAAAhAAAAFgAAABYAAAAWAAAAIQAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAhAAAAIQAAABYAAAAWAAAAFgAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAABYAAAAhAAAAFgAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAhAAAAFgAAACEAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAACEAAAAYAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAIQAAACAAAAAgAAAAIAAAACAAAAAgAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAIQAAABYAAAAWAAAAFgAAABgAAAAgAAAAGAAAABgAAAAYAAAAIAAAACAAAAAgAAAAGAAAABgAAAAYAAAAIQAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIAAAABgAAAAhAAAAGAAAAA== + tiles: HgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAAB4AAAAhAAAAFgAAABYAAAAWAAAAIQAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAhAAAAIQAAABYAAAAWAAAAFgAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIAAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAABYAAAAhAAAAFgAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAhAAAAFgAAACEAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAACEAAAAYAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAIQAAACAAAAAgAAAAIAAAACAAAAAgAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAIQAAABYAAAAWAAAAFgAAABgAAAAgAAAAGAAAABgAAAAYAAAAIAAAACAAAAAgAAAAGAAAABgAAAAYAAAAIQAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIAAAABgAAAAhAAAAGAAAAA== - ind: "1,0" - tiles: GwAAABsAAAAbAAAAIQAAACAAAAAYAAAAGAAAABgAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABsAAAAbAAAAGwAAACEAAAAgAAAAIAAAABgAAAAYAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAABgAAAAhAAAAIQAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAGAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAIAAAACAAAAAgAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGAAAACAAAAAYAAAAIAAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAYAAAAGAAAABgAAAAYAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAGAAAABgAAAAYAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABgAAAAYAAAAGAAAACEAAAAWAAAAFgAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAgAAAAIAAAACAAAAAhAAAAFgAAABYAAAAYAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIQAAAA== + tiles: GwAAABsAAAAbAAAAGwAAACAAAAAYAAAAGAAAABgAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABsAAAAbAAAAGwAAABsAAAAgAAAAIAAAABgAAAAYAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAABgAAAAhAAAAIQAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAGAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAIAAAACAAAAAgAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGAAAACAAAAAYAAAAIAAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAYAAAAGAAAABgAAAAYAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAGAAAABgAAAAYAAAAIQAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABgAAAAYAAAAGAAAACEAAAAWAAAAFgAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAgAAAAIAAAACAAAAAhAAAAFgAAABYAAAAYAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIQAAAA== - ind: "0,1" - tiles: IQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAhAAAAIAAAABgAAAAhAAAAGAAAABgAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAACEAAAAgAAAAGAAAACEAAAAAAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAhAAAAIAAAABgAAAAhAAAAAAAAABgAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGAAAACAAAAAYAAAAIQAAAAAAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAACEAAAAYAAAAGAAAACEAAAAAAAAAIQAAACEAAAAWAAAAIQAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAGAAAACEAAAAhAAAAAAAAAA8AAAAhAAAAFgAAABYAAAAWAAAAIQAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAGAAAABgAAAAYAAAAIQAAAAAAAAAPAAAADwAAABYAAAAWAAAAFgAAACEAAAAeAAAAHgAAAB4AAAAeAAAAIQAAACAAAAAgAAAAIAAAACEAAAAAAAAADwAAAA8AAAAWAAAAFgAAABYAAAAWAAAAHgAAAB4AAAAeAAAAHgAAACEAAAAhAAAAIQAAACEAAAAhAAAAAAAAAA8AAAAhAAAAFgAAABYAAAAWAAAAIQAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIQAAABYAAAAWAAAAFgAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: IQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAhAAAAIAAAABgAAAAhAAAAGAAAABgAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAACEAAAAgAAAAGAAAACEAAAAAAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAhAAAAIAAAABgAAAAhAAAAAAAAABgAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGAAAACAAAAAYAAAAIQAAAAAAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAACEAAAAYAAAAGAAAACEAAAAAAAAAIQAAACEAAAAWAAAAIQAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAGAAAACEAAAAhAAAAAAAAAA8AAAAhAAAAFgAAABYAAAAWAAAAIQAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAGAAAABgAAAAYAAAAIQAAAAAAAAAPAAAADwAAABYAAAAWAAAAFgAAACEAAAAeAAAAHgAAAB4AAAAeAAAAIQAAACAAAAAgAAAAIAAAACEAAAAAAAAADwAAAA8AAAAWAAAAFgAAABYAAAAWAAAAHgAAAB4AAAAeAAAAHgAAACEAAAAhAAAAIQAAACEAAAAhAAAAAAAAAA8AAAAhAAAAFgAAABYAAAAWAAAAIQAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAFAAAABQAAAAhAAAAAAAAAAAAAAAhAAAAIQAAABYAAAAWAAAAFgAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAAAAAAAAAAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - ind: "-2,0" - tiles: IQAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAFgAAABYAAAAWAAAAFgAAACEAAAAOAAAAFgAAABYAAAAWAAAAFgAAACEAAAAeAAAAHgAAAB4AAAAeAAAAIQAAACEAAAAWAAAAFgAAACEAAAAhAAAADgAAAA4AAAAOAAAADgAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAACEAAAAhAAAAIQAAACEAAAAhAAAAGAAAACAAAAAhAAAAFgAAABYAAAAWAAAAIQAAACEAAAAWAAAAFgAAACEAAAAhAAAAFgAAABYAAAAhAAAAIAAAACAAAAAYAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAIQAAACAAAAAYAAAAGAAAACEAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAgAAAAGAAAABgAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAhAAAAIAAAABgAAAAYAAAAIQAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAhAAAAIQAAABgAAAAhAAAAIQAAACAAAAAgAAAAGAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAABgAAAAgAAAAGAAAABgAAAAgAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAIAAAACAAAAAgAAAAIAAAABgAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAhAAAAIQAAACAAAAAYAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACAAAAAgAAAAGAAAAA== + tiles: IQAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAFgAAABYAAAAWAAAAFgAAACEAAAAOAAAAFgAAABYAAAAWAAAAFgAAACEAAAAeAAAAHgAAAB4AAAAeAAAAIQAAACEAAAAWAAAAFgAAACEAAAAhAAAADgAAAA4AAAAOAAAADgAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAACEAAAAhAAAAIQAAACEAAAAhAAAAGAAAACAAAAAhAAAAFgAAABYAAAAWAAAAIQAAACEAAAAWAAAAFgAAACEAAAAhAAAAFgAAABYAAAAhAAAAIAAAACAAAAAYAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAIQAAACAAAAAYAAAAGAAAACEAAAAWAAAAFgAAABYAAAAWAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAgAAAAGAAAABgAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAWAAAAFgAAABYAAAAhAAAAIAAAABgAAAAYAAAAIQAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAhAAAAIQAAABgAAAAhAAAAIQAAACAAAAAgAAAAGAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAABgAAAAgAAAAGAAAABgAAAAgAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAIAAAACAAAAAgAAAAIAAAABgAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAhAAAAIQAAACAAAAAYAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACAAAAAgAAAAGAAAAA== - ind: "1,-1" - tiles: GwAAABsAAAAbAAAAGwAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAABsAAAAbAAAAGwAAABsAAAAhAAAAGwAAABsAAAAbAAAAGwAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAhAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAIQAAACEAAAAbAAAAGwAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAABsAAAAbAAAAGwAAABsAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAbAAAAGwAAABsAAAAbAAAAGAAAACAAAAAYAAAAGAAAABgAAAAYAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAGwAAABsAAAAbAAAAGwAAACEAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAABsAAAAbAAAAGwAAABsAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAgAAAAGAAAACEAAAAhAAAAIQAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAABsAAAAbAAAAGwAAABsAAAAhAAAAIAAAABgAAAAhAAAAIAAAACAAAAAgAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAIQAAACAAAAAgAAAAIAAAACAAAAAYAAAAGAAAABsAAAAbAAAAGwAAABsAAAAhAAAAGwAAABsAAAAbAAAAGwAAACEAAAAgAAAAGAAAACEAAAAhAAAAIQAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAABsAAAAbAAAAGwAAABsAAAAhAAAAIAAAABgAAAAhAAAAFgAAABYAAAAWAAAAIQAAABsAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACAAAAAYAAAAIQAAABYAAAAWAAAAFgAAABsAAAAbAAAAGwAAACEAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAGAAAACEAAAAWAAAAFgAAABYAAAAbAAAAGwAAABsAAAAhAAAAIAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAhAAAAFgAAABYAAAAWAAAAGwAAABsAAAAbAAAAIQAAACAAAAAYAAAAGAAAACAAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAAA== + tiles: GwAAABsAAAAbAAAAGwAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAABsAAAAbAAAAGwAAABsAAAAhAAAAGwAAABsAAAAbAAAAGwAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAhAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAIQAAACEAAAAbAAAAGwAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAABsAAAAbAAAAGwAAABsAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAbAAAAGwAAABsAAAAbAAAAGAAAACAAAAAYAAAAGAAAABgAAAAYAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAGwAAABsAAAAbAAAAGwAAACEAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAABsAAAAbAAAAGwAAABsAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAgAAAAGAAAACEAAAAhAAAAIQAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAABsAAAAbAAAAGwAAABsAAAAhAAAAIAAAABgAAAAhAAAAIAAAACAAAAAgAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAGwAAABsAAAAbAAAAIQAAACAAAAAgAAAAIAAAACAAAAAYAAAAGAAAABsAAAAbAAAAGwAAABsAAAAhAAAAGwAAABsAAAAbAAAAGwAAACEAAAAgAAAAGAAAACEAAAAhAAAAIQAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAABsAAAAbAAAAGwAAABsAAAAhAAAAIAAAABgAAAAhAAAAFgAAABYAAAAWAAAAIQAAABsAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACAAAAAYAAAAIQAAABYAAAAWAAAAFgAAABsAAAAbAAAAGwAAABsAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAGAAAACEAAAAWAAAAFgAAABYAAAAbAAAAGwAAABsAAAAbAAAAIAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAhAAAAFgAAABYAAAAWAAAAGwAAABsAAAAbAAAAGwAAACAAAAAYAAAAGAAAACAAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAFgAAAA== - ind: "0,-2" tiles: AAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAAAAAAAAAAAAAAAAAhAAAAFgAAABYAAAAWAAAAIQAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAAAAAAAAAAAAAAAAAIQAAABYAAAAWAAAAFgAAACEAAAAeAAAAHgAAAB4AAAAeAAAAIQAAAB4AAAAeAAAAIQAAAAAAAAAAAAAAIQAAACEAAAAWAAAAFgAAABYAAAAhAAAAHgAAAB4AAAAeAAAAHgAAACEAAAAhAAAAIQAAACEAAAAAAAAAAAAAABgAAAAhAAAAFgAAABYAAAAWAAAAIQAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAAAAAAAAAAAAYAAAAIQAAABYAAAAWAAAAFgAAACEAAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAIQAAACEAAAAhAAAAGAAAACEAAAAWAAAAFgAAABYAAAAhAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAACEAAAAgAAAAIAAAABgAAAAhAAAAFgAAABYAAAAWAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAhAAAAIAAAABgAAAAgAAAAGAAAABYAAAAWAAAAFgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAIQAAACAAAAAYAAAAIQAAACEAAAAWAAAAFgAAABYAAAAhAAAAIQAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAHgAAACEAAAAgAAAAGAAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAhAAAAGAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIAAAABgAAAAbAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAYAAAAGwAAABYAAAAWAAAAFgAAABYAAAAWAAAAGAAAACAAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAgAAAAGAAAACEAAAAWAAAAFgAAABYAAAAWAAAAFgAAACEAAAAYAAAAGAAAABgAAAAYAAAAIQAAACEAAAAhAAAAGAAAACEAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAbAAAAGwAAABsAAAAbAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABsAAAAbAAAAGwAAABsAAAAhAAAAGwAAABsAAAAbAAAAGwAAAA== - ind: "1,-2" - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAACAAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIQAAACEAAAAhAAAAIQAAABgAAAAgAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAABsAAAAbAAAAGwAAACEAAAAYAAAAIAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAbAAAAGwAAABsAAAAhAAAAGAAAACAAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAGwAAABsAAAAbAAAAIQAAABgAAAAgAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAABsAAAAbAAAAGwAAACEAAAAYAAAAIAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAAAAAAAAAAAAAAAAAACEAAAAhAAAAIQAAABsAAAAhAAAAGAAAACAAAAAYAAAAGAAAABgAAAAYAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAbAAAAGwAAABsAAAAbAAAAIQAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAGwAAABsAAAAbAAAAGwAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAABgAAAAYAAAAGAAAABgAAAAYAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACEAAAAYAAAAGAAAABgAAAAYAAAAGAAAAAAAAAAAAAAAAAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAACAAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAAAAAAAAAAAAAAAAAhAAAAIQAAACEAAAAhAAAAIQAAABgAAAAgAAAAIQAAABgAAAAYAAAAGAAAABgAAAAYAAAAAAAAAAAAAAAAAAAAIQAAABsAAAAbAAAAGwAAACEAAAAYAAAAIAAAACEAAAAYAAAAGAAAABgAAAAYAAAAGAAAAAAAAAAAAAAAAAAAACEAAAAbAAAAGwAAABsAAAAhAAAAGAAAACAAAAAhAAAAGAAAABgAAAAYAAAAGAAAABgAAAAAAAAAAAAAAAAAAAAgAAAAGwAAABsAAAAbAAAAIQAAABgAAAAgAAAAIQAAABgAAAAYAAAAGAAAABgAAAAYAAAAAAAAAAAAAAAAAAAAGAAAABsAAAAbAAAAGwAAACEAAAAYAAAAIAAAACEAAAAhAAAAIQAAABgAAAAhAAAAIQAAAAAAAAAAAAAAAAAAACEAAAAhAAAAIQAAABsAAAAhAAAAGAAAACAAAAAYAAAAGAAAABgAAAAYAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAbAAAAGwAAABsAAAAbAAAAIQAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAGwAAABsAAAAbAAAAGwAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAAA== - ind: "-1,1" tiles: IQAAABYAAAAWAAAAFgAAABYAAAAhAAAAFgAAABYAAAAWAAAAFgAAABYAAAAhAAAAGAAAABgAAAAYAAAAGAAAACEAAAAhAAAAIQAAABYAAAAhAAAAIQAAABYAAAAWAAAAFgAAABYAAAAWAAAAIQAAABgAAAAYAAAAGAAAABgAAAAhAAAAEQAAABEAAAARAAAAEQAAACEAAAAhAAAAFgAAACEAAAAhAAAAIQAAACEAAAAYAAAAGAAAABgAAAAYAAAAIQAAABEAAAARAAAAEQAAABEAAAAhAAAACAAAAAgAAAAIAAAACAAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAARAAAAEQAAABEAAAARAAAAFgAAAAgAAAAeAAAAHgAAAB4AAAAhAAAAGAAAABgAAAAYAAAAGAAAACAAAAAhAAAAEQAAABEAAAARAAAAEQAAACEAAAAIAAAAHgAAAB4AAAAeAAAAIQAAACAAAAAgAAAAIAAAACAAAAAgAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAACAAAAB4AAAAeAAAAHgAAACEAAAAgAAAAIQAAACEAAAAPAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAABgAAAAYAAAAGAAAABgAAAAYAAAAIAAAACEAAAAPAAAADwAAAA8AAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAACAAAAAgAAAAIAAAACAAAAAhAAAADwAAAA8AAAAPAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIQAAACEAAAAhAAAAIQAAAA8AAAAPAAAADwAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAhAAAAIQAAACEAAAAAAAAAAAAAACEAAAAPAAAADwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIQAAACEAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAABYAAAAWAAAAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAWAAAAFgAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAFgAAABYAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAACEAAAAWAAAAFgAAAA== - ind: "-2,1" - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAACAAAAAYAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIAAAABgAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAhAAAAIQAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + tiles: AAAAAAAAAAAAAAAAAAAAACEAAAAYAAAAIAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAACAAAAAYAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAGAAAACAAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAIQAAABgAAAAgAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAYAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAGAAAABgAAAAYAAAAGAAAABgAAAAYAAAAGAAAABgAAAAhAAAAIAAAABgAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAACAAAAAYAAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAgAAAAGAAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAIAAAABgAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAACAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAhAAAAIQAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - ind: "-1,2" tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAhAAAAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIQAAACEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== - ind: "1,1" @@ -279,8 +279,7 @@ entities: - uid: 23 type: SuspicionPistolMagazineSpawner components: - - rot: 4.371139006309477E-08 rad - pos: -9.5,6.5 + - pos: -13.5,7.5 parent: 853 type: Transform - uid: 24 @@ -326,10 +325,9 @@ entities: parent: 853 type: Transform - uid: 30 - type: SuspicionSMGSpawner + type: RandomSpawner components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,8.5 + - pos: 26.5,-5.5 parent: 853 type: Transform - uid: 31 @@ -346,13 +344,11 @@ entities: parent: 853 type: Transform - uid: 33 - type: LargeBeaker + type: WallReinforced components: - - pos: 17.923235,1.6235776 + - pos: 20.5,-0.5 parent: 853 type: Transform - - canCollide: False - type: Physics - uid: 34 type: SuspicionGrenadesSpawner components: @@ -377,8 +373,7 @@ entities: - uid: 37 type: SuspicionGrenadesSpawner components: - - rot: 4.371139006309477E-08 rad - pos: 18.5,1.5 + - pos: 21.5,0.5 parent: 853 type: Transform - uid: 38 @@ -879,15 +874,11 @@ entities: parent: 853 type: Transform - uid: 105 - type: AirlockBarGlassLocked + type: WindoorBarLocked components: - pos: -7.5,-3.5 parent: 853 type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer - uid: 106 type: WeldingFuelTankFull components: @@ -1230,9 +1221,9 @@ entities: parent: 853 type: Transform - uid: 148 - type: TableReinforcedGlass + type: ReinforcedWindow components: - - pos: 14.5,-2.5 + - pos: -1.5,12.5 parent: 853 type: Transform - uid: 149 @@ -1302,9 +1293,9 @@ entities: parent: 853 type: Transform - uid: 157 - type: TableReinforcedGlass + type: LowWall components: - - pos: 14.5,-1.5 + - pos: -1.5,13.5 parent: 853 type: Transform - uid: 158 @@ -1317,19 +1308,20 @@ entities: stash: !type:ContainerSlot {} type: ContainerContainer - uid: 159 + type: chem_master + components: + - pos: 16.5,1.5 + parent: 853 + type: Transform + - containers: + ChemMaster-reagentContainerContainer: !type:ContainerSlot {} + type: ContainerContainer +- uid: 160 type: WallReinforced components: - - pos: 13.5,1.5 + - pos: 20.5,-1.5 parent: 853 type: Transform -- uid: 160 - type: LargeBeaker - components: - - pos: 17.454485,1.6235776 - parent: 853 - type: Transform - - canCollide: False - type: Physics - uid: 161 type: CableApcExtension components: @@ -1958,26 +1950,22 @@ entities: - canCollide: False type: Physics - uid: 215 - type: DisposalTrunk + type: CableApcExtension components: - anchored: True - pos: 14.5,1.5 + pos: -6.5,4.5 parent: 853 type: Transform - - containers: - DisposalEntry: !type:Container - ents: [] - type: ContainerContainer + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 216 - type: DisposalUnit + type: LowWall components: - - pos: 14.5,1.5 + - pos: -10.5,6.5 parent: 853 type: Transform - - containers: - DisposalUnit: !type:Container - ents: [] - type: ContainerContainer - uid: 217 type: DisposalPipe components: @@ -2957,39 +2945,34 @@ entities: ents: [] type: ContainerContainer - uid: 300 + type: Poweredlight + components: + - pos: -4.226936,15.0964775 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 301 type: DisposalPipe components: - anchored: True - pos: 14.5,0.5 + rot: -1.5707963267948966 rad + pos: 13.5,-0.5 parent: 853 type: Transform - containers: DisposalTransit: !type:Container ents: [] type: ContainerContainer -- uid: 301 - type: DisposalJunctionFlipped - components: - - anchored: True - pos: 12.5,-0.5 - parent: 853 - type: Transform - - containers: - DisposalJunction: !type:Container - ents: [] - type: ContainerContainer - uid: 302 - type: DisposalBend + type: WallReinforced components: - - anchored: True - rot: -1.5707963267948966 rad - pos: 14.5,-0.5 + - pos: -1.5,11.5 parent: 853 type: Transform - - containers: - DisposalBend: !type:Container - ents: [] - type: ContainerContainer - uid: 303 type: Window components: @@ -4474,10 +4457,9 @@ entities: parent: 853 type: Transform - uid: 444 - type: RandomSpawner + type: SuspicionSMGSpawner components: - - rot: 4.371139006309477E-08 rad - pos: 20.5,0.5 + - pos: -10.5,7.5 parent: 853 type: Transform - uid: 445 @@ -4846,10 +4828,9 @@ entities: parent: 853 type: Transform - uid: 497 - type: RandomSpawner + type: SpawnPointSecurityOfficer components: - - rot: 4.371139006309477E-08 rad - pos: -3.5,7.5 + - pos: -2.5,8.5 parent: 853 type: Transform - uid: 498 @@ -4867,10 +4848,9 @@ entities: parent: 853 type: Transform - uid: 500 - type: RandomSpawner + type: SpawnPointSecurityOfficer components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,23.5 + - pos: -13.5,14.5 parent: 853 type: Transform - uid: 501 @@ -5284,31 +5264,23 @@ entities: parent: 853 type: Transform - uid: 554 - type: AirlockSecurityGlassLocked + type: BoxBeaker components: - - name: Cell 2 - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: 0.5,9.5 + - pos: 19.51463,0.6960156 parent: 853 type: Transform + - canCollide: False + type: Physics - containers: - board: !type:Container + storagebase: !type:Container ents: [] type: ContainerContainer - uid: 555 - type: AirlockSecurityGlassLocked + type: EmergencyLight components: - - name: Cell 1 - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -2.5,9.5 + - pos: 0.49181414,14.9871025 parent: 853 type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer - uid: 556 type: Catwalk components: @@ -5533,33 +5505,32 @@ entities: ents: [] type: ContainerContainer - uid: 576 - type: ChairOfficeDark + type: Firelock components: - - rot: 1.5707963705062866 rad - pos: -8.5,8.5 + - pos: 15.5,-3.5 parent: 853 type: Transform - uid: 577 - type: ChairOfficeDark + type: FirelockGlass components: - - rot: 4.371139006309477E-08 rad - pos: -9.5,7.5 + - pos: -6.5,3.5 parent: 853 type: Transform - uid: 578 - type: TableReinforced + type: TableMetal components: - - rot: 4.371139006309477E-08 rad - pos: -8.5,6.5 + - pos: -0.5,13.5 parent: 853 type: Transform - uid: 579 - type: TableReinforced + type: chem_dispenser components: - - rot: 4.371139006309477E-08 rad - pos: -9.5,6.5 + - pos: 17.5,1.5 parent: 853 type: Transform + - containers: + ReagentDispenser-reagentContainerContainer: !type:ContainerSlot {} + type: ContainerContainer - uid: 580 type: SuspicionHitscanSpawner components: @@ -5858,18 +5829,13 @@ entities: ents: [] type: ContainerContainer - uid: 607 - type: AirlockSecurityGlassLocked + type: SheetPlasma1 components: - - name: Brig - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -6.5,6.5 + - pos: 19.54588,-0.4289844 parent: 853 type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer + - canCollide: False + type: Physics - uid: 608 type: CableApcExtension components: @@ -6112,18 +6078,11 @@ entities: ents: [] type: ContainerContainer - uid: 629 - type: AirlockSecurityGlassLocked + type: WallReinforced components: - - name: Reception Desk - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -9.5,9.5 + - pos: 20.5,1.5 parent: 853 type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer - uid: 630 type: AirlockSecurityGlassLocked components: @@ -6385,44 +6344,24 @@ entities: ents: [] type: ContainerContainer - uid: 651 - type: AirlockSecurityGlassLocked + type: Chair components: - - name: Brig - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -6.5,9.5 + - pos: -0.5,14.5 parent: 853 type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer - uid: 652 - type: AirlockSecurityGlassLocked + type: ReinforcedWindow components: - - name: Brig - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -5.5,9.5 + - pos: 13.5,-2.5 parent: 853 type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer - uid: 653 - type: AirlockSecurityGlassLocked + type: SignInterrogation components: - - name: Brig - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -5.5,6.5 + - rot: 3.141592653589793 rad + pos: -0.42353058,11.3777275 parent: 853 type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer - uid: 654 type: AirlockExternalLocked components: @@ -7088,16 +7027,14 @@ entities: parent: 853 type: Transform - uid: 742 - type: PottedPlantRandomPlastic + type: FoodDonutChocolate components: - - rot: 4.371139006309477E-08 rad - pos: -12.5,7.5 + - rot: 0.0065181441605091095 rad + pos: -3.494867,8.58493 parent: 853 type: Transform - - containers: - potted_plant_hide: !type:ContainerSlot {} - stash: !type:ContainerSlot {} - type: ContainerContainer + - sleepTime: 1150.1904 + type: Physics - uid: 743 type: ReinforcedWindow components: @@ -7126,17 +7063,12 @@ entities: stash: !type:ContainerSlot {} type: ContainerContainer - uid: 746 - type: DisposalPipe + type: Windoor components: - - anchored: True - rot: -1.5707963267948966 rad - pos: 13.5,-0.5 + - rot: -1.5707963267948966 rad + pos: -3.5,7.5 parent: 853 type: Transform - - containers: - DisposalTransit: !type:Container - ents: [] - type: ContainerContainer - uid: 747 type: LowWall components: @@ -7357,15 +7289,15 @@ entities: stash: !type:ContainerSlot {} type: ContainerContainer - uid: 770 - type: ClosetEmergencyFilledRandom + type: ClothingBeltSecurityFilled components: - - pos: 0.5,14.5 + - pos: -6.54198,13.584116 parent: 853 type: Transform - - IsPlaceable: False - type: PlaceableSurface + - canCollide: False + type: Physics - containers: - EntityStorageComponent: !type:Container + storagebase: !type:Container ents: [] type: ContainerContainer - uid: 771 @@ -7732,18 +7664,12 @@ entities: ents: [] type: ContainerContainer - uid: 802 - type: LockerMedicineFilled + type: ChairOfficeDark components: - - rot: 4.371139006309477E-08 rad - pos: 18.5,-2.5 + - rot: 3.141592653589793 rad + pos: -0.5,12.5 parent: 853 type: Transform - - IsPlaceable: False - type: PlaceableSurface - - containers: - EntityStorageComponent: !type:Container - ents: [] - type: ContainerContainer - uid: 803 type: LockerMedicalFilled components: @@ -7968,10 +7894,9 @@ entities: stash: !type:ContainerSlot {} type: ContainerContainer - uid: 823 - type: ClosetBase + type: ClosetMaintenanceFilledRandom components: - - rot: 4.371139006309477E-08 rad - pos: -11.5,-12.5 + - pos: -11.5,-12.5 parent: 853 type: Transform - IsPlaceable: False @@ -12315,6 +12240,95 @@ entities: 45,-33: 0 46,-34: 0 47,-34: 0 + 24,-27: 0 + 24,-26: 0 + 24,-25: 0 + 24,-24: 0 + 24,-23: 0 + 24,-22: 0 + 24,-21: 0 + 25,-27: 0 + 25,-26: 0 + 25,-25: 0 + 25,-24: 0 + 25,-23: 0 + 25,-22: 0 + 25,-21: 0 + 26,-27: 0 + 26,-26: 0 + 26,-25: 0 + 26,-24: 0 + 26,-23: 0 + 26,-22: 0 + 26,-21: 0 + 27,-27: 0 + 27,-26: 0 + 27,-25: 0 + 27,-24: 0 + 27,-23: 0 + 27,-22: 0 + 27,-21: 0 + 28,-27: 0 + 28,-26: 0 + 28,-25: 0 + 28,-24: 0 + 28,-23: 0 + 28,-22: 0 + 28,-21: 0 + -28,16: 0 + -28,17: 0 + -28,18: 0 + -28,19: 0 + -28,20: 0 + -28,21: 0 + -27,16: 0 + -27,17: 0 + -27,18: 0 + -27,19: 0 + -27,20: 0 + -27,21: 0 + -26,16: 0 + -26,17: 0 + -26,18: 0 + -26,19: 0 + -26,20: 0 + -26,21: 0 + -25,16: 0 + -25,17: 0 + -25,18: 0 + -25,19: 0 + -25,20: 0 + -25,21: 0 + -24,16: 0 + -24,17: 0 + -24,18: 0 + -24,19: 0 + -24,20: 0 + -24,21: 0 + -23,16: 0 + -23,17: 0 + -23,18: 0 + -23,19: 0 + -23,20: 0 + -23,21: 0 + -22,16: 0 + -22,17: 0 + -22,18: 0 + -22,19: 0 + -22,20: 0 + -22,21: 0 + -21,16: 0 + -21,17: 0 + -21,18: 0 + -21,19: 0 + -21,20: 0 + -21,21: 0 + 11,26: 1 + 11,27: 1 + 12,26: 1 + 12,27: 1 + 13,26: 1 + 13,27: 1 uniqueMixes: - volume: 2500 temperature: 293.15 @@ -12327,6 +12341,17 @@ entities: - 0 - 0 - 0 + - volume: 2500 + temperature: 293.15 + moles: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 type: GridAtmosphere - linearDamping: 0.05 fixtures: @@ -12337,8 +12362,6 @@ entities: - -16,0 - -16,-16 id: grid_chunk--1--1 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12347,8 +12370,6 @@ entities: - -16,16 - -16,0 id: grid_chunk--1-0 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12357,8 +12378,6 @@ entities: - 0,0 - 0,-16 id: grid_chunk-0--1 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12367,8 +12386,6 @@ entities: - 0,16 - 0,0 id: grid_chunk-0-0 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12377,8 +12394,6 @@ entities: - 16,16 - 16,0 id: grid_chunk-1-0 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12387,8 +12402,6 @@ entities: - 0,32 - 0,16 id: grid_chunk-0-1 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12397,8 +12410,6 @@ entities: - -32,16 - -32,0 id: grid_chunk--2-0 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12407,8 +12418,6 @@ entities: - 16,0 - 16,-16 id: grid_chunk-1--1 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12417,8 +12426,6 @@ entities: - 0,-16 - 0,-32 id: grid_chunk-0--2 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12427,8 +12434,6 @@ entities: - 16,-16 - 16,-27 id: grid_chunk-1--2 - mask: [] - layer: [] mass: 143 - shape: !type:PolygonShape vertices: @@ -12437,19 +12442,7 @@ entities: - -16,32 - -16,16 id: grid_chunk--1-1 - mask: [] - layer: [] mass: 256 - - shape: !type:PolygonShape - vertices: - - -16,16 - - -16,27 - - -20,27 - - -20,16 - id: grid_chunk--2-1 - mask: [] - layer: [] - mass: 44 - shape: !type:PolygonShape vertices: - 0,32 @@ -12457,8 +12450,6 @@ entities: - -3,34 - -3,32 id: grid_chunk--1-2 - mask: [] - layer: [] mass: 6 - shape: !type:PolygonShape vertices: @@ -12467,8 +12458,6 @@ entities: - 16,17 - 16,16 id: grid_chunk-1-1 - mask: [] - layer: [] mass: 2 - shape: !type:PolygonShape vertices: @@ -12477,8 +12466,6 @@ entities: - 0,34 - 0,32 id: grid_chunk-0-2 - mask: [] - layer: [] mass: 20 - shape: !type:PolygonShape vertices: @@ -12487,8 +12474,6 @@ entities: - 32,16 - 32,0 id: grid_chunk-2-0 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12497,8 +12482,6 @@ entities: - 48,15 - 48,0 id: grid_chunk-3-0 - mask: [] - layer: [] mass: 60 - shape: !type:PolygonShape vertices: @@ -12507,8 +12490,6 @@ entities: - 32,0 - 32,-16 id: grid_chunk-2--1 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12517,8 +12498,6 @@ entities: - -16,-16 - -16,-31 id: grid_chunk--1--2 - mask: [] - layer: [] mass: 240 - shape: !type:PolygonShape vertices: @@ -12527,8 +12506,6 @@ entities: - -26,-16 - -26,-28 id: grid_chunk--2--2 - mask: [] - layer: [] mass: 120 - shape: !type:PolygonShape vertices: @@ -12537,8 +12514,6 @@ entities: - -32,0 - -32,-16 id: grid_chunk--2--1 - mask: [] - layer: [] mass: 256 - shape: !type:PolygonShape vertices: @@ -12547,8 +12522,6 @@ entities: - -42,16 - -42,0 id: grid_chunk--3-0 - mask: [] - layer: [] mass: 160 - shape: !type:PolygonShape vertices: @@ -12557,8 +12530,6 @@ entities: - -40,0 - -40,-13 id: grid_chunk--3--1 - mask: [] - layer: [] mass: 104 - shape: !type:PolygonShape vertices: @@ -12567,8 +12538,6 @@ entities: - 48,0 - 48,-16 id: grid_chunk-3--1 - mask: [] - layer: [] mass: 128 - shape: !type:PolygonShape vertices: @@ -12577,8 +12546,6 @@ entities: - 40,-16 - 40,-32 id: grid_chunk-2--2 - mask: [] - layer: [] mass: 128 - shape: !type:PolygonShape vertices: @@ -12587,8 +12554,6 @@ entities: - 48,-16 - 48,-32 id: grid_chunk-3--2 - mask: [] - layer: [] mass: 176 - shape: !type:PolygonShape vertices: @@ -12597,8 +12562,6 @@ entities: - 48,-32 - 48,-34 id: grid_chunk-3--3 - mask: [] - layer: [] mass: 22 - shape: !type:PolygonShape vertices: @@ -12607,9 +12570,15 @@ entities: - 40,-32 - 40,-34 id: grid_chunk-2--3 - mask: [] - layer: [] mass: 16 + - shape: !type:PolygonShape + vertices: + - -16,16 + - -16,27 + - -28,27 + - -28,16 + id: grid_chunk--2-1 + mass: 132 bodyType: Dynamic type: Physics - uid: 854 @@ -12992,10 +12961,9 @@ entities: parent: 853 type: Transform - uid: 900 - type: SpawnPointSecurityOfficer + type: RandomSpawner components: - - rot: 4.371139006309477E-08 rad - pos: -11.5,8.5 + - pos: -4.5,7.5 parent: 853 type: Transform - uid: 901 @@ -13504,12 +13472,14 @@ entities: - canCollide: False type: Physics - uid: 975 - type: Table + type: FoodDonutChocolate components: - - rot: 1.5707963705062866 rad - pos: 6.5,20.5 + - rot: 0.0065185800194740295 rad + pos: -3.5051332,8.41546 parent: 853 type: Transform + - sleepTime: 1148.4512 + type: Physics - uid: 976 type: WarpPoint components: @@ -13577,6 +13547,10 @@ entities: type: Transform - canCollide: False type: Physics + - containers: + storagebase: !type:Container + ents: [] + type: ContainerContainer - uid: 985 type: VendingMachineCoffee components: @@ -13854,14 +13828,14 @@ entities: ents: [] type: ContainerContainer - uid: 1016 - type: FirelockGlass + type: Paper components: - - rot: 4.371139006309477E-08 rad - pos: -9.5,6.5 + - rot: 0.00652383454144001 rad + pos: -3.5051389,7.5080695 parent: 853 type: Transform - - airBlocked: False - type: Airtight + - sleepTime: 1126.2756 + type: Physics - uid: 1017 type: Firelock components: @@ -13912,7 +13886,7 @@ entities: type: SpawnPointStationEngineer components: - rot: 4.371139006309477E-08 rad - pos: 33.5,-2.5 + pos: 33.5,-1.5 parent: 853 type: Transform - uid: 1024 @@ -13975,10 +13949,10 @@ entities: - airBlocked: False type: Airtight - uid: 1032 - type: TableReinforced + type: Windoor components: - - rot: 4.371139006309477E-08 rad - pos: 0.5,-14.5 + - rot: -1.5707963267948966 rad + pos: 6.5,20.5 parent: 853 type: Transform - uid: 1033 @@ -14011,12 +13985,17 @@ entities: light_bulb: !type:ContainerSlot {} type: ContainerContainer - uid: 1036 - type: SpawnPointSecurityOfficer + type: Poweredlight components: - - rot: 4.371139006309477E-08 rad - pos: -13.5,15.5 + - rot: 1.5707963267948966 rad + pos: -10.9225445,7.479561 parent: 853 type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 1037 type: SpawnPointSecurityOfficer components: @@ -14132,17 +14111,20 @@ entities: parent: 853 type: Transform - uid: 1052 - type: TableReinforced + type: Poweredlight components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,7.5 + - pos: -11.519875,12.105981 parent: 853 type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 1053 - type: TableReinforced + type: ReinforcedWindow components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,8.5 + - pos: -1.5,9.5 parent: 853 type: Transform - uid: 1054 @@ -14562,15 +14544,11 @@ entities: parent: 853 type: Transform - uid: 1103 - type: chem_dispenser + type: ReinforcedWindow components: - - rot: -1.5707963267948966 rad - pos: 16.5,1.5 + - pos: -0.5,9.5 parent: 853 type: Transform - - containers: - ReagentDispenser-reagentContainerContainer: !type:ContainerSlot {} - type: ContainerContainer - uid: 1104 type: DisposalTrunk components: @@ -15050,18 +15028,16 @@ entities: parent: 853 type: Transform - uid: 1161 - type: LockerChemistry + type: DrinkHotCoffee components: - - rot: 4.371139006309477E-08 rad - pos: 18.5,-1.5 + - rot: -0.03783857449889183 rad + pos: -3.4980159,7.997542 parent: 853 type: Transform - - IsPlaceable: False - type: PlaceableSurface - - containers: - EntityStorageComponent: !type:Container - ents: [] - type: ContainerContainer + - caps: Refillable, Drainable + type: SolutionContainer + - sleepTime: 1114.4688 + type: Physics - uid: 1162 type: LockerElectricalSupplies components: @@ -15084,12 +15060,14 @@ entities: parent: 853 type: Transform - uid: 1164 - type: Table + type: BedsheetOrange components: - - rot: 4.371139006309477E-08 rad - pos: -12.5,8.5 + - anchored: True + pos: -12.5,7.5 parent: 853 type: Transform + - canCollide: False + type: Physics - uid: 1165 type: Chair components: @@ -15122,23 +15100,31 @@ entities: ents: [] type: ContainerContainer - uid: 1168 - type: WallReinforced + type: Poweredlight components: - - rot: 3.141592653589793 rad - pos: 13.5,-2.5 + - pos: -8.48515,5.8506384 parent: 853 type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 1169 - type: LowWall + type: KitchenReagentGrinder components: - - pos: 13.5,0.5 + - pos: 19.5,1.5 parent: 853 type: Transform + - containers: + ReagentGrinder-reagentContainerContainer: !type:ContainerSlot {} + ReagentGrinder-entityContainerContainer: !type:Container + ents: [] + type: ContainerContainer - uid: 1170 - type: TableReinforcedGlass + type: FirelockGlass components: - - rot: 4.371139006309477E-08 rad - pos: 18.5,1.5 + - pos: -6.5,5.5 parent: 853 type: Transform - uid: 1171 @@ -15148,30 +15134,39 @@ entities: parent: 853 type: Transform - uid: 1172 - type: TableReinforcedGlass + type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: 18.5,0.5 + - pos: 20.5,0.5 parent: 853 type: Transform - uid: 1173 type: LowWall components: - - pos: 13.5,-0.5 + - pos: -1.5,12.5 parent: 853 type: Transform - uid: 1174 - type: WallReinforced + type: Poweredlight components: - - pos: 19.5,1.5 + - pos: -3.5686207,5.8193884 parent: 853 type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 1175 - type: WallReinforced + type: CableApcExtension components: - - pos: 19.5,0.5 + - anchored: True + pos: -6.5,5.5 parent: 853 type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 1176 type: SheetPlasteel components: @@ -15438,21 +15433,21 @@ entities: parent: 853 type: Transform - uid: 1208 - type: ChairOfficeDark + type: AirlockSecurityGlassLocked components: - - rot: 4.71238902409608 rad - pos: -11.5,8.5 + - pos: -5.5,6.5 parent: 853 type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 1209 - type: Chair + type: TableReinforced components: - - rot: 1.5707963705062866 rad - pos: -13.5,8.5 + - pos: 6.5,20.5 parent: 853 type: Transform - - bodyType: Dynamic - type: Physics - uid: 1210 type: PowerCellSmallStandard components: @@ -15839,8 +15834,7 @@ entities: - uid: 1248 type: WallSolid components: - - rot: 4.371139006309477E-08 rad - pos: -33.5,9.5 + - pos: -33.5,9.5 parent: 853 type: Transform - uid: 1249 @@ -15945,19 +15939,16 @@ entities: parent: 853 type: Transform - uid: 1258 - type: Poweredlight + type: CableApcExtension components: - - rot: 4.71238902409608 rad - pos: -11,8.5 + - anchored: True + pos: -5.5,4.5 parent: 853 type: Transform - - color: '#FFFFFFFF' - type: PointLight - - powerLoad: 0 - type: ApcPowerReceiver - - containers: - light_bulb: !type:ContainerSlot {} - type: ContainerContainer + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 1259 type: Poweredlight components: @@ -15982,19 +15973,11 @@ entities: light_bulb: !type:ContainerSlot {} type: ContainerContainer - uid: 1261 - type: Poweredlight + type: ReinforcedWindow components: - - rot: 4.371139006309477E-08 rad - pos: -1.5,6 + - pos: -10.5,14.5 parent: 853 type: Transform - - color: '#FFFFFFFF' - type: PointLight - - powerLoad: 0 - type: ApcPowerReceiver - - containers: - light_bulb: !type:ContainerSlot {} - type: ContainerContainer - uid: 1262 type: WallSolid components: @@ -16081,16 +16064,11 @@ entities: parent: 853 type: Transform - uid: 1271 - type: Poweredlight + type: LowWall components: - - pos: -1.4910796,14.8427725 + - pos: -1.5,14.5 parent: 853 type: Transform - - powerLoad: 0 - type: ApcPowerReceiver - - containers: - light_bulb: !type:ContainerSlot {} - type: ContainerContainer - uid: 1272 type: Poweredlight components: @@ -16117,61 +16095,29 @@ entities: light_bulb: !type:ContainerSlot {} type: ContainerContainer - uid: 1274 - type: PoweredSmallLight + type: ReinforcedWindow components: - - rot: 1.5707963705062866 rad - pos: -1,8.5 + - pos: -1.5,13.5 parent: 853 type: Transform - - color: '#FFFFFFFF' - type: PointLight - - powerLoad: 0 - type: ApcPowerReceiver - - containers: - light_bulb: !type:ContainerSlot {} - type: ContainerContainer - uid: 1275 - type: PoweredSmallLight + type: ReinforcedWindow components: - - rot: 1.5707963705062866 rad - pos: -4,8.5 + - pos: -1.5,14.5 parent: 853 type: Transform - - color: '#FFFFFFFF' - type: PointLight - - powerLoad: 0 - type: ApcPowerReceiver - - containers: - light_bulb: !type:ContainerSlot {} - type: ContainerContainer - uid: 1276 - type: Poweredlight + type: WallReinforced components: - - rot: 4.71238902409608 rad - pos: -5,7.5 + - pos: -0.5,11.5 parent: 853 type: Transform - - color: '#FFFFFFFF' - type: PointLight - - powerLoad: 0 - type: ApcPowerReceiver - - containers: - light_bulb: !type:ContainerSlot {} - type: ContainerContainer - uid: 1277 - type: Poweredlight + type: WallReinforced components: - - rot: 1.5707963705062866 rad - pos: -10,7.5 + - pos: 20.5,-2.5 parent: 853 type: Transform - - color: '#FFFFFFFF' - type: PointLight - - powerLoad: 0 - type: ApcPowerReceiver - - containers: - light_bulb: !type:ContainerSlot {} - type: ContainerContainer - uid: 1278 type: Poweredlight components: @@ -16723,17 +16669,11 @@ entities: parent: 853 type: Transform - uid: 1327 - type: Poweredlight + type: WallReinforced components: - - rot: -1.5707963267948966 rad - pos: 18.89989,-0.46983594 + - pos: 20.5,-1.5 parent: 853 type: Transform - - powerLoad: 0 - type: ApcPowerReceiver - - containers: - light_bulb: !type:ContainerSlot {} - type: ContainerContainer - uid: 1328 type: WallSolid components: @@ -16840,15 +16780,21 @@ entities: parent: 853 type: Transform - uid: 1339 - type: WallReinforced + type: BoxSyringe components: - - pos: 19.5,-0.5 + - pos: 14.499004,1.2741406 parent: 853 type: Transform + - canCollide: False + type: Physics + - containers: + storagebase: !type:Container + ents: [] + type: ContainerContainer - uid: 1340 - type: WallReinforced + type: FirelockGlass components: - - pos: 19.5,-1.5 + - pos: -6.5,4.5 parent: 853 type: Transform - uid: 1341 @@ -17441,13 +17387,11 @@ entities: parent: 853 type: Transform - uid: 1398 - type: FirelockGlass + type: ChairOfficeLight components: - - pos: 6.5,20.5 + - pos: 15.5,-2.5 parent: 853 type: Transform - - airBlocked: False - type: Airtight - uid: 1399 type: WallReinforced components: @@ -17633,73 +17577,88 @@ entities: parent: 853 type: Transform - uid: 1426 - type: Window + type: Poweredlight components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,18.5 + - pos: -1.2113109,11.1277275 parent: 853 type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 1427 - type: Window + type: AirlockSecurityGlassLocked components: - - rot: 4.371139006309477E-08 rad - pos: -10.5,14.5 + - pos: 0.5,11.5 parent: 853 type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 1428 - type: ReinforcedWindow + type: PoweredSmallLight components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,9.5 + - pos: -0.50818586,15.0808525 parent: 853 type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 1429 - type: ReinforcedWindow + type: PoweredSmallLight components: - - rot: 4.371139006309477E-08 rad - pos: -8.5,9.5 + - rot: -1.5707963267948966 rad + pos: 1.1636891,13.8464775 parent: 853 type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 1430 - type: ReinforcedWindow + type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -3.5,9.5 + - pos: -11.5,7.5 parent: 853 type: Transform - uid: 1431 - type: ReinforcedWindow + type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -0.5,9.5 + - pos: -11.5,9.5 parent: 853 type: Transform - uid: 1432 - type: ReinforcedWindow + type: LowWall components: - - rot: 4.371139006309477E-08 rad - pos: 0.5,6.5 + - pos: -13.5,9.5 parent: 853 type: Transform - uid: 1433 - type: ReinforcedWindow + type: AirlockSecurityGlassLocked components: - - rot: 4.371139006309477E-08 rad - pos: -0.5,6.5 + - pos: -12.5,9.5 parent: 853 type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 1434 - type: ReinforcedWindow + type: TableReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -2.5,6.5 + - pos: -11.5,8.5 parent: 853 type: Transform - uid: 1435 - type: ReinforcedWindow + type: WindoorSecurityLocked components: - - rot: 4.371139006309477E-08 rad - pos: -3.5,6.5 + - rot: 1.5707963267948966 rad + pos: -11.5,8.5 parent: 853 type: Transform - uid: 1436 @@ -18197,9 +18156,10 @@ entities: parent: 853 type: Transform - uid: 1505 - type: WallReinforced + type: WindoorSecurityLocked components: - - pos: 19.5,-2.5 + - rot: -1.5707963267948966 rad + pos: -9.5,8.5 parent: 853 type: Transform - uid: 1506 @@ -19315,9 +19275,9 @@ entities: parent: 853 type: Transform - uid: 1657 - type: WallReinforced + type: RandomSpawner components: - - pos: 23.5,-24.5 + - pos: 26.5,-25.5 parent: 853 type: Transform - uid: 1658 @@ -19399,11 +19359,16 @@ entities: parent: 853 type: Transform - uid: 1671 - type: WallReinforced + type: CableApcExtension components: - - pos: 26.5,-19.5 + - anchored: True + pos: 28.5,-24.5 parent: 853 type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 1672 type: WallReinforced components: @@ -19526,11 +19491,15 @@ entities: parent: 853 type: Transform - uid: 1692 - type: WallReinforced + type: AirlockMaint components: - - pos: -25.5,15.5 + - pos: 23.5,-24.5 parent: 853 type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 1693 type: WallReinforced components: @@ -19634,11 +19603,15 @@ entities: parent: 853 type: Transform - uid: 1709 - type: WallReinforced + type: AirlockMaint components: - - pos: -19.5,19.5 + - pos: -25.5,15.5 parent: 853 type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 1710 type: WallReinforced components: @@ -20643,10 +20616,10 @@ entities: parent: 853 type: Transform - uid: 1855 - type: WallReinforced + type: EmergencyLight components: - - rot: 4.371139006309477E-08 rad - pos: 10.5,26.5 + - rot: -1.5707963267948966 rad + pos: 9.693537,24.5 parent: 853 type: Transform - uid: 1856 @@ -21488,31 +21461,36 @@ entities: parent: 853 type: Transform - uid: 1975 - type: WallReinforced + type: WardrobePrison components: - - rot: 4.371139006309477E-08 rad - pos: -10.5,6.5 + - rot: -1.5707963267948966 rad + pos: -13.5,8.5 parent: 853 type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer - uid: 1976 - type: WallReinforced + type: Bed components: - - rot: 4.371139006309477E-08 rad - pos: -10.5,7.5 + - rot: -1.5707963267948966 rad + pos: -12.5,7.5 parent: 853 type: Transform - uid: 1977 - type: WallReinforced + type: EmergencyLight components: - - rot: 4.371139006309477E-08 rad - pos: -10.5,8.5 + - rot: 1.5707963267948966 rad + pos: -13.770238,7.833312 parent: 853 type: Transform - uid: 1978 type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -10.5,9.5 + - pos: -9.5,7.5 parent: 853 type: Transform - uid: 1979 @@ -21711,94 +21689,94 @@ entities: - uid: 2006 type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -4.5,6.5 + - pos: -9.5,6.5 parent: 853 type: Transform - uid: 2007 type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -1.5,6.5 + - pos: -8.5,6.5 parent: 853 type: Transform - uid: 2008 type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,6.5 + - pos: -9.5,9.5 parent: 853 type: Transform - uid: 2009 - type: WallSolid + type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -4.5,7.5 + - pos: -7.5,6.5 parent: 853 type: Transform - uid: 2010 - type: WallSolid + type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -4.5,8.5 + - pos: -6.5,6.5 parent: 853 type: Transform - uid: 2011 - type: WallSolid + type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -4.5,9.5 + - pos: -6.5,7.5 parent: 853 type: Transform - uid: 2012 - type: WallSolid + type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -1.5,9.5 + - pos: -6.5,8.5 parent: 853 type: Transform - uid: 2013 - type: WallSolid + type: WallReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -1.5,7.5 + - pos: -6.5,9.5 parent: 853 type: Transform - uid: 2014 - type: WallSolid + type: AirlockSecurityGlassLocked components: - - rot: 4.371139006309477E-08 rad - pos: -1.5,8.5 + - pos: -8.5,9.5 parent: 853 type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 2015 type: LowWall components: - - rot: 4.371139006309477E-08 rad - pos: -0.5,9.5 + - pos: -7.5,9.5 parent: 853 type: Transform - uid: 2016 - type: LowWall + type: WardrobePrison components: - - rot: 4.371139006309477E-08 rad - pos: -3.5,9.5 + - pos: -7.5,8.5 parent: 853 type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer - uid: 2017 - type: LowWall + type: Bed components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,9.5 + - pos: -8.5,7.5 parent: 853 type: Transform - uid: 2018 - type: LowWall + type: BedsheetOrange components: - - rot: 4.371139006309477E-08 rad - pos: -8.5,9.5 + - anchored: True + pos: -8.5,7.5 parent: 853 type: Transform + - canCollide: False + type: Physics - uid: 2019 type: WallSolid components: @@ -22471,31 +22449,28 @@ entities: parent: 853 type: Transform - uid: 2115 - type: LowWall + type: ReinforcedWindow components: - - rot: 4.371139006309477E-08 rad - pos: -2.5,6.5 + - pos: -13.5,9.5 parent: 853 type: Transform - uid: 2116 - type: LowWall + type: ReinforcedWindow components: - - rot: 4.371139006309477E-08 rad - pos: -3.5,6.5 + - pos: -7.5,9.5 parent: 853 type: Transform - uid: 2117 - type: LowWall + type: TableReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -0.5,6.5 + - pos: -9.5,8.5 parent: 853 type: Transform - uid: 2118 - type: LowWall + type: Windoor components: - - rot: 4.371139006309477E-08 rad - pos: 0.5,6.5 + - rot: -1.5707963267948966 rad + pos: -11.5,8.5 parent: 853 type: Transform - uid: 2119 @@ -22615,17 +22590,24 @@ entities: parent: 853 type: Transform - uid: 2136 - type: ReinforcedWindow + type: Windoor components: - - pos: 13.5,0.5 + - rot: 1.5707963267948966 rad + pos: -9.5,8.5 parent: 853 type: Transform - uid: 2137 - type: ReinforcedWindow + type: CableApcExtension components: - - pos: 13.5,-0.5 + - anchored: True + rot: 1.5707963267948966 rad + pos: -12.5,9.5 parent: 853 type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 2138 type: ReinforcedWindow components: @@ -22633,11 +22615,17 @@ entities: parent: 853 type: Transform - uid: 2139 - type: TableReinforcedGlass + type: CableApcExtension components: - - pos: 17.5,1.5 + - anchored: True + rot: 1.5707963267948966 rad + pos: -8.5,9.5 parent: 853 type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 2140 type: WallSolid components: @@ -22674,11 +22662,17 @@ entities: parent: 853 type: Transform - uid: 2145 - type: TableReinforcedGlass + type: CableApcExtension components: - - pos: 14.5,-1.5 + - anchored: True + rot: 1.5707963267948966 rad + pos: -8.5,8.5 parent: 853 type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 2146 type: CrateMaterialPlastic components: @@ -23154,12 +23148,15 @@ entities: parent: 853 type: Transform - uid: 2210 - type: WallSolid + type: AirlockMaint components: - - rot: 4.371139006309477E-08 rad - pos: 25.5,-15.5 + - pos: -23.5,18.5 parent: 853 type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 2211 type: WallSolid components: @@ -23219,8 +23216,7 @@ entities: - uid: 2219 type: WallSolid components: - - rot: 4.371139006309477E-08 rad - pos: 20.5,-17.5 + - pos: 20.5,-17.5 parent: 853 type: Transform - uid: 2220 @@ -24562,6 +24558,10 @@ entities: pos: -1.5,6.5 parent: 853 type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 2411 type: ShotgunGladstone components: @@ -24589,7 +24589,7 @@ entities: parent: 853 type: Transform - uid: 2414 - type: ClosetBase + type: ClosetMaintenanceFilledRandom components: - pos: -8.5,-25.5 parent: 853 @@ -24614,6 +24614,10 @@ entities: pos: -2.5,6.5 parent: 853 type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 2417 type: WallSolid components: @@ -24635,6 +24639,10 @@ entities: pos: -4.5,6.5 parent: 853 type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 2420 type: WallSolid components: @@ -26075,8 +26083,8 @@ entities: type: CableApcExtension components: - anchored: True - rot: 4.371139006309477E-08 rad - pos: -12.5,9.5 + rot: 1.5707963267948966 rad + pos: -9.5,8.5 parent: 853 type: Transform - visible: False @@ -26087,8 +26095,8 @@ entities: type: CableApcExtension components: - anchored: True - rot: 4.371139006309477E-08 rad - pos: -12.5,7.5 + rot: 1.5707963267948966 rad + pos: -12.5,8.5 parent: 853 type: Transform - visible: False @@ -26099,8 +26107,8 @@ entities: type: CableApcExtension components: - anchored: True - rot: 4.371139006309477E-08 rad - pos: -12.5,8.5 + rot: 1.5707963267948966 rad + pos: -11.5,8.5 parent: 853 type: Transform - visible: False @@ -26108,17 +26116,17 @@ entities: - canCollide: False type: Physics - uid: 2583 - type: CableApcExtension + type: PoweredSmallLight components: - - anchored: True - rot: 4.371139006309477E-08 rad - pos: -11.5,7.5 + - rot: 1.5707963267948966 rad + pos: -14.082738,8.770812 parent: 853 type: Transform - - visible: False - type: Sprite - - canCollide: False - type: Physics + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 2584 type: CableApcExtension components: @@ -40304,16 +40312,12 @@ entities: - startingCharge: 30000 type: Battery - uid: 3909 - type: EmergencyLight + type: WindoorMedicalLocked components: - - rot: 4.71238902409608 rad - pos: 9.704595,26.5 + - rot: 3.141592653589793 rad + pos: 15.5,-3.5 parent: 853 type: Transform - - powerLoad: 1 - type: ApcPowerReceiver - - startingCharge: 30000 - type: Battery - uid: 3910 type: EmergencyLight components: @@ -40815,23 +40819,24 @@ entities: - canCollide: False type: Physics - uid: 3965 - type: FirelockGlass + type: PoweredSmallLight components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,7.5 + - rot: -1.5707963267948966 rad + pos: -6.942113,8.770812 parent: 853 type: Transform - - airBlocked: False - type: Airtight + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer - uid: 3966 - type: FirelockGlass + type: EmergencyLight components: - - rot: 4.371139006309477E-08 rad - pos: -4.5,5.5 + - rot: -1.5707963267948966 rad + pos: -7.223363,7.817687 parent: 853 type: Transform - - airBlocked: False - type: Airtight - uid: 3967 type: CableApcExtension components: @@ -41293,23 +41298,18 @@ entities: - airBlocked: False type: Airtight - uid: 4016 - type: FirelockGlass + type: ChairOfficeDark components: - - rot: 4.371139006309477E-08 rad - pos: -4.5,4.5 + - rot: -1.5707963267948966 rad + pos: -2.5,8.5 parent: 853 type: Transform - - airBlocked: False - type: Airtight - uid: 4017 - type: FirelockGlass + type: ChairOfficeDark components: - - rot: 4.371139006309477E-08 rad - pos: -4.5,3.5 + - pos: -1.5,7.5 parent: 853 type: Transform - - airBlocked: False - type: Airtight - uid: 4018 type: Firelock components: @@ -41319,13 +41319,12 @@ entities: - airBlocked: False type: Airtight - uid: 4019 - type: FirelockGlass + type: WindoorMedicalLocked components: - - pos: 0.5,-14.5 + - rot: -1.5707963267948966 rad + pos: 0.5,-15.5 parent: 853 type: Transform - - airBlocked: False - type: Airtight - uid: 4020 type: ShellShotgunBeanbag components: @@ -41464,13 +41463,11 @@ entities: - airBlocked: False type: Airtight - uid: 4036 - type: FirelockGlass + type: Firelock components: - - pos: 15.5,8.5 + - pos: -1.5,6.5 parent: 853 type: Transform - - airBlocked: False - type: Airtight - uid: 4037 type: FirelockGlass components: @@ -41480,21 +41477,19 @@ entities: - airBlocked: False type: Airtight - uid: 4038 - type: FirelockGlass + type: VendingMachineCoffee + components: + - name: Hot drinks machine + type: MetaData + - pos: -11.5,11.5 + parent: 853 + type: Transform +- uid: 4039 + type: Table components: - pos: -10.5,11.5 parent: 853 type: Transform - - airBlocked: False - type: Airtight -- uid: 4039 - type: FirelockGlass - components: - - pos: -10.5,10.5 - parent: 853 - type: Transform - - airBlocked: False - type: Airtight - uid: 4040 type: FirelockGlass components: @@ -41512,13 +41507,17 @@ entities: - airBlocked: False type: Airtight - uid: 4042 - type: FirelockGlass + type: FoodBoxDonut components: - - pos: 0.5,-15.5 + - pos: -10.490147,11.612438 parent: 853 type: Transform - - airBlocked: False - type: Airtight + - canCollide: False + type: Physics + - containers: + storagebase: !type:Container + ents: [] + type: ContainerContainer - uid: 4043 type: FirelockGlass components: @@ -41659,23 +41658,25 @@ entities: parent: 853 type: Transform - uid: 4061 - type: FirelockGlass + type: CableApcExtension components: - - rot: 4.371139006309477E-08 rad - pos: -8.5,6.5 + - anchored: True + pos: -6.5,3.5 parent: 853 type: Transform - - airBlocked: False - type: Airtight + - visible: False + type: Sprite + - canCollide: False + type: Physics - uid: 4062 - type: FirelockGlass + type: PottedPlantRandom components: - - rot: 4.371139006309477E-08 rad - pos: -7.5,8.5 + - pos: 0.5,7.5 parent: 853 type: Transform - - airBlocked: False - type: Airtight + - containers: + stash: !type:ContainerSlot {} + type: ContainerContainer - uid: 4063 type: CableApcExtension components: @@ -42277,65 +42278,41 @@ entities: parent: 853 type: Transform - uid: 4139 - type: Bed + type: ReinforcedWindow components: - - rot: 4.371139006309477E-08 rad - pos: -2.5,7.5 + - pos: -7.5,18.5 parent: 853 type: Transform - uid: 4140 - type: Bed + type: ReinforcedWindow components: - - rot: 4.371139006309477E-08 rad - pos: 0.5,7.5 + - pos: -10.5,6.5 parent: 853 type: Transform - uid: 4141 - type: BedsheetOrange + type: WallReinforced components: - - anchored: True - rot: 4.371139006309477E-08 rad - pos: -2.5,7.5 + - pos: -3.5,9.5 parent: 853 type: Transform - - canCollide: False - type: Physics - uid: 4142 - type: BedsheetOrange + type: WallReinforced components: - - anchored: True - rot: 4.371139006309477E-08 rad - pos: 0.5,7.5 + - pos: -3.5,6.5 parent: 853 type: Transform - - canCollide: False - type: Physics - uid: 4143 - type: ClosetBase + type: TableReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -3.5,8.5 + - pos: -3.5,8.5 parent: 853 type: Transform - - IsPlaceable: False - type: PlaceableSurface - - containers: - EntityStorageComponent: !type:Container - ents: [] - type: ContainerContainer - uid: 4144 - type: ClosetBase + type: TableReinforced components: - - rot: 4.371139006309477E-08 rad - pos: -0.5,8.5 + - pos: -3.5,7.5 parent: 853 type: Transform - - IsPlaceable: False - type: PlaceableSurface - - containers: - EntityStorageComponent: !type:Container - ents: [] - type: ContainerContainer - uid: 4145 type: Poweredlight components: @@ -42544,14 +42521,12 @@ entities: morgue_tray: !type:ContainerSlot {} type: ContainerContainer - uid: 4166 - type: chem_master + type: WindoorSecurityLocked components: - - pos: 15.5,1.5 + - rot: 1.5707963267948966 rad + pos: -3.5,8.5 parent: 853 type: Transform - - containers: - ChemMaster-reagentContainerContainer: !type:ContainerSlot {} - type: ContainerContainer - uid: 4167 type: Table components: @@ -43193,15 +43168,12 @@ entities: parent: 853 type: Transform - uid: 4249 - type: ClothingBeltSecurityFilled + type: WindoorSecurityLocked components: - - pos: -6.5,13.5 + - rot: 1.5707963267948966 rad + pos: -3.5,7.5 parent: 853 type: Transform - - containers: - storagebase: !type:Container - ents: [] - type: ContainerContainer - uid: 4250 type: Shelf components: @@ -48794,66 +48766,42 @@ entities: parent: 853 type: Transform - uid: 4904 - type: TableReinforcedGlass + type: Windoor components: - - pos: 14.5,-0.5 + - rot: -1.5707963267948966 rad + pos: -3.5,8.5 parent: 853 type: Transform - uid: 4905 - type: BoxSyringe + type: TableReinforced components: - - pos: 14.469343,-0.36327124 + - pos: -2.5,6.5 parent: 853 type: Transform - - canCollide: False - type: Physics - - containers: - storagebase: !type:Container - ents: [] - type: ContainerContainer - uid: 4906 - type: BoxSyringe + type: TableReinforced components: - - pos: 14.469343,-0.73827124 + - pos: -1.5,6.5 parent: 853 type: Transform - - canCollide: False - type: Physics - - containers: - storagebase: !type:Container - ents: [] - type: ContainerContainer - uid: 4907 - type: SheetPlasma1 + type: LowWall components: - - pos: 14.484968,-1.2538962 + - pos: -0.5,6.5 parent: 853 type: Transform - - canCollide: False - type: Physics - uid: 4908 - type: BoxBeaker + type: LowWall components: - - pos: 18.53261,0.71732765 + - pos: 0.5,6.5 parent: 853 type: Transform - - canCollide: False - type: Physics - - containers: - storagebase: !type:Container - ents: [] - type: ContainerContainer - uid: 4909 - type: KitchenReagentGrinder + type: ReinforcedWindow components: - - pos: 18.5,1.5 + - pos: -0.5,6.5 parent: 853 type: Transform - - containers: - ReagentGrinder-reagentContainerContainer: !type:ContainerSlot {} - ReagentGrinder-entityContainerContainer: !type:Container - ents: [] - type: ContainerContainer - uid: 4910 type: TableReinforced components: @@ -48873,10 +48821,9 @@ entities: light_bulb: !type:ContainerSlot {} type: ContainerContainer - uid: 4912 - type: ChairOfficeDark + type: ReinforcedWindow components: - - rot: 3.141592653589793 rad - pos: 17.5,0.5 + - pos: 0.5,6.5 parent: 853 type: Transform - uid: 4913 @@ -48886,9 +48833,10 @@ entities: parent: 853 type: Transform - uid: 4914 - type: ChairOfficeDark + type: WindoorSecurityLocked components: - - pos: 15.5,-2.5 + - rot: 3.141592653589793 rad + pos: -2.5,6.5 parent: 853 type: Transform - uid: 4915 @@ -48898,43 +48846,40 @@ entities: parent: 853 type: Transform - uid: 4916 - type: Firelock + type: WindoorSecurityLocked components: - - pos: 15.5,-3.5 + - rot: 3.141592653589793 rad + pos: -1.5,6.5 parent: 853 type: Transform - uid: 4917 - type: SheetPlasma1 + type: Windoor components: - - pos: 14.531843,-1.5507712 + - pos: -2.5,6.5 parent: 853 type: Transform - - canCollide: False - type: Physics - uid: 4918 - type: SheetPlasma1 + type: Windoor components: - - pos: 14.578718,-1.8476462 + - pos: -1.5,6.5 parent: 853 type: Transform - - canCollide: False - type: Physics - uid: 4919 - type: Dropper + type: AirlockSecurityLocked components: - - pos: 14.531843,-2.2695212 + - pos: 0.5,9.5 parent: 853 type: Transform - - canCollide: False - type: Physics + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 4920 - type: Dropper + type: LowWall components: - - pos: 14.531843,-2.5351462 + - pos: -0.5,9.5 parent: 853 type: Transform - - canCollide: False - type: Physics - uid: 4921 type: ExtinguisherCabinetFilled components: @@ -49218,4 +49163,1531 @@ entities: pos: 2.5,31.5 parent: 853 type: Transform +- uid: 4961 + type: WallReinforced + components: + - pos: 28.5,-22.5 + parent: 853 + type: Transform +- uid: 4962 + type: WallReinforced + components: + - pos: 24.5,-26.5 + parent: 853 + type: Transform +- uid: 4963 + type: WallReinforced + components: + - pos: 25.5,-26.5 + parent: 853 + type: Transform +- uid: 4964 + type: WallReinforced + components: + - pos: 26.5,-26.5 + parent: 853 + type: Transform +- uid: 4965 + type: WallReinforced + components: + - pos: 28.5,-20.5 + parent: 853 + type: Transform +- uid: 4966 + type: WallReinforced + components: + - pos: 28.5,-23.5 + parent: 853 + type: Transform +- uid: 4967 + type: WallReinforced + components: + - pos: 28.5,-21.5 + parent: 853 + type: Transform +- uid: 4968 + type: WallReinforced + components: + - pos: 28.5,-24.5 + parent: 853 + type: Transform +- uid: 4969 + type: WallReinforced + components: + - pos: 28.5,-25.5 + parent: 853 + type: Transform +- uid: 4970 + type: WallReinforced + components: + - pos: 28.5,-26.5 + parent: 853 + type: Transform +- uid: 4971 + type: WallReinforced + components: + - pos: 27.5,-26.5 + parent: 853 + type: Transform +- uid: 4972 + type: WallSolid + components: + - pos: 25.5,-24.5 + parent: 853 + type: Transform +- uid: 4973 + type: WallSolid + components: + - pos: 25.5,-25.5 + parent: 853 + type: Transform +- uid: 4974 + type: AirlockMaint + components: + - pos: 25.5,-23.5 + parent: 853 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer +- uid: 4975 + type: WallSolid + components: + - pos: 25.5,-21.5 + parent: 853 + type: Transform +- uid: 4976 + type: WallSolid + components: + - pos: 27.5,-21.5 + parent: 853 + type: Transform +- uid: 4977 + type: WallSolid + components: + - pos: 26.5,-21.5 + parent: 853 + type: Transform +- uid: 4978 + type: WallSolid + components: + - pos: 25.5,-15.5 + parent: 853 + type: Transform +- uid: 4979 + type: AcousticGuitarInstrument + components: + - pos: 27.500875,-23.377102 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 4980 + type: WallSolid + components: + - pos: 25.5,-22.5 + parent: 853 + type: Transform +- uid: 4981 + type: Table + components: + - pos: 27.5,-25.5 + parent: 853 + type: Transform +- uid: 4982 + type: Table + components: + - pos: 26.5,-25.5 + parent: 853 + type: Transform +- uid: 4983 + type: PoweredSmallLight + components: + - rot: -1.5707963267948966 rad + pos: 28,-24.5 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 4984 + type: PoweredSmallLight + components: + - pos: 25.5,-20 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 4985 + type: PoweredSmallLight + components: + - rot: 1.5707963267948966 rad + pos: 24,-23.5 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 4986 + type: ComfyChair + components: + - pos: 26.5,-24.5 + parent: 853 + type: Transform +- uid: 4987 + type: CableApcExtension + components: + - anchored: True + pos: 26.5,-19.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 4988 + type: CableApcExtension + components: + - anchored: True + pos: 26.5,-20.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 4989 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-19.5 + parent: 853 + type: Transform +- uid: 4990 + type: CableApcExtension + components: + - anchored: True + pos: 23.5,-24.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 4991 + type: CableApcExtension + components: + - anchored: True + pos: 23.5,-23.5 + parent: 853 + type: Transform +- uid: 4992 + type: CableApcExtension + components: + - anchored: True + pos: 23.5,-22.5 + parent: 853 + type: Transform +- uid: 4993 + type: CableApcExtension + components: + - anchored: True + pos: 23.5,-21.5 + parent: 853 + type: Transform +- uid: 4994 + type: CableApcExtension + components: + - anchored: True + pos: 23.5,-20.5 + parent: 853 + type: Transform +- uid: 4995 + type: CableApcExtension + components: + - anchored: True + pos: 23.5,-19.5 + parent: 853 + type: Transform +- uid: 4996 + type: CableApcExtension + components: + - anchored: True + pos: 24.5,-24.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 4997 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-24.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 4998 + type: CableApcExtension + components: + - anchored: True + pos: 26.5,-24.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 4999 + type: CableApcExtension + components: + - anchored: True + pos: 27.5,-24.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5000 + type: AirlockMaint + components: + - pos: 26.5,-19.5 + parent: 853 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer +- uid: 5001 + type: AirlockMaint + components: + - pos: -19.5,19.5 + parent: 853 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer +- uid: 5002 + type: WallReinforced + components: + - pos: -27.5,16.5 + parent: 853 + type: Transform +- uid: 5003 + type: WallReinforced + components: + - pos: -27.5,17.5 + parent: 853 + type: Transform +- uid: 5004 + type: WallReinforced + components: + - pos: -27.5,18.5 + parent: 853 + type: Transform +- uid: 5005 + type: WallReinforced + components: + - pos: -27.5,19.5 + parent: 853 + type: Transform +- uid: 5006 + type: WallReinforced + components: + - pos: -27.5,20.5 + parent: 853 + type: Transform +- uid: 5007 + type: CrateEngineeringCableMV + components: + - pos: -20.5,16.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5008 + type: WallSolid + components: + - pos: -20.5,18.5 + parent: 853 + type: Transform +- uid: 5009 + type: WallSolid + components: + - pos: -22.5,18.5 + parent: 853 + type: Transform +- uid: 5010 + type: WallSolid + components: + - pos: -21.5,18.5 + parent: 853 + type: Transform +- uid: 5011 + type: WallSolid + components: + - pos: -24.5,18.5 + parent: 853 + type: Transform +- uid: 5012 + type: WallSolid + components: + - pos: -24.5,17.5 + parent: 853 + type: Transform +- uid: 5013 + type: WallSolid + components: + - pos: -24.5,16.5 + parent: 853 + type: Transform +- uid: 5014 + type: WallReinforced + components: + - pos: -27.5,21.5 + parent: 853 + type: Transform +- uid: 5015 + type: WallReinforced + components: + - pos: -26.5,21.5 + parent: 853 + type: Transform +- uid: 5016 + type: WallReinforced + components: + - pos: -25.5,21.5 + parent: 853 + type: Transform +- uid: 5017 + type: WallReinforced + components: + - pos: -24.5,21.5 + parent: 853 + type: Transform +- uid: 5018 + type: WallReinforced + components: + - pos: -23.5,21.5 + parent: 853 + type: Transform +- uid: 5019 + type: WallReinforced + components: + - pos: -22.5,21.5 + parent: 853 + type: Transform +- uid: 5020 + type: WallReinforced + components: + - pos: -21.5,21.5 + parent: 853 + type: Transform +- uid: 5021 + type: WallReinforced + components: + - pos: -20.5,21.5 + parent: 853 + type: Transform +- uid: 5022 + type: CableApcExtension + components: + - anchored: True + pos: -27.5,14.5 + parent: 853 + type: Transform +- uid: 5023 + type: CableApcExtension + components: + - anchored: True + pos: -26.5,14.5 + parent: 853 + type: Transform +- uid: 5024 + type: CableApcExtension + components: + - anchored: True + pos: -25.5,14.5 + parent: 853 + type: Transform +- uid: 5025 + type: CableApcExtension + components: + - anchored: True + pos: -25.5,15.5 + parent: 853 + type: Transform +- uid: 5026 + type: CableApcExtension + components: + - anchored: True + pos: -19.5,19.5 + parent: 853 + type: Transform +- uid: 5027 + type: CableApcExtension + components: + - anchored: True + pos: -18.5,19.5 + parent: 853 + type: Transform +- uid: 5028 + type: CableApcExtension + components: + - anchored: True + pos: -19.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5029 + type: CableApcExtension + components: + - anchored: True + pos: -20.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5030 + type: CableApcExtension + components: + - anchored: True + pos: -21.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5031 + type: CableApcExtension + components: + - anchored: True + pos: -22.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5032 + type: CableApcExtension + components: + - anchored: True + pos: -23.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5033 + type: CableApcExtension + components: + - anchored: True + pos: -23.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5034 + type: PoweredSmallLight + components: + - pos: -21.5,18 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 5035 + type: PoweredSmallLight + components: + - pos: -23.5,21 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 5036 + type: PoweredSmallLight + components: + - rot: 1.5707963267948966 rad + pos: -27,17.5 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 5037 + type: CrateEngineeringCableLV + components: + - pos: -20.5,17.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5038 + type: CableHVStack + components: + - pos: -22.628485,16.665777 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5039 + type: Rack + components: + - pos: -23.5,16.5 + parent: 853 + type: Transform +- uid: 5040 + type: RandomSpawner + components: + - pos: -20.5,17.5 + parent: 853 + type: Transform +- uid: 5041 + type: Rack + components: + - pos: -22.5,16.5 + parent: 853 + type: Transform +- uid: 5042 + type: ClothingMaskGas + components: + - pos: -23.68732,16.378263 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5043 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-23.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5044 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-22.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5045 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-23.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5046 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-22.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5047 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-23.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5048 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-22.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5049 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-24.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5050 + type: CableApcExtension + components: + - anchored: True + pos: 25.5,-22.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5051 + type: CableApcExtension + components: + - anchored: True + pos: -25.5,16.5 + parent: 853 + type: Transform +- uid: 5052 + type: CableApcExtension + components: + - anchored: True + pos: -25.5,17.5 + parent: 853 + type: Transform +- uid: 5053 + type: CableApcExtension + components: + - anchored: True + pos: -25.5,18.5 + parent: 853 + type: Transform +- uid: 5054 + type: CableApcExtension + components: + - anchored: True + pos: -24.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5055 + type: CableApcExtension + components: + - anchored: True + pos: -23.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5056 + type: CableApcExtension + components: + - anchored: True + pos: -22.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5057 + type: CableApcExtension + components: + - anchored: True + pos: -21.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5058 + type: CableApcExtension + components: + - anchored: True + pos: -23.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5059 + type: CableApcExtension + components: + - anchored: True + pos: -24.5,18.5 + parent: 853 + type: Transform + - visible: False + type: Sprite + - canCollide: False + type: Physics +- uid: 5060 + type: Firelock + components: + - pos: 26.5,-19.5 + parent: 853 + type: Transform +- uid: 5061 + type: Firelock + components: + - pos: 23.5,-24.5 + parent: 853 + type: Transform +- uid: 5062 + type: Firelock + components: + - pos: -25.5,15.5 + parent: 853 + type: Transform +- uid: 5063 + type: Firelock + components: + - pos: -19.5,19.5 + parent: 853 + type: Transform +- uid: 5064 + type: ClosetMaintenanceFilledRandom + components: + - pos: 26.5,-22.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5065 + type: SignToolStorage + components: + - pos: 26.514614,-21.5 + parent: 853 + type: Transform +- uid: 5066 + type: Rack + components: + - pos: 27.5,-22.5 + parent: 853 + type: Transform +- uid: 5067 + type: CableMVStack + components: + - rot: 0.0004529799334704876 rad + pos: 27.736885,-22.289246 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5068 + type: Wirecutter + components: + - pos: 27.503157,-22.435783 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5069 + type: CableApcStack + components: + - pos: 27.36774,-22.300274 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5070 + type: SignEngineering + components: + - pos: -22.503485,18.5 + parent: 853 + type: Transform +- uid: 5071 + type: ClothingHandsGlovesCombat + components: + - pos: -23.447739,16.722254 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5072 + type: HarmonicaInstrument + components: + - pos: -22.46857,16.513775 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5073 + type: ClothingHeadHatOrangesoft + components: + - pos: -23.316175,16.5 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5074 + type: ClosetMaintenanceFilledRandom + components: + - pos: -26.5,19.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5075 + type: ClosetEmergencyFilledRandom + components: + - pos: -26.5,18.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5076 + type: ClosetFireFilled + components: + - pos: -26.5,17.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5077 + type: ClothingHandsGlovesColorYellow + components: + - pos: 27.39341,-25.414946 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5078 + type: DrinkBottleBeer + components: + - pos: 26.341326,-25.258587 + parent: 853 + type: Transform + - caps: Refillable, Drainable + type: SolutionContainer + - canCollide: False + type: Physics +- uid: 5079 + type: DrinkBeerglass + components: + - pos: 26.73716,-25.331554 + parent: 853 + type: Transform + - caps: Refillable, Drainable + type: SolutionContainer + - canCollide: False + type: Physics +- uid: 5080 + type: ClosetFireFilled + components: + - pos: 24.5,-25.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5081 + type: ClosetEmergencyFilledRandom + components: + - pos: 27.5,-20.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5082 + type: CableHVStack + components: + - pos: -22.638397,16.589691 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5083 + type: WallSolid + components: + - pos: 0.5,-14.5 + parent: 853 + type: Transform +- uid: 5084 + type: WindoorCommandLocked + components: + - rot: 1.5707963267948966 rad + pos: 6.5,20.5 + parent: 853 + type: Transform +- uid: 5085 + type: WallReinforced + components: + - pos: 11.5,27.5 + parent: 853 + type: Transform +- uid: 5086 + type: WallReinforced + components: + - pos: 12.5,27.5 + parent: 853 + type: Transform +- uid: 5087 + type: WallReinforced + components: + - pos: 13.5,27.5 + parent: 853 + type: Transform +- uid: 5088 + type: WallReinforced + components: + - pos: 13.5,26.5 + parent: 853 + type: Transform +- uid: 5089 + type: AirlockCommandLocked + components: + - pos: 10.5,26.5 + parent: 853 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer +- uid: 5090 + type: ToiletEmpty + components: + - rot: -1.5707963267948966 rad + pos: 12.5,26.5 + parent: 853 + type: Transform + - containers: + stash: !type:ContainerSlot {} + type: ContainerContainer +- uid: 5091 + type: SoapNT + components: + - rot: -1.5668895244598389 rad + pos: 11.687332,26.585844 + parent: 853 + type: Transform +- uid: 5092 + type: LowWall + components: + - pos: -1.5,9.5 + parent: 853 + type: Transform +- uid: 5093 + type: WallReinforced + components: + - pos: -2.5,9.5 + parent: 853 + type: Transform +- uid: 5094 + type: AirlockSecurityGlassLocked + components: + - pos: -5.5,9.5 + parent: 853 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer +- uid: 5095 + type: AirlockSecurityGlassLocked + components: + - pos: -4.5,9.5 + parent: 853 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer +- uid: 5096 + type: AirlockSecurityGlassLocked + components: + - pos: -4.5,6.5 + parent: 853 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer +- uid: 5097 + type: LowWall + components: + - pos: 13.5,1.5 + parent: 853 + type: Transform +- uid: 5098 + type: LowWall + components: + - pos: 13.5,-2.5 + parent: 853 + type: Transform +- uid: 5099 + type: ReinforcedWindow + components: + - pos: 13.5,1.5 + parent: 853 + type: Transform +- uid: 5100 + type: LargeBeaker + components: + - pos: 18.88963,1.6178906 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5101 + type: SheetPlasma1 + components: + - pos: 19.561504,-0.1164844 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5102 + type: SheetPlasma1 + components: + - pos: 19.54588,0.1647656 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5103 + type: TableReinforcedGlass + components: + - rot: -1.5707963267948966 rad + pos: 18.5,1.5 + parent: 853 + type: Transform +- uid: 5104 + type: TableReinforcedGlass + components: + - rot: -1.5707963267948966 rad + pos: 19.5,1.5 + parent: 853 + type: Transform +- uid: 5105 + type: TableReinforcedGlass + components: + - rot: -1.5707963267948966 rad + pos: 19.5,0.5 + parent: 853 + type: Transform +- uid: 5106 + type: TableReinforcedGlass + components: + - rot: -1.5707963267948966 rad + pos: 19.5,-0.5 + parent: 853 + type: Transform +- uid: 5107 + type: TableReinforcedGlass + components: + - rot: -1.5707963267948966 rad + pos: 15.5,1.5 + parent: 853 + type: Transform +- uid: 5108 + type: LargeBeaker + components: + - pos: 18.405254,1.6178906 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5109 + type: LowWall + components: + - pos: 13.5,0.5 + parent: 853 + type: Transform +- uid: 5110 + type: ReinforcedWindow + components: + - pos: 13.5,0.5 + parent: 853 + type: Transform +- uid: 5111 + type: TableReinforced + components: + - pos: 13.5,-0.5 + parent: 853 + type: Transform +- uid: 5112 + type: WindoorMedicalLocked + components: + - rot: 1.5707963267948966 rad + pos: 13.5,-0.5 + parent: 853 + type: Transform +- uid: 5113 + type: Windoor + components: + - rot: -1.5707963267948966 rad + pos: 13.5,-0.5 + parent: 853 + type: Transform +- uid: 5114 + type: TableReinforcedGlass + components: + - rot: -1.5707963267948966 rad + pos: 14.5,1.5 + parent: 853 + type: Transform +- uid: 5115 + type: TableReinforcedGlass + components: + - rot: -1.5707963267948966 rad + pos: 14.5,0.5 + parent: 853 + type: Transform +- uid: 5116 + type: Windoor + components: + - pos: 15.5,8.5 + parent: 853 + type: Transform +- uid: 5117 + type: Dropper + components: + - pos: 14.514629,0.4460156 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5118 + type: Dropper + components: + - pos: 14.514629,0.6335156 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5119 + type: Dropper + components: + - pos: 14.530254,0.8210156 + parent: 853 + type: Transform + - canCollide: False + type: Physics +- uid: 5120 + type: BoxSyringe + components: + - pos: 14.499004,1.6491406 + parent: 853 + type: Transform + - canCollide: False + type: Physics + - containers: + storagebase: !type:Container + ents: [] + type: ContainerContainer +- uid: 5121 + type: ChairOfficeLight + components: + - rot: 3.141592653589793 rad + pos: 17.5,0.5 + parent: 853 + type: Transform +- uid: 5122 + type: LockerChemistryFilled + components: + - rot: -1.5707963267948966 rad + pos: 19.5,-2.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5123 + type: LockerMedicineFilled + components: + - rot: -1.5707963267948966 rad + pos: 18.5,-2.5 + parent: 853 + type: Transform + - IsPlaceable: False + type: PlaceableSurface + - containers: + EntityStorageComponent: !type:Container + ents: [] + type: ContainerContainer +- uid: 5124 + type: ChairOfficeLight + components: + - rot: -1.5707963267948966 rad + pos: 14.5,-0.5 + parent: 853 + type: Transform +- uid: 5125 + type: Windoor + components: + - pos: 15.5,-3.5 + parent: 853 + type: Transform +- uid: 5126 + type: DisposalTrunk + components: + - anchored: True + rot: 3.141592653589793 rad + pos: 14.5,-2.5 + parent: 853 + type: Transform + - containers: + DisposalEntry: !type:Container + ents: [] + type: ContainerContainer +- uid: 5127 + type: DisposalUnit + components: + - rot: 3.141592653589793 rad + pos: 14.5,-2.5 + parent: 853 + type: Transform + - containers: + DisposalUnit: !type:Container + ents: [] + type: ContainerContainer +- uid: 5128 + type: DisposalPipe + components: + - anchored: True + rot: 3.141592653589793 rad + pos: 14.5,-1.5 + parent: 853 + type: Transform + - containers: + DisposalTransit: !type:Container + ents: [] + type: ContainerContainer +- uid: 5129 + type: DisposalBend + components: + - anchored: True + pos: 14.5,-0.5 + parent: 853 + type: Transform + - containers: + DisposalBend: !type:Container + ents: [] + type: ContainerContainer +- uid: 5130 + type: DisposalJunction + components: + - anchored: True + rot: 3.141592653589793 rad + pos: 12.5,-0.5 + parent: 853 + type: Transform + - containers: + DisposalJunction: !type:Container + ents: [] + type: ContainerContainer +- uid: 5131 + type: Poweredlight + components: + - pos: 18.51463,1.8678906 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 5132 + type: Poweredlight + components: + - rot: -1.5707963267948966 rad + pos: 19.936504,-0.3821094 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 5133 + type: Firelock + components: + - pos: 13.5,-0.5 + parent: 853 + type: Transform +- uid: 5134 + type: Windoor + components: + - rot: 1.5707963267948966 rad + pos: 0.5,-15.5 + parent: 853 + type: Transform +- uid: 5135 + type: Firelock + components: + - pos: 0.5,-15.5 + parent: 853 + type: Transform +- uid: 5136 + type: Poweredlight + components: + - pos: -2.5304983,9.057686 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 5137 + type: Poweredlight + components: + - pos: 12.5166855,27.017359 + parent: 853 + type: Transform + - powerLoad: 0 + type: ApcPowerReceiver + - containers: + light_bulb: !type:ContainerSlot {} + type: ContainerContainer +- uid: 5138 + type: Firelock + components: + - pos: 15.5,8.5 + parent: 853 + type: Transform +- uid: 5139 + type: SignDirectionalSupply + components: + - rot: 3.141592653589793 rad + pos: 11.424202,6.304905 + parent: 853 + type: Transform +- uid: 5140 + type: Firelock + components: + - pos: -10.5,9.5 + parent: 853 + type: Transform +- uid: 5141 + type: WindoorCargoLocked + components: + - rot: 3.141592653589793 rad + pos: 15.5,8.5 + parent: 853 + type: Transform +- uid: 5142 + type: Firelock + components: + - pos: -2.5,6.5 + parent: 853 + type: Transform +- uid: 5143 + type: Medkit + components: + - pos: 15.0694065,1.5443661 + parent: 853 + type: Transform + - canCollide: False + type: Physics + - containers: + storagebase: !type:Container + ents: [] + type: ContainerContainer +- uid: 5144 + type: MedkitToxin + components: + - pos: 15.6162815,1.5443661 + parent: 853 + type: Transform + - canCollide: False + type: Physics + - containers: + storagebase: !type:Container + ents: [] + type: ContainerContainer +- uid: 5145 + type: Firelock + components: + - pos: 6.5,20.5 + parent: 853 + type: Transform +- uid: 5146 + type: Firelock + components: + - pos: -3.5,8.5 + parent: 853 + type: Transform +- uid: 5147 + type: Firelock + components: + - pos: -3.5,7.5 + parent: 853 + type: Transform ... diff --git a/Resources/Prototypes/Body/Mechanisms/human.yml b/Resources/Prototypes/Body/Mechanisms/human.yml index aec9769c51..44652e4e65 100644 --- a/Resources/Prototypes/Body/Mechanisms/human.yml +++ b/Resources/Prototypes/Body/Mechanisms/human.yml @@ -50,18 +50,30 @@ compatibility: Biological - type: entity - id: OrganHumanHeart + id: OrganHumanTongue parent: BaseHumanOrgan - name: heart - description: "I feel bad for the heartless bastard who lost this." + name: tongue + description: "A fleshy muscle mostly used for lying." components: - type: Sprite - state: heart-on + state: tongue + - type: Mechanism + size: 1 + compatibility: Biological + +- type: entity + id: OrganHumanAppendix + parent: BaseHumanOrgan + name: appendix + components: + - type: Sprite + layers: + - state: appendix + - state: appendix-inflamed + visible: false - type: Mechanism size: 1 compatibility: Biological - behaviors: - - !type:HeartBehavior {} - type: entity id: OrganHumanEars @@ -91,6 +103,105 @@ behaviors: - !type:LungBehavior {} +- type: entity + id: OrganHumanHeart + parent: BaseHumanOrgan + name: heart + description: "I feel bad for the heartless bastard who lost this." + components: + - type: Sprite + state: heart-on + - type: Mechanism + size: 1 + compatibility: Biological + behaviors: + - !type:HeartBehavior {} + # The heart 'metabolizes' medicines and poisons that aren't filtered out by other organs. + # This is done because these chemicals need to have some effect even if they aren't being filtered out of your body. + # You're technically 'immune to poison' without a heart, but.. uhh, you'll have bigger problems on your hands. + - type: Metabolizer + metabolisms: + Arithrazine: + effects: + - !type:HealthChange + damageClass: Toxin + healthChange: -1 + - !type:HealthChange + damageClass: Brute + healthChange: 0.5 + Bicaridine: + effects: + - !type:HealthChange + damageClass: Brute + healthChange: -2 + Dermaline: + effects: + - !type:HealthChange + damageClass: Burn + healthChange: -3 + Dexalin: + effects: + - !type:HealthChange + damageClass: Airloss + healthChange: -1 + DexalinPlus: + effects: + - !type:HealthChange + damageClass: Airloss + healthChange: -3 + Dylovene: + effects: + - !type:HealthChange + damageClass: Toxin + healthChange: -1 + Ephedrine: + effects: + - !type:MovespeedModifier + walkSpeedModifier: 1.2 + sprintSpeedModifier: 1.2 + HeartbreakerToxin: + effects: + - !type:HealthChange + damageClass: Airloss + healthChange: 4 + Kelotane: + effects: + - !type:HealthChange + damageClass: Burn + healthChange: -1 + Lexorin: + effects: + - !type:HealthChange + damageClass: Airloss + healthChange: 7 + Meth: + effects: + - !type:HealthChange + healthChange: 2.5 + damageClass: Toxin + - !type:MovespeedModifier + walkSpeedModifier: 1.3 + sprintSpeedModifier: 1.3 + Omnizine: + effects: + - !type:HealthChange + healthChange: -2 + damageClass: Burn + - !type:HealthChange + healthChange: -2 + damageClass: Toxin + - !type:HealthChange + healthChange: -2 + damageClass: Airloss + - !type:HealthChange + healthChange: -2 + damageClass: Brute + Synaptizine: + effects: + - !type:HealthChange + damageClass: Toxin + healthChange: 0.5 + - type: entity id: OrganHumanStomach parent: BaseHumanOrgan @@ -108,6 +219,93 @@ digestionDelay: 20 - type: SolutionContainer maxVol: 250 + # The stomach metabolizes stuff like foods and drinks. + # TODO: Have it work off of the ent's solution container, and move this + # to intestines instead. + - type: Metabolizer # Release me from this hell called 1 reagent for every drink + # TODO please make every drink their own base thing + metabolisms: + Flour: + effects: + - !type:SatiateHunger + JuiceApple: + effects: + - !type:SatiateThirst + JuiceBerry: + effects: + - !type:SatiateThirst + JuiceBanana: + effects: + - !type:SatiateThirst + JuiceCarrot: + effects: + - !type:SatiateThirst + JuiceLime: + effects: + - !type:SatiateThirst + JuiceLemon: + effects: + - !type:SatiateThirst + JuiceGrape: + effects: + - !type:SatiateThirst + JuiceOrange: + effects: + - !type:SatiateThirst + JuiceTomato: + effects: + - !type:SatiateThirst + JuiceBerryPoison: + effects: + - !type:SatiateThirst + - !type:HealthChange + damageClass: Toxin + healthChange: 1 + JuiceWatermelon: + effects: + - !type:SatiateThirst + JuicePineapple: + effects: + - !type:SatiateThirst + Nutriment: + effects: + - !type:SatiateHunger + Water: + effects: + - !type:SatiateThirst + hydrationFactor: 2 + Coffee: + effects: + - !type:SatiateThirst + Tea: + effects: + - !type:SatiateThirst + Milk: + effects: + - !type:SatiateThirst + SpoiledMilk: + effects: + - !type:SatiateThirst + hydrationFactor: -2 + MilkSoy: + effects: + - !type:SatiateThirst + hydrationFactor: 2 # soyboys stay winning + MilkOat: + effects: + - !type:SatiateThirst + hydrationFactor: 2 # oatboys stay winning + Cola: + effects: + - !type:SatiateThirst + hydrationFactor: 0.5 # sodaboys stay losing + ThirteenLoko: + effects: + - !type:SatiateThirst + hydrationFactor: 2 + - !type:HealthChange + damageClass: Toxin + healthChange: 1 - type: entity id: OrganHumanLiver @@ -120,12 +318,17 @@ - type: Mechanism size: 1 compatibility: Biological - behaviors: - - !type:LiverBehavior - alcoholLethality: 0.005 - alcoholExponent: 1.6 - toxinTolerance: 3 - toxinLethality: 0.01 + - type: Metabolizer # The liver metabolizes certain chemicals only, like alcohol. + metabolisms: # TODO add the rest of alcohol + CreamyDelight: + effects: + - !type:SatiateThirst + Lean: + effects: + - !type:SatiateThirst + LeanShine: # who added this? + effects: + - !type:SatiateThirst - type: entity id: OrganHumanKidneys @@ -141,28 +344,4 @@ size: 1 compatibility: Biological -- type: entity - id: OrganHumanTongue - parent: BaseHumanOrgan - name: tongue - description: "A fleshy muscle mostly used for lying." - components: - - type: Sprite - state: tongue - - type: Mechanism - size: 1 - compatibility: Biological -- type: entity - id: OrganHumanAppendix - parent: BaseHumanOrgan - name: appendix - components: - - type: Sprite - layers: - - state: appendix - - state: appendix-inflamed - visible: false - - type: Mechanism - size: 1 - compatibility: Biological diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml index 415db3246d..660e6ff506 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml @@ -131,6 +131,10 @@ prob: 1 - id: ClothingMaskSterile prob: 1 + - id: ClothingHeadHelmetHardsuitMedical + prob: 1 + - id: ClothingOuterHardsuitMedical + prob: 1 - type: entity id: LockerResearchDirectorFilled diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index 78cb14b62b..0a789fc616 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -32,3 +32,37 @@ prob: 0.6 - id: ClothingOuterSuitFire prob: 0.75 + +- type: entity + id: ClosetMaintenanceFilledRandom + suffix: Filled, Random + parent: ClosetMaintenance + components: + - type: StorageFill + contents: + - id: lantern + prob: 0.66 + - id: Wirecutter + prob: 0.44 + - id: Screwdriver + prob: 0.44 + - id: Wrench + prob: 0.44 + - id: Crowbar + prob: 0.44 + - id: Welder + prob: 0.44 + - id: Multitool + prob: 0.44 + - id: Soap + prob: 0.44 + - id: PlushieCarp + prob: 0.2 + - id: PlushieSlime + prob: 0.2 + - id: PlushieSnake + prob: 0.2 + - id: ClothingHandsGlovesColorYellow + prob: 0.33 + - id: ClothingBeltUtility + prob: 0.33 diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml index d2e13f35e9..8585bae168 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml @@ -84,3 +84,7 @@ prob: 0.8 - id: TomatoSeeds prob: 1 + - id: ClothingUniformOveralls + prob: 1 + - id: ClothingHeadHatTrucker + prob: 0.1 diff --git a/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml b/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml index 70073d0dde..a13d46df4b 100644 --- a/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml +++ b/Resources/Prototypes/Catalog/ReagentDispensers/chemical.yml @@ -9,6 +9,7 @@ - Fluorine - Glucose - Hydrogen + - Iodine - Iron - Lithium - Mercury @@ -25,3 +26,4 @@ - SulfuricAcid - Uranium - Water + diff --git a/Resources/Prototypes/Catalog/Research/technologies.yml b/Resources/Prototypes/Catalog/Research/technologies.yml index 1c39d4178d..45e9e60a8b 100644 --- a/Resources/Prototypes/Catalog/Research/technologies.yml +++ b/Resources/Prototypes/Catalog/Research/technologies.yml @@ -241,6 +241,8 @@ - FirelockElectronics - DoorElectronics - APCElectronics + - CloningPodMachineCircuitboard + - MedicalScannerMachineCircuitboard # Bluespace Theory Technology Tree diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index 783c4e9186..fb84c0f136 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -23,7 +23,7 @@ - type: entity parent: ClothingHeadBase id: ClothingHeadHatBeretEngineering - name: engineering beret + name: engineering beret description: A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety. components: - type: Sprite @@ -360,3 +360,14 @@ sprite: Clothing/Head/Hats/xmascrown.rsi - type: Clothing sprite: Clothing/Head/Hats/xmascrown.rsi + +- type: entity + parent: ClothingHeadBase + id: ClothingHeadHatTrucker + name: truckers hat + description: Formerly Chucks, this hat is yours now. + components: + - type: Sprite + sprite: Clothing/Head/Hats/truckershat.rsi + - type: Clothing + sprite: Clothing/Head/Hats/truckershat.rsi diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml index 77f7eb264e..f71364f361 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml @@ -571,3 +571,14 @@ sprite: Clothing/Uniforms/Jumpsuit/rainbow.rsi - type: Clothing sprite: Clothing/Uniforms/Jumpsuit/rainbow.rsi + +- type: entity + parent: ClothingUniformBase + id: ClothingUniformOveralls + name: overalls + description: Great for working outdoors. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/overalls.rsi + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/overalls.rsi diff --git a/Resources/Prototypes/Entities/Markers/clientsideclone.yml b/Resources/Prototypes/Entities/Markers/clientsideclone.yml index a3c662c28a..e935d5685f 100644 --- a/Resources/Prototypes/Entities/Markers/clientsideclone.yml +++ b/Resources/Prototypes/Entities/Markers/clientsideclone.yml @@ -4,5 +4,4 @@ abstract: true components: - type: Sprite - - type: Physics - type: AnimationPlayer diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index d7bbe39452..b8eb93bea9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -68,7 +68,7 @@ currentTemperature: 310.15 specificHeat: 42 tempDamageCoefficient: 0.1 - - type: Metabolism + - type: Respirator metabolismHeat: 5000 radiatedHeat: 400 implicitHeatRegulation: 5000 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index 7b5b7dfcab..cbbabdf716 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -38,7 +38,7 @@ 0: !type:NormalMobState {} 150: !type:CriticalMobState {} 200: !type:DeadMobState {} - - type: Metabolism + - type: Respirator - type: UnarmedCombat range: 1.5 arcwidth: 0 diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 0b9407b025..84fa813d13 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -168,7 +168,7 @@ preset: HumanPreset - type: Damageable damageContainer: biologicalDamageContainer - - type: Metabolism + - type: Respirator metabolismHeat: 5000 radiatedHeat: 400 implicitHeatRegulation: 5000 diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index dd9f5119c7..1f097c19a2 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -84,7 +84,7 @@ - type: Body template: HumanoidTemplate preset: VoxPreset - - type: Metabolism + - type: Respirator needsGases: Nitrogen: 0.00060763888 producesGases: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml index 507493fb6e..d7c7a4493b 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml @@ -11,7 +11,6 @@ reagents: - ReagentId: Nutriment Quantity: 20 - trash: BowlBigTrash - type: Sprite sprite: Objects/Consumable/Food/bowl.rsi netsync: false diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 26c589371b..1871429c56 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -28,3 +28,32 @@ Amount: 2 DefaultPrototype: Beaker ExamineName: Glass Beaker + +- type: entity + id: CloningPodMachineCircuitboard + parent: BaseMachineCircuitboard + name: Cloning Pod (Machine Board) + components: + - type: MachineBoard + prototype: CloningPod + requirements: + ScanningModule: 2 + Manipulator: 2 + materialRequirements: + Glass: 1 + Cable: 1 + +- type: entity + id: MedicalScannerMachineCircuitboard + parent: BaseMachineCircuitboard + name: Medical Scanner (Machine Board) + components: + - type: MachineBoard + prototype: MedicalScanner + requirements: + ScanningModule: 1 + Manipulator: 1 + Laser: 1 + materialRequirements: + Glass: 1 + Cable: 1 diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml index 71c81c55d0..6a4d77d8aa 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml @@ -92,6 +92,16 @@ - plasma - plasma_2 - plasma_3 + - type: SolutionContainer + caps: 1 + contents: + reagents: + - ReagentId: Plasma + Quantity: 10 + - type: Tag + tags: + - Grindable + - Sheet - type: entity parent: SheetPlasma diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base.yml index 96eee35691..c570660647 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base.yml @@ -38,16 +38,16 @@ - SmallImpassable - type: Door board: DoorElectronics + openSound: + path: /Audio/Machines/airlock_open.ogg + closeSound: + path: /Audio/Machines/airlock_close.ogg + denySound: + path: /Audio/Machines/airlock_deny.ogg - type: Airlock - type: Appearance visuals: - type: AirlockVisualizer - open_sound: - path: /Audio/Machines/airlock_open.ogg - close_sound: - path: /Audio/Machines/airlock_close.ogg - deny_sound: - path: /Audio/Machines/airlock_deny.ogg - type: WiresVisualizer - type: ApcPowerReceiver - type: Wires diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml index 8d3fc73512..6435ab2a95 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml @@ -2,19 +2,19 @@ parent: Airlock id: AirlockExternal suffix: External - description: "It opens, it closes, it might crush you, and there might be only space behind it.\nHas to be manually activated." + description: It opens, it closes, it might crush you, and there might be only space behind it. Has to be manually activated. components: - type: Door bumpOpen: false + openSound: + path: /Audio/Machines/airlock_ext_open.ogg + closeSound: + path: /Audio/Machines/airlock_ext_close.ogg + denySound: + path: /Audio/Machines/airlock_deny.ogg - type: Sprite sprite: Structures/Doors/Airlocks/Standard/external.rsi - type: Appearance visuals: - type: AirlockVisualizer - open_sound: - path: /Audio/Machines/airlock_ext_open.ogg - close_sound: - path: /Audio/Machines/airlock_ext_close.ogg - deny_sound: - path: /Audio/Machines/airlock_deny.ogg - type: WiresVisualizer diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml index e8f8e3e43a..bba134dd1b 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml @@ -54,17 +54,17 @@ startOpen: true bumpOpen: false inhibitCrush: false + openSound: + path: /Audio/Machines/airlock_open.ogg + closeSound: + path: /Audio/Machines/airlock_close.ogg + denySound: + path: /Audio/Machines/airlock_deny.ogg - type: Firelock - type: Appearance visuals: - type: AirlockVisualizer - open_sound: - path: /Audio/Machines/airlock_open.ogg - close_sound: - path: /Audio/Machines/airlock_close.ogg - deny_sound: - path: /Audio/Machines/airlock_deny.ogg - animation_time: 0.6 + animationTime: 0.6 - type: WiresVisualizer - type: Wires BoardName: "Firelock Control" @@ -113,7 +113,7 @@ fixtures: - shape: !type:PhysShapeRect - bounds: "0.49,-0.49,-0.49,-0.2" # don't want this colliding with walls or they won't close + bounds: "-0.2,-0.49,-0.49,0.49" # don't want this colliding with walls or they won't close mask: - MobImpassable layer: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml new file mode 100644 index 0000000000..be3701c135 --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml @@ -0,0 +1,76 @@ +- type: entity + id: WindoorAssembly + name: windoor assembly + description: It opens, it closes, and you can see through it! + parent: BaseStructure + components: + - type: InteractionOutline + - type: Sprite + netsync: false + drawdepth: FloorObjects + sprite: Structures/Doors/Windoors/windoor.rsi + layers: + - state: assembly + - type: Physics + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.2,-0.49,-0.49,0.49" + mass: 30 + mask: + - Impassable + - VaultImpassable + - type: Anchorable + - type: Pullable + - type: Rotatable + - type: Damageable + resistances: metallicResistances + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 3 + - type: Construction + graph: windoor + node: assembly + placement: + mode: SnapgridCenter + +- type: entity + id: WindoorAssemblySecure + name: secure windoor assembly + description: It opens, it closes, and you can see through it! This one looks tough. + parent: WindoorAssembly + components: + - type: Sprite + netsync: false + drawdepth: Mobs + sprite: Structures/Doors/Windoors/windoor.rsi + layers: + - state: secure_underlay + - state: assembly + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 600 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:SpawnEntitiesBehavior + spawn: + SheetPlasteel1: + min: 1 + max: 2 + - type: Construction + graph: windoor + node: assemblySecure + diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base.yml new file mode 100644 index 0000000000..cc078a321b --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base.yml @@ -0,0 +1,135 @@ +- type: entity + id: BaseWindoor + parent: BaseStructure + abstract: true + placement: + mode: SnapgridCenter + components: + - type: InteractionOutline + - type: Physics + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.2,-0.49,-0.49,0.49" + mass: 50 + layer: + - Impassable + - MobImpassable + - VaultImpassable + - SmallImpassable + mask: + - VaultImpassable + - type: Sprite + netsync: false + drawdepth: FloorObjects + sprite: Structures/Doors/Windoors/windoor.rsi + layers: + - state: closed + map: ["enum.DoorVisualLayers.Base"] + - state: closed_unlit + shader: unshaded + map: ["enum.DoorVisualLayers.BaseUnlit"] + - state: welded + map: ["enum.DoorVisualLayers.BaseWelded"] + - state: bolted_unlit + shader: unshaded + map: ["enum.DoorVisualLayers.BaseBolted"] + - state: panel_open + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: ApcPowerReceiver + - type: Damageable + resistances: glassResistances + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 200 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 2 + SheetSteel1: + min: 2 + max: 4 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: AccessReader + - type: Airlock + openPanelVisible: true + - type: Door + weldable: false + openSound: + path: /Audio/Machines/windoor_open.ogg + closeSound: + path: /Audio/Machines/windoor_open.ogg + denySound: + path: /Audio/Machines/airlock_deny.ogg + - type: Wires + BoardName: "Windoor Control" + LayoutId: Airlock + - type: UserInterface + interfaces: + - key: enum.WiresUiKey.Key + type: WiresBoundUserInterface + - type: Appearance + visuals: + - type: AirlockVisualizer + animationTime: 0.9 + denyAnimationTime: 0.4 + animatedPanel: false + openUnlitVisible: true + - type: WiresVisualizer + - type: Construction + graph: windoor + node: windoor + +- type: entity + id: BaseSecureWindoor + parent: BaseWindoor + abstract: true + components: + - type: Airtight + fixVacuum: true + noAirWhenFullyAirBlocked: false + airBlockedDirection: + - South + - type: Sprite + netsync: false + drawdepth: FloorObjects + sprite: Structures/Doors/Windoors/windoor.rsi + layers: + - state: secure_underlay + - state: closed + map: [ "enum.DoorVisualLayers.Base" ] + - state: closed_unlit + shader: unshaded + map: [ "enum.DoorVisualLayers.BaseUnlit" ] + - state: welded + map: [ "enum.DoorVisualLayers.BaseWelded" ] + - state: bolted_unlit + shader: unshaded + map: [ "enum.DoorVisualLayers.BaseBolted" ] + - state: panel_open + map: [ "enum.WiresVisualLayers.MaintenancePanel" ] + visible: false + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 400 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 2 + SheetPlasteel1: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: Construction + graph: windoor + node: windoorSecure diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/windoor.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/windoor.yml new file mode 100644 index 0000000000..57fa6f18a9 --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/windoor.yml @@ -0,0 +1,66 @@ +- type: entity + id: Windoor + parent: BaseWindoor + name: windoor + description: It's a window and a sliding door. Wow! + +- type: entity + id: WindoorSecure + parent: BaseSecureWindoor + name: secure windoor + description: It's a sturdy window and a sliding door. Wow! + +# TODO remove these with parameterized prototypes/whatever we end up doing +# Bar windoor +- type: entity + parent: Windoor + id: WindoorBarLocked + suffix: Bar, Locked + components: + - type: AccessReader + access: [["Bar"]] + +# Chemistry windoor +- type: entity + parent: WindoorSecure + id: WindoorMedicalLocked + suffix: Medical, Locked + components: + - type: AccessReader + access: [["Medical"]] + +# Science windoor +- type: entity + parent: WindoorSecure + id: WindoorScienceLocked + suffix: Science, Locked + components: + - type: AccessReader + access: [["Research"]] + +# HOP's office windoor +- type: entity + parent: WindoorSecure + id: WindoorCommandLocked + suffix: Command, Locked + components: + - type: AccessReader + access: [["Command"]] + +# Cargo windoor +- type: entity + parent: Windoor + id: WindoorCargoLocked + suffix: Cargo, Locked + components: + - type: AccessReader + access: [["Cargo"]] + +# Security windoor +- type: entity + parent: WindoorSecure + id: WindoorSecurityLocked + suffix: Security, Locked + components: + - type: AccessReader + access: [["Security"]] diff --git a/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml b/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml index 95154b771d..82d21f7b82 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml @@ -23,6 +23,25 @@ - Opaque mask: - MobMask + - type: Construction + graph: machine + node: machine + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Machine + board: CloningPodMachineCircuitboard + - type: MaterialStorage + - type: Wires + BoardName: "CloningPod" + LayoutId: CloningPod - type: Appearance visuals: - type: CloningPodVisualizer diff --git a/Resources/Prototypes/Entities/Structures/Machines/gravity_generator.yml b/Resources/Prototypes/Entities/Structures/Machines/gravity_generator.yml index bfbeb05fb5..f06d0cdae4 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/gravity_generator.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/gravity_generator.yml @@ -25,6 +25,9 @@ - shape: !type:PhysShapeAabb bounds: "-1.5,-1.5,1.5,1.5" + mass: 500 + mask: + - Impassable layer: - Opaque - Impassable diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index c43572e7f4..a98e8d0138 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -166,6 +166,8 @@ - FirelockElectronics - DoorElectronics - APCElectronics + - CloningPodMachineCircuitboard + - MedicalScannerMachineCircuitboard - Bucket - MopItem - SprayBottle diff --git a/Resources/Prototypes/Entities/Structures/Machines/medical_scanner.yml b/Resources/Prototypes/Entities/Structures/Machines/medical_scanner.yml index f83802bdba..facc87125d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/medical_scanner.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/medical_scanner.yml @@ -14,6 +14,25 @@ map: ["enum.MedicalScannerVisualLayers.Machine"] - state: idle_unlit map: ["enum.MedicalScannerVisualLayers.Terminal"] + - type: Construction + graph: machine + node: machine + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Machine + board: MedicalScannerMachineCircuitboard + - type: MaterialStorage + - type: Wires + BoardName: "MedicalScanner" + LayoutId: MedicalScanner - type: Appearance visuals: - type: MedicalScannerVisualizer diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml index f7d23a40a4..c62a227933 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml @@ -33,11 +33,14 @@ state: pipeStraight map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: pumpPressure + map: [ "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer", "enum.PressurePumpVisualizer+Layers.Enabled" ] - type: Appearance visuals: + - type: SubFloorShowLayerVisualizer - type: PipeConnectorVisualizer - type: PipeColorVisualizer - type: PressurePumpVisualizer + disabledState: pumpPressure enabledState: pumpPressureOn - type: GasPressurePump @@ -57,6 +60,12 @@ state: pipeStraight map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: pumpVolume + map: [ "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] + - type: Appearance + visuals: + - type: SubFloorShowLayerVisualizer + - type: PipeConnectorVisualizer + - type: PipeColorVisualizer - type: GasVolumePump - type: entity @@ -75,6 +84,12 @@ state: pipeStraight map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: pumpPassiveGate + map: [ "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] + - type: Appearance + visuals: + - type: SubFloorShowLayerVisualizer + - type: PipeConnectorVisualizer + - type: PipeColorVisualizer - type: GasPassiveGate - type: entity @@ -94,6 +109,12 @@ state: pipeStraight map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: pumpPassiveGate + map: [ "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] + - type: Appearance + visuals: + - type: SubFloorShowLayerVisualizer + - type: PipeConnectorVisualizer + - type: PipeColorVisualizer - type: GasValve - type: NodeContainer nodes: @@ -118,6 +139,12 @@ state: pipeHalf map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: gasCanisterPort + map: [ "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] + - type: Appearance + visuals: + - type: SubFloorShowLayerVisualizer + - type: PipeConnectorVisualizer + - type: PipeColorVisualizer - type: GasPort - type: NodeContainer nodes: @@ -142,9 +169,10 @@ state: pipeStraight map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: vent_off - map: ["enum.VentVisualLayers.Vent"] + map: [ "enum.VentVisualLayers.Vent", "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] - type: Appearance visuals: + - type: SubFloorShowLayerVisualizer - type: PipeConnectorVisualizer - type: PipeColorVisualizer - type: VentPumpVisualizer diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml index df3534b3d3..ed58aecde5 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml @@ -33,6 +33,7 @@ - type: NodeContainer - type: AtmosUnsafeUnanchor - type: AtmosPipeColor + - type: SubFloorHide #Note: The PipeDirection of the PipeNode should be the south-facing version, because the entity starts at an angle of 0 (south) diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml index 916ba7302e..55b541dd2a 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml @@ -38,6 +38,15 @@ state: pipeTJunction map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: gasFilter + map: [ "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer", "enum.GasFilterVisualizer+Layers.Enabled" ] + - type: Appearance + visuals: + - type: SubFloorShowLayerVisualizer + - type: PipeConnectorVisualizer + - type: PipeColorVisualizer + - type: GasFilterVisualizer + disabledState: gasFilter + enabledState: gasFilterOn - type: GasFilter - type: entity @@ -57,6 +66,12 @@ state: pipeTJunction map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: gasFilter + map: [ "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] + - type: Appearance + visuals: + - type: SubFloorShowLayerVisualizer + - type: PipeConnectorVisualizer + - type: PipeColorVisualizer - type: GasMixer inletOne: inlet inletTwo: filter diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml index 14be2b9c3a..f628e58595 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/unary.yml @@ -29,9 +29,10 @@ state: pipeHalf map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: vent_off - map: ["enum.VentVisualLayers.Vent"] + map: [ "enum.VentVisualLayers.Vent", "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] - type: Appearance visuals: + - type: SubFloorShowLayerVisualizer - type: PipeConnectorVisualizer - type: PipeColorVisualizer - type: VentPumpVisualizer @@ -54,8 +55,10 @@ state: pipeHalf map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: vent_off + map: [ "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] - type: Appearance visuals: + - type: SubFloorShowLayerVisualizer - type: PipeConnectorVisualizer - type: PipeColorVisualizer - type: GasPassiveVent @@ -76,9 +79,10 @@ state: pipeHalf map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - state: scrub_off - map: ["enum.ScrubberVisualLayers.Scrubber"] + map: [ "enum.ScrubberVisualLayers.Scrubber", "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] - type: Appearance visuals: + - type: SubFloorShowLayerVisualizer - type: PipeConnectorVisualizer - type: PipeColorVisualizer - type: ScrubberVisualizer @@ -93,18 +97,24 @@ placement: mode: SnapgridCenter components: - - type: GasOutletInjector + # TODO ATMOS: Actual sprite for this. - type: Sprite netsync: false layers: - state: pipeHalf sprite: Structures/Piping/Atmospherics/pipe.rsi - map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] + map: [ "enum.PipeColorVisualizer+Layers.Pipe", "enum.SubFloorShowLayerVisualizer+Layers.FirstLayer" ] + - type: Appearance + visuals: + - type: SubFloorShowLayerVisualizer + - type: PipeConnectorVisualizer + - type: PipeColorVisualizer + - type: GasOutletInjector - type: entity - parent: GasUnaryBase - id: GasThermoMachineBase + parent: BaseMachinePowered + id: BaseGasThermoMachine name: thermomachine description: Heats or cools gas in connected pipes. abstract: true @@ -117,10 +127,19 @@ - type: Appearance visuals: - type: PipeConnectorVisualizer + - type: PipeColorVisualizer - type: GasThermoMachine + - type: AtmosPipeColor + - type: AtmosDevice + - type: NodeContainer + nodes: + pipe: + !type:PipeNode + nodeGroupID: Pipe + pipeDirection: South - type: entity - parent: GasThermoMachineBase + parent: BaseGasThermoMachine id: GasThermoMachineFreezer name: freezer placement: @@ -129,18 +148,22 @@ - type: Sprite layers: - state: freezer_off + map: [ "enum.ThermoMachineVisualizer+Layers.Enabled" ] - state: pipe + map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - type: Appearance visuals: - type: PipeConnectorVisualizer + - type: PipeColorVisualizer - type: ThermoMachineVisualizer + disabledState: freezer_off enabledState: freezer_on - type: GasThermoMachine mode: Freezer minTemperature: 73.15 - type: entity - parent: GasThermoMachineBase + parent: BaseGasThermoMachine id: GasThermoMachineHeater name: heater placement: @@ -149,11 +172,15 @@ - type: Sprite layers: - state: heater_off + map: [ "enum.ThermoMachineVisualizer+Layers.Enabled" ] - state: pipe + map: [ "enum.PipeColorVisualizer+Layers.Pipe" ] - type: Appearance visuals: - type: PipeConnectorVisualizer + - type: PipeColorVisualizer - type: ThermoMachineVisualizer + disabledState: heater_off enabledState: heater_on - type: GasThermoMachine mode: Heater diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/containment.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/containment.yml index d0654ead03..38ccc87a25 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/containment.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/containment.yml @@ -1,7 +1,7 @@ - type: entity id: ContainmentFieldGenerator name: containment field generator - description: A machine that generates a containment field when powered by an emitter.\nKeeps the Singularity docile. + description: A machine that generates a containment field when powered by an emitter. Keeps the Singularity docile. placement: mode: SnapgridCenter components: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml index 3f6629d99f..bcc1dd509f 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml @@ -1,7 +1,7 @@ - type: entity id: Singularity name: gravitational singularity - description: A mesmerizing swirl of darkness that sucks in everything.\nIf it's moving towards you, run. + description: A mesmerizing swirl of darkness that sucks in everything. If it's moving towards you, run. components: - type: Clickable - type: Physics diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/base.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/base.yml deleted file mode 100644 index b0d71322f7..0000000000 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/base.yml +++ /dev/null @@ -1,73 +0,0 @@ -- type: entity - abstract: true - id: GasCanister - name: gas canister - description: A canister that can contain any type of gas. It can be attached to connector ports using a wrench. - parent: BaseStructureDynamic - components: - - type: InteractionOutline - - type: Sprite - netsync: false - sprite: Structures/Storage/canister.rsi - state: grey - - type: Appearance - visuals: - - type: GasPortableVisualizer - stateConnected: can-connector - - type: GasCanisterVisualizer - insertedTankState: can-open - pressureStates: - - can-o0 - - can-o1 - - can-o2 - - can-o3 - - type: UserInterface - interfaces: - - key: enum.GasCanisterUiKey.Key - type: GasCanisterBoundUserInterface - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 300 - behaviors: - - !type:PlaySoundBehavior - sound: - path: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - GasCanisterBrokenBase: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] - - type: Damageable - resistances: metallicResistances - - type: Physics - bodyType: Dynamic - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.25,0.25,0.25" - mass: 25 - mask: - - MobImpassable - layer: - - Opaque - - MobImpassable - - SmallImpassable - - VaultImpassable - - type: AtmosDevice - requireAnchored: false - - type: ContainerContainer - containers: - GasCanisterTankHolder: !type:ContainerSlot {} - - type: NodeContainer - nodes: - port: - !type:PortablePipeNode - nodeGroupID: Pipe - rotationsEnabled: false - volume: 1 - - type: GasPortable - - type: GasCanister diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index 6420621d67..37eda3fc53 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -1,3 +1,77 @@ +- type: entity + abstract: true + id: GasCanister + name: gas canister + description: A canister that can contain any type of gas. It can be attached to connector ports using a wrench. + parent: BaseStructureDynamic + components: + - type: InteractionOutline + - type: Sprite + netsync: false + sprite: Structures/Storage/canister.rsi + state: grey + - type: Appearance + visuals: + - type: GasPortableVisualizer + stateConnected: can-connector + - type: GasCanisterVisualizer + insertedTankState: can-open + pressureStates: + - can-o0 + - can-o1 + - can-o2 + - can-o3 + - type: UserInterface + interfaces: + - key: enum.GasCanisterUiKey.Key + type: GasCanisterBoundUserInterface + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + GasCanisterBrokenBase: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: Damageable + resistances: metallicResistances + - type: Physics + bodyType: Dynamic + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + mass: 25 + mask: + - MobImpassable + layer: + - Opaque + - MobImpassable + - SmallImpassable + - VaultImpassable + - type: AtmosDevice + requireAnchored: false + joinSystem: true + - type: ContainerContainer + containers: + GasCanisterTankHolder: !type:ContainerSlot {} + - type: NodeContainer + nodes: + port: + !type:PortablePipeNode + nodeGroupID: Pipe + rotationsEnabled: false + volume: 1 + - type: GasPortable + - type: GasCanister + - type: entity parent: GasCanister id: StorageCanister diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/closets.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/closets.yml index ad7a284db0..41c491a005 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/closets.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/closets.yml @@ -103,3 +103,16 @@ visuals: - type: StorageVisualizer state: bio_jan + +# Maintenance closet +- type: entity + id: ClosetMaintenance + name: maintenance closet + parent: ClosetBase + description: It's a storage unit. + components: + - type: Appearance + visuals: + - type: StorageVisualizer + state_open: generic_open + state_closed: generic_door diff --git a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml index 4c6a1251f6..1dc59eae2a 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml @@ -56,6 +56,18 @@ name: morgue tray description: If you lay down to have a rest on this, you'll soon have a problem. components: + - type: Physics + bodyType: Static + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.5, -0.5, 0.5, 0.5" + mass: 25 + mask: + - Impassable + - MobImpassable + - VaultImpassable + - SmallImpassable - type: Sprite netsync: false sprite: Structures/Storage/morgue.rsi diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml index b31d59a19d..bff9a06605 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml @@ -183,9 +183,6 @@ desc: A combination of cream, wine and moonshine. Why would you do this? physicalDesc: foul color: "#a6969a" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: Lean @@ -193,9 +190,6 @@ desc: Turn up for days. physicalDesc: bubbly color: "#9400D3" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: LeanShine @@ -203,8 +197,3 @@ desc: Lean mixed with moonshine. Turn up for months. physicalDesc: bubbly color: "#9d5fb8" - metabolism: - - !type:DefaultDrink - rate: 1 - - diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml b/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml index b4c5009d35..baa8792dd2 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml @@ -4,9 +4,6 @@ desc: A drink made from brewed coffee beans. Contains a moderate amount of caffeine. physicalDesc: aromatic color: "#664300" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: Tea @@ -14,9 +11,6 @@ desc: A drink made by boiling leaves of the tea tree, Camellia sinensis. physicalDesc: aromatic color: "#8a5a3a" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: Cream @@ -24,9 +18,6 @@ desc: The fatty, still liquid part of milk. Why don't you mix this with sum scotch, eh? physicalDesc: creamy color: "#DFD7AF" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: Milk @@ -34,9 +25,6 @@ desc: An opaque white liquid produced by the mammary glands of mammals. physicalDesc: opaque color: "#DFDFDF" - metabolism: - - !type:DefaultDrink - rate: 1 plantMetabolism: - !type:AdjustNutrition amount: 0.1 @@ -49,9 +37,6 @@ desc: This milk has gone rancid. physicalDesc: putrid color: "#faffba" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: MilkSoy @@ -59,9 +44,6 @@ desc: Surprisingly tasty. physicalDesc: refreshing color: "#302000" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: MilkOat @@ -69,6 +51,3 @@ desc: Surprisingly tasty. physicalDesc: refreshing color: "#302000" - metabolism: - - !type:DefaultDrink - rate: 1 diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml b/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml index 44a75bbd1d..ac4a65a2f5 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/juice.yml @@ -4,9 +4,6 @@ desc: It's a little piece of Eden. physicalDesc: crisp color: "#FDAD01" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: JuiceBerry @@ -14,9 +11,6 @@ desc: A delicious blend of several different kinds of berries. physicalDesc: sweet color: "#660099" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: JuiceBanana @@ -24,9 +18,6 @@ desc: The raw essence of a banana. HONK. physicalDesc: crisp color: "#FFE777" - metabolism: - - !type:DefaultDrink - rate: 1 #TODO: port on_mob_life: restore eyesight #if(..()) @@ -45,9 +36,6 @@ desc: It's like a carrot, but less crunchy. physicalDesc: crisp color: "#FF8820" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: JuiceLime @@ -55,9 +43,6 @@ desc: The sweet-sour juice of limes. physicalDesc: citric color: "#99bb43" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: JuiceLemon @@ -65,9 +50,6 @@ desc: This juice is VERY sour. physicalDesc: citric color: "#fff690" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: JuiceGrape @@ -75,9 +57,6 @@ desc: Freshly squeezed juice from red grapes. Quite sweet. physicalDesc: crisp color: "#512284" - metabolism: - - !type:DefaultDrink - rate: 1 # /datum/reagent/drink/orangejuice/on_mob_life(var/mob/living/M) @@ -93,9 +72,6 @@ desc: Both delicious AND rich in Vitamin C. What more do you need? physicalDesc: citric color: "#E78108" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: JuiceTomato @@ -103,9 +79,6 @@ desc: Tomatoes made into juice. What a waste of good tomatoes, huh? physicalDesc: saucey color: "#731008" - metabolism: - - !type:DefaultDrink - rate: 1 # /datum/reagent/drink/poisonberryjuice/on_mob_life(var/mob/living/M) @@ -120,9 +93,6 @@ desc: A surprisingly tasty juice blended from various kinds of very deadly and toxic berries. physicalDesc: aromatic #maybe should be 'sickly'? color: "#6600CC" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: JuiceWatermelon @@ -130,9 +100,6 @@ desc: The delicious juice of a watermelon. physicalDesc: sweet color: "#EF3520" - metabolism: - - !type:DefaultDrink - rate: 1 - type: reagent id: JuicePineapple @@ -140,9 +107,6 @@ desc: The delicious juice of a pineapple. physicalDesc: tropical color: yellow - metabolism: - - !type:DefaultDrink - rate: 1 # - type: reagent # id: PotatoJuice @@ -150,6 +114,3 @@ # desc: Juice of the potato. Bleh. # physicalDesc: starchy # color: "#302000" -# metabolism: -# - !type:DefaultDrink -# rate: 1 diff --git a/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml b/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml index eb2b34041b..b28baa70f1 100644 --- a/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml +++ b/Resources/Prototypes/Reagents/Consumable/Drink/soda.yml @@ -4,9 +4,6 @@ desc: A sweet, carbonated soft drink. Caffeine free. physicalDesc: fizzy color: "#422912" - metabolism: - - !type:DefaultDrink - rate: 1 plantMetabolism: - !type:AdjustNutrition amount: 0.1 @@ -21,12 +18,6 @@ desc: A highly processed liquid substance barely-passing intergalatic health standarts for a soft drink. physicalDesc: fizzy color: "#deb928" - metabolism: - - !type:DefaultDrink - rate: 1 - - !type:HealthChangeMetabolism - healthChange: 2 - damageClass: Toxin plantMetabolism: - !type:AdjustNutrition amount: 0.1 diff --git a/Resources/Prototypes/Reagents/Consumable/Food/condiments.yml b/Resources/Prototypes/Reagents/Consumable/Food/condiments.yml index 88183dbfb6..8cdcb5903a 100644 --- a/Resources/Prototypes/Reagents/Consumable/Food/condiments.yml +++ b/Resources/Prototypes/Reagents/Consumable/Food/condiments.yml @@ -4,9 +4,6 @@ desc: The sweetness of a thousand sugars but none of the calories. # physicalDesc: color: aquamarine - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: BbqSauce @@ -14,9 +11,6 @@ desc: Hand wipes not included. physicalDesc: Gloopy. color: darkred - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Cornoil @@ -24,9 +18,6 @@ desc: Corn oil, A delicious oil used in cooking. Made from corn. # physicalDesc: color: yellow - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Frostoil @@ -34,9 +25,6 @@ desc: Leaves the tongue numb in its passage. # physicalDesc: color: skyblue - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: HorseradishSauce @@ -44,9 +32,6 @@ desc: Smelly horseradish sauce. physicalDesc: Overpowering. color: gray - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Hotsauce @@ -54,9 +39,6 @@ desc: Burns so good. # physicalDesc: color: red - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Ketchup @@ -64,9 +46,6 @@ desc: Made from pureed tomatoes and flavored with spices. # physicalDesc: color: red - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Soysauce @@ -74,6 +53,3 @@ desc: A salty soy-based flavoring. # physicalDesc: color: saddlebrown - metabolism: - - !type:DefaultFood - rate: 1 diff --git a/Resources/Prototypes/Reagents/Consumable/Food/ingredients.yml b/Resources/Prototypes/Reagents/Consumable/Food/ingredients.yml index ee13427ec1..bfeed21bb5 100644 --- a/Resources/Prototypes/Reagents/Consumable/Food/ingredients.yml +++ b/Resources/Prototypes/Reagents/Consumable/Food/ingredients.yml @@ -4,9 +4,6 @@ desc: Used for baking. physicalDesc: powdery color: white - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Oats @@ -14,18 +11,12 @@ desc: Used for a variety of tasty purposes. physicalDesc: coarse color: tan - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Enzyme name: universal enzyme desc: Used in cooking various dishes. color: "#009900" - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Egg @@ -33,9 +24,6 @@ desc: Used for baking. physicalDesc: mucus-like color: white - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Sugar @@ -43,9 +31,6 @@ desc: Tasty spacey sugar! physicalDesc: color: white - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Salt @@ -53,9 +38,6 @@ desc: Salt. From space oceans, presumably. physicalDesc: Coarse. color: white - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Blackpepper @@ -63,33 +45,21 @@ desc: Often used to flavor food or make people sneeze. physicalDesc: Grainy. color: black - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Vinegar name: vinegar desc: Often used to flavor food. color: tan - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: Rice name: rice desc: Hard, small white grains. color: white - metabolism: - - !type:DefaultFood - rate: 1 - type: reagent id: OilOlive name: olive oil desc: Viscous and fragrant. color: olive - metabolism: - - !type:DefaultFood - rate: 1 diff --git a/Resources/Prototypes/Reagents/chemicals.yml b/Resources/Prototypes/Reagents/chemicals.yml index 3d6d49be64..fb5be33970 100644 --- a/Resources/Prototypes/Reagents/chemicals.yml +++ b/Resources/Prototypes/Reagents/chemicals.yml @@ -89,9 +89,6 @@ desc: All the vitamins, minerals, and carbohydrates the body needs in pure form. physicalDesc: opaque color: "#664330" - metabolism: - - !type:DefaultFood - rate: 1 plantMetabolism: - !type:AdjustNutrition amount: 1 @@ -180,7 +177,7 @@ amount: 10 - !type:AdjustHealth amount: -5 - + - type: reagent id: SulfuricAcid name: sulfuric acid @@ -218,9 +215,6 @@ color: "#c0e0ff20" boilingPoint: 100.0 meltingPoint: 0.0 - metabolism: - - !type:DefaultDrink - rate: 1 tileReactions: - !type:ExtinguishTileReaction {} - !type:SpillIfPuddlePresentTileReaction {} @@ -228,13 +222,40 @@ - !type:AdjustWater amount: 1 +- type: reagent + id: Meth + name: meth + desc: Methamphetamine, more commonly know as meth, is a potent stimulant, with dangerous side-effects if too much is consumed. + physicalDesc: translucent + color: "#FAFAFA" + boilingPoint: 212.0 #Meth vape when? + meltingPoint: 170.0 + +- type: reagent + id: Iodine + name: iodine + desc: Commonly added to table salt as a nutrient. On its own it tastes far less pleasing. + physicalDesc: Dark Brown + color: "#BC8A00" + boilingPoint: 184.3 + meltingPoint: 113.7 + +- type: reagent + id: Ephedrine + name: ephedrine + desc: Increases stun resistance and movement speed, giving you hand cramps. Overdose deals toxin damage and inhibits breathing + physicalDesc: Bone white + color: "#D2FFFA" + boilingPoint: 255.0 + meltingPoint: 36.0 + - type: reagent id: Oil name: oil desc: Used by chefs to cook. physicalDesc: oily color: "#b67823" - boilingPoint: 300.0 + boilingPoint: 300.0 meltingPoint: -16.0 tileReactions: - !type:FlammableTileReaction {} diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index 16dba85352..c49cc13300 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -18,10 +18,6 @@ desc: A broad-spectrum anti-toxin, which treats toxin damage in the blood stream. Overdosing will cause vomiting, dizzyness and pain. physicalDesc: translucent color: "#3a1d8a" - metabolism: - - !type:HealthChangeMetabolism - healthChange: -1 - damageClass: Toxin plantMetabolism: - !type:AdjustToxins amount: -10 @@ -34,13 +30,6 @@ desc: A slightly unstable medication used for the most extreme any serious case of radiation poisoning. Lowers radiation level at over twice the rate Hyronalin does and will heal toxin damage at the same time. Deals very minor brute damage to the patient over time, but the patient's body will typically out-regenerate it easily. physicalDesc: cloudy color: "#bd5902" - metabolism: - - !type:HealthChangeMetabolism - healthChange: -1 - damageClass: Toxin #I think you need multiple of these components for different types of damage so I'll maybe change it to work better in the future - - !type:HealthChangeMetabolism - healthChange: 0.5 - damageClass: Brute - type: reagent id: Bicaridine @@ -48,10 +37,6 @@ desc: An analgesic which is highly effective at treating brute damage. It is useful for stabilizing people who have been severely beaten, as well as treating less life-threatening injuries. In the case of bleeding (internal or external), bicaridine will slow down the bleeding heavily. If the dosage exceeds the overdose limit, it'll stop it outright. physicalDesc: opaque color: "#ffaa00" - metabolism: - - !type:HealthChangeMetabolism - healthChange: -2 - damageClass: Brute - type: reagent id: Cryoxadone @@ -90,10 +75,6 @@ desc: An advanced chemical that is more effective at treating burn damage than Kelotane. physicalDesc: translucent color: "#215263" - metabolism: - - !type:HealthChangeMetabolism - healthChange: -3 - damageClass: Burn - type: reagent id: Dexalin @@ -101,21 +82,13 @@ desc: Used for treating oxygen deprivation. In most cases where it is likely to be needed, the strength of Dexalin Plus will probably be more useful (Results in 1 unit instead of 2). physicalDesc: opaque color: "#0041a8" - metabolism: - - !type:HealthChangeMetabolism - healthChange: -1 - damageClass: Airloss #this may be asphyxiation - + - type: reagent id: DexalinPlus name: dexalin plus desc: Used in treatment of extreme cases of oxygen deprivation. Even a single unit immediately counters all oxygen loss, which is hugely useful in many circumstances. Any dose beyond this will continue to counter oxygen loss until it is metabolized, essentially removing the need to breathe. physicalDesc: cloudy color: "#4da0bd" - metabolism: - - !type:HealthChangeMetabolism - healthChange: -3 - damageClass: Airloss - type: reagent id: Ethylredoxrazine @@ -165,10 +138,6 @@ desc: Treats burn damage and prevents infection. physicalDesc: strong-smelling color: "#bf3d19" - metabolism: - - !type:HealthChangeMetabolism - healthChange: -1 - damageClass: Burn - type: reagent id: Leporazine @@ -201,7 +170,7 @@ - type: reagent id: Paroxetine name: paroxetine - desc: Prevents hallucination, but has a 10% chance of causing intense hallucinations. + desc: Prevents hallucination, but has a 10% chance of causing intense hallucinations. physicalDesc: acrid color: "#fffbad" @@ -225,11 +194,6 @@ desc: Toxic, but treats hallucinations, drowsiness & halves the duration of paralysis, stuns and knockdowns. It is metabolized very slowly. One unit is enough to treat hallucinations; two units is deadly. physicalDesc: pungent color: "#d49a2f" - metabolism: - - !type:HealthChangeMetabolism - healthChange: 0.67 - damageClass: Toxin - rate: 0.2 - type: reagent id: Tramadol @@ -289,10 +253,6 @@ plantMetabolism: - !type:AdjustToxins amount: 10 - metabolism: - - !type:HealthChangeMetabolism - healthChange: 1 - damageClass: Airloss - type: reagent id: Impedrezene @@ -307,10 +267,6 @@ desc: Temporarily stops respiration and causes tissue damage. Large doses are fatal, and will cause people to pass out very quickly. Dexalin and Dexalin Plus will both remove it, however. physicalDesc: pungent color: "#6b0007" - metabolism: - - !type:HealthChangeMetabolism - healthChange: 7 - damageClass: Airloss - type: reagent id: Lipozine @@ -356,24 +312,10 @@ desc: Pure THC oil, extracted from the leaves of the cannabis plant. Much stronger than in it's natural form and can be used to numb chronic pain in patients. physicalDesc: skunky color: "#DAA520" - + - type: reagent id: Omnizine name: Omnizine desc: A soothing milky liquid with an iridescent gleam. A well known conspiracy theory says that it's origins remain a mystery because knowing the secrets of its production would render most commercial pharmaceuticals obsolete. physicalDesc: soothing color: "#fcf7f9" - metabolism: - - !type:HealthChangeMetabolism - healthChange: -2 - damageClass: Burn - - !type:HealthChangeMetabolism - healthChange: -2 - damageClass: Toxin - - !type:HealthChangeMetabolism - healthChange: -2 - damageClass: Airloss - - !type:HealthChangeMetabolism - healthChange: -2 - damageClass: Brute - diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/windoor.yml b/Resources/Prototypes/Recipes/Construction/Graphs/windoor.yml new file mode 100644 index 0000000000..f947fdd414 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/windoor.yml @@ -0,0 +1,221 @@ +- type: constructionGraph + id: windoor + start: start + graph: + - node: start + edges: + - to: assembly + completed: + - !type:SetAnchor + value: false + steps: + - material: Steel + amount: 4 + doAfter: 2 + - to: assemblySecure + completed: + - !type:SetAnchor + value: false + steps: + - material: Plasteel + amount: 4 + doAfter: 2 + + - node: assembly + entity: WindoorAssembly + actions: + - !type:SnapToGrid {} + - !type:SetAnchor {} + edges: + - to: glass + conditions: + - !type:EntityAnchored {} + steps: + - material: Glass + amount: 5 + doAfter: 1 + - to: start + conditions: + - !type:EntityAnchored + anchored: false + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 4 + - !type:DeleteEntity {} + steps: + - tool: Welding + doAfter: 2 + + - node: glass + entity: WindoorAssembly + edges: + - to: wired + conditions: + - !type:EntityAnchored { } + steps: + - material: Cable + amount: 5 + doAfter: 1 + - to: assembly + conditions: + - !type:EntityAnchored + anchored: false + completed: + - !type:SpawnPrototype + prototype: SheetGlass1 + amount: 5 + - !type:DeleteEntity { } + steps: + - tool: Screwing + doAfter: 2 + + - node: wired + entity: WindoorAssembly + edges: + - to: electronics + conditions: + - !type:EntityAnchored {} + steps: + - tag: DoorElectronics + store: board + name: "door electronics circuit board" + icon: + sprite: "Objects/Misc/module.rsi" + state: "door_electronics" + doAfter: 1 + - to: glass + completed: + - !type:SpawnPrototype + prototype: CableApcStack1 + amount: 5 + steps: + - tool: Cutting + doAfter: 1 + + - node: electronics + entity: WindoorAssembly + edges: + - to: windoor + conditions: + - !type:EntityAnchored {} + steps: + - tool: Screwing + doAfter: 2 + + - node: windoor + entity: Windoor + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + - !type:AirlockBolted + value: false + - !type:WirePanel {} + - !type:ContainerNotEmpty # TODO ShadowCommander: Remove when map gets updated + container: board + completed: + - !type:EmptyAllContainers {} + steps: + - tool: Prying + doAfter: 1 + + - node: assemblySecure + entity: WindoorAssemblySecure + actions: + - !type:SnapToGrid { } + - !type:SetAnchor { } + edges: + - to: glassSecure + conditions: + - !type:EntityAnchored { } + steps: + - material: ReinforcedGlass + amount: 5 + doAfter: 1 + - to: start + conditions: + - !type:EntityAnchored + anchored: false + completed: + - !type:SpawnPrototype + prototype: SheetPlasteel1 + amount: 4 + - !type:DeleteEntity { } + steps: + - tool: Welding + doAfter: 10 + + - node: glassSecure + entity: WindoorAssemblySecure + edges: + - to: wiredSecure + conditions: + - !type:EntityAnchored { } + steps: + - material: Cable + amount: 5 + doAfter: 1 + - to: assemblySecure + conditions: + - !type:EntityAnchored + anchored: false + completed: + - !type:SpawnPrototype + prototype: SheetRGlass1 + amount: 5 + - !type:DeleteEntity { } + steps: + - tool: Screwing + doAfter: 4 + + + - node: wiredSecure + entity: WindoorAssemblySecure + edges: + - to: electronicsSecure + conditions: + - !type:EntityAnchored { } + steps: + - tag: DoorElectronics + store: board + name: "door electronics circuit board" + icon: + sprite: "Objects/Misc/module.rsi" + state: "door_electronics" + doAfter: 1 + - to: glassSecure + completed: + - !type:SpawnPrototype + prototype: CableApcStack1 + amount: 5 + steps: + - tool: Cutting + doAfter: 3 + + - node: electronicsSecure + entity: WindoorAssemblySecure + edges: + - to: windoorSecure + conditions: + - !type:EntityAnchored { } + steps: + - tool: Screwing + doAfter: 4 + + - node: windoorSecure + entity: WindoorSecure + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + - !type:AirlockBolted + value: false + - !type:WirePanel {} + - !type:ContainerNotEmpty # TODO ShadowCommander: Remove when map gets updated + container: board + completed: + - !type:EmptyAllContainers {} + steps: + - tool: Prying + doAfter: 4 diff --git a/Resources/Prototypes/Recipes/Construction/structures.yml b/Resources/Prototypes/Recipes/Construction/structures.yml index 2e52a35875..a9424d1aea 100644 --- a/Resources/Prototypes/Recipes/Construction/structures.yml +++ b/Resources/Prototypes/Recipes/Construction/structures.yml @@ -125,7 +125,7 @@ canRotate: false - type: construction - name: Firelock + name: firelock id: Firelock graph: Firelock startNode: start @@ -142,7 +142,7 @@ - !type:TileNotBlocked {} - type: construction - name: Catwalk + name: catwalk id: Catwalk graph: Catwalk startNode: start @@ -164,7 +164,7 @@ canBuildInImpassable: false - type: construction - name: Wooden Barricade + name: wooden barricade id: Barricade graph: barricade startNode: start @@ -181,7 +181,7 @@ - !type:TileNotBlocked {} - type: construction - name: Airlock + name: airlock id: airlock graph: airlock startNode: start @@ -196,3 +196,37 @@ canBuildInImpassable: false conditions: - !type:TileNotBlocked {} + +- type: construction + name: windoor + id: windoor + graph: windoor + startNode: start + targetNode: windoor + category: Structures + description: It opens, it closes, and you can see through it! + icon: + sprite: Structures/Doors/Windoors/windoor.rsi + state: closed + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked {} + +- type: construction + name: secure windoor + id: secureWindoor + graph: windoor + startNode: start + targetNode: windoorSecure + category: Structures + description: It opens, it closes, and you can see through it! This one looks tough. + icon: + sprite: Structures/Doors/Windoors/windoor.rsi + state: closed + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked {} diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index 69b7503055..2462d1a6dd 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -24,3 +24,21 @@ materials: Steel: 50 Glass: 50 + +- type: latheRecipe + id: CloningPodMachineCircuitboard + icon: Objects/Misc/module.rsi/id_mod.png + result: CloningPodMachineCircuitboard + completetime: 1000 + materials: + Steel: 100 + Glass: 100 + +- type: latheRecipe + id: MedicalScannerMachineCircuitboard + icon: Objects/Misc/module.rsi/id_mod.png + result: MedicalScannerMachineCircuitboard + completetime: 1000 + materials: + Steel: 100 + Glass: 100 diff --git a/Resources/Prototypes/Recipes/Reactions/chemicals.yml b/Resources/Prototypes/Recipes/Reactions/chemicals.yml index e6a4204143..687d9267d3 100644 --- a/Resources/Prototypes/Recipes/Reactions/chemicals.yml +++ b/Resources/Prototypes/Recipes/Reactions/chemicals.yml @@ -227,3 +227,31 @@ amount: 1 products: Fluorosurfactant: 5 + +- type: reaction + id: Meth + reactants: + Ephedrine: + amount: 1 + Carbon: + amount: 1 + Iodine: + amount: 1 + Phosphorus: + amount: 1 + products: + Meth: 4 #I kinda remember having to heat this up, and if you heated it up too much, it went boom, I can't remember the specific values tho. + +- type: reaction + id: Ephedrine + reactants: + Oil: + amount: 1 + Hydrogen: + amount: 1 + Sugar: + amount: 1 + Diethylamine: + amount: 1 + products: + Ephedrine: 4 diff --git a/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/equipped-HELMET.png new file mode 100644 index 0000000000..8e0362d8dd Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/equipped-HELMET.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/icon.png b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/icon.png new file mode 100644 index 0000000000..95a2ca01f5 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/inhand-left.png new file mode 100644 index 0000000000..7e2ef801dc Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/inhand-right.png b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/inhand-right.png new file mode 100644 index 0000000000..db26e66334 Binary files /dev/null and b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/meta.json b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/meta.json new file mode 100644 index 0000000000..48deea96a8 --- /dev/null +++ b/Resources/Textures/Clothing/Head/Hats/truckershat.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from baystation12 and modified by Swept at commit https://github.com/Baystation12/Baystation12/commit/80b2ded014aa871e6bceb6913b2961a23109aa23", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/equipped-INNERCLOTHING.png new file mode 100644 index 0000000000..42ea220dba Binary files /dev/null and b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/equipped-INNERCLOTHING.png differ diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/icon.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/icon.png new file mode 100644 index 0000000000..6f2f21e14b Binary files /dev/null and b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/inhand-left.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/inhand-left.png new file mode 100644 index 0000000000..ef474f95ad Binary files /dev/null and b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/inhand-left.png differ diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/inhand-right.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/inhand-right.png new file mode 100644 index 0000000000..79468f3b60 Binary files /dev/null and b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/inhand-right.png differ diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/meta.json b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/meta.json new file mode 100644 index 0000000000..c35ec06fb5 --- /dev/null +++ b/Resources/Textures/Clothing/Uniforms/Jumpsuit/overalls.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation and modified by Swept at commit https://github.com/tgstation/tgstation/commit/ac792e3226d48e5b3aaccff165ce100f7636a040", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-INNERCLOTHING", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/assembly.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/assembly.png new file mode 100644 index 0000000000..668997d837 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/assembly.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/bolted_unlit.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/bolted_unlit.png new file mode 100644 index 0000000000..2ca98a410d Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/bolted_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closed.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closed.png new file mode 100644 index 0000000000..fe1f75ba13 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closed.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closed_unlit.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closed_unlit.png new file mode 100644 index 0000000000..435a5cb044 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closed_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closing.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closing.png new file mode 100644 index 0000000000..e7085665d7 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closing.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closing_unlit.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closing_unlit.png new file mode 100644 index 0000000000..a7265ce6ec Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/closing_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/deny_unlit.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/deny_unlit.png new file mode 100644 index 0000000000..aface6a2f6 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/deny_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/meta.json b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/meta.json new file mode 100644 index 0000000000..9a8f157fc8 --- /dev/null +++ b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/meta.json @@ -0,0 +1,168 @@ +{ + "version":1, + "license":"CC-BY-SA-3.0", + "copyright":"Taken from tgstation at https://github.com/tgstation/tgstation/blob/3681006d7102045e334e8eddb23a8685fcdb258a/icons/obj/doors/windoor.dmi", + "size": { + "x":32, + "y":32 + }, + "states":[ + { + "name":"assembly", + "directions":4, + "delays":[ + [ + 0.3,0.3 + ], + [ + 0.3,0.3 + ], + [ + 0.3,0.3 + ], + [ + 0.3,0.3 + ] + ] + }, + { + "name":"closed", + "directions":4 + }, + { + "name":"closed_unlit", + "directions":4 + }, + { + "name":"closing", + "directions":4, + "delays":[ + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ] + ] + }, + { + "name":"closing_unlit", + "directions":4, + "delays":[ + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1] + ] + }, + { + "name":"open", + "directions":4 + }, + { + "name":"open_unlit", + "directions":4 + }, + { + "name":"opening", + "directions":4, + "delays":[ + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ] + ] + }, + { + "name":"opening_unlit", + "directions":4, + "delays":[ + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1,0.1 + ] + ] + }, + { + "name":"deny_unlit", + "directions":4, + "delays":[ + [ + 0.1,0.2,0.1 + ], + [ + 0.1,0.2,0.1 + ], + [ + 0.1,0.2,0.1 + ], + [ + 0.1,0.2,0.1 + ] + ] + }, + { + "name":"spark", + "directions":4, + "delays":[ + [ + 0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1 + ], + [ + 0.1,0.1,0.1,0.1,0.1,0.1 + ] + ] + }, + { + "name":"panel_open", + "directions":4 + }, + { + "name":"bolted_unlit", + "directions":4 + }, + { + "name":"welded", + "directions":4 + }, + { + "name":"secure_underlay", + "directions":4 + } + ] +} diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/open.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/open.png new file mode 100644 index 0000000000..2089729a6a Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/open.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/open_unlit.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/open_unlit.png new file mode 100644 index 0000000000..ddcc55de0a Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/open_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/opening.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/opening.png new file mode 100644 index 0000000000..5b68943ff6 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/opening.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/opening_unlit.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/opening_unlit.png new file mode 100644 index 0000000000..9a3072db8a Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/opening_unlit.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/panel_open.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/panel_open.png new file mode 100644 index 0000000000..21bbb4dedf Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/panel_open.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/secure_underlay.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/secure_underlay.png new file mode 100644 index 0000000000..e574e9a12c Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/secure_underlay.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/spark.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/spark.png new file mode 100644 index 0000000000..a139b5d3c3 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/spark.png differ diff --git a/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/welded.png b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/welded.png new file mode 100644 index 0000000000..2975c479be Binary files /dev/null and b/Resources/Textures/Structures/Doors/Windoors/windoor.rsi/welded.png differ diff --git a/Resources/Textures/Structures/Piping/Atmospherics/gasfilter.rsi/gasFilterOn.png b/Resources/Textures/Structures/Piping/Atmospherics/gasfilter.rsi/gasFilterOn.png index 891eface87..06cdacf03e 100644 Binary files a/Resources/Textures/Structures/Piping/Atmospherics/gasfilter.rsi/gasFilterOn.png and b/Resources/Textures/Structures/Piping/Atmospherics/gasfilter.rsi/gasFilterOn.png differ diff --git a/RobustToolbox b/RobustToolbox index 9397cc4a6b..d58e380dd9 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 9397cc4a6b91c04087540319de58d469d9ceb42e +Subproject commit d58e380dd9dfbcc32316f0064193fdd9d61ef7a5