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