--- /dev/null
+using Content.Client.Construction;
+using Content.Shared.Atmos.Components;
+using Content.Shared.Atmos.EntitySystems;
+using Content.Shared.Construction.Prototypes;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Client.Placement;
+using Robust.Client.Placement.Modes;
+using Robust.Client.Utility;
+using Robust.Shared.Enums;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+using System.Numerics;
+using static Robust.Client.Placement.PlacementManager;
+
+namespace Content.Client.Atmos;
+
+/// <summary>
+/// Allows users to place atmos pipes on different layers depending on how the mouse cursor is positioned within a grid tile.
+/// </summary>
+/// <remarks>
+/// This placement mode is not on the engine because it is content specific.
+/// </remarks>
+public sealed class AlignAtmosPipeLayers : SnapgridCenter
+{
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+ [Dependency] private readonly IMapManager _mapManager = default!;
+ [Dependency] private readonly IEyeManager _eyeManager = default!;
+
+ private readonly SharedMapSystem _mapSystem;
+ private readonly SharedTransformSystem _transformSystem;
+ private readonly SharedAtmosPipeLayersSystem _pipeLayersSystem;
+ private readonly SpriteSystem _spriteSystem;
+
+ private const float SearchBoxSize = 2f;
+ private EntityCoordinates _unalignedMouseCoords = default;
+ private const float MouseDeadzoneRadius = 0.25f;
+
+ private Color _guideColor = new Color(0, 0, 0.5785f);
+ private const float GuideRadius = 0.1f;
+ private const float GuideOffset = 0.21875f;
+
+ public AlignAtmosPipeLayers(PlacementManager pMan) : base(pMan)
+ {
+ IoCManager.InjectDependencies(this);
+
+ _mapSystem = _entityManager.System<SharedMapSystem>();
+ _transformSystem = _entityManager.System<SharedTransformSystem>();
+ _pipeLayersSystem = _entityManager.System<SharedAtmosPipeLayersSystem>();
+ _spriteSystem = _entityManager.System<SpriteSystem>();
+ }
+
+ /// <inheritdoc/>
+ public override void Render(in OverlayDrawArgs args)
+ {
+ var gridUid = _entityManager.System<SharedTransformSystem>().GetGrid(MouseCoords);
+
+ if (gridUid == null || Grid == null)
+ return;
+
+ // Draw guide circles for each pipe layer if we are not in line/grid placing mode
+ if (pManager.PlacementType == PlacementTypes.None)
+ {
+ var gridRotation = _transformSystem.GetWorldRotation(gridUid.Value);
+ var worldPosition = _mapSystem.LocalToWorld(gridUid.Value, Grid, MouseCoords.Position);
+ var direction = (_eyeManager.CurrentEye.Rotation + gridRotation + Math.PI / 2).GetCardinalDir();
+ var multi = (direction == Direction.North || direction == Direction.South) ? -1f : 1f;
+
+ args.WorldHandle.DrawCircle(worldPosition, GuideRadius, _guideColor);
+ args.WorldHandle.DrawCircle(worldPosition + gridRotation.RotateVec(new Vector2(multi * GuideOffset, GuideOffset)), GuideRadius, _guideColor);
+ args.WorldHandle.DrawCircle(worldPosition - gridRotation.RotateVec(new Vector2(multi * GuideOffset, GuideOffset)), GuideRadius, _guideColor);
+ }
+
+ base.Render(args);
+ }
+
+ /// <inheritdoc/>
+ public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
+ {
+ _unalignedMouseCoords = ScreenToCursorGrid(mouseScreen);
+ base.AlignPlacementMode(mouseScreen);
+
+ // Exit early if we are in line/grid placing mode
+ if (pManager.PlacementType != PlacementTypes.None)
+ return;
+
+ MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager);
+
+ var gridId = _transformSystem.GetGrid(MouseCoords);
+
+ if (!_entityManager.TryGetComponent<MapGridComponent>(gridId, out var mapGrid))
+ return;
+
+ var gridRotation = _transformSystem.GetWorldRotation(gridId.Value);
+ CurrentTile = _mapSystem.GetTileRef(gridId.Value, mapGrid, MouseCoords);
+
+ float tileSize = mapGrid.TileSize;
+ GridDistancing = tileSize;
+
+ MouseCoords = new EntityCoordinates(MouseCoords.EntityId, new Vector2(CurrentTile.X + tileSize / 2 + pManager.PlacementOffset.X,
+ CurrentTile.Y + tileSize / 2 + pManager.PlacementOffset.Y));
+
+ // Calculate the position of the mouse cursor with respect to the center of the tile to determine which layer to use
+ var mouseCoordsDiff = _unalignedMouseCoords.Position - MouseCoords.Position;
+ var layer = AtmosPipeLayer.Primary;
+
+ if (mouseCoordsDiff.Length() > MouseDeadzoneRadius)
+ {
+ // Determine the direction of the mouse is relative to the center of the tile, adjusting for the player eye and grid rotation
+ var direction = (new Angle(mouseCoordsDiff) + _eyeManager.CurrentEye.Rotation + gridRotation + Math.PI / 2).GetCardinalDir();
+ layer = (direction == Direction.North || direction == Direction.East) ? AtmosPipeLayer.Secondary : AtmosPipeLayer.Tertiary;
+ }
+
+ // Update the construction menu placer
+ if (pManager.Hijack != null)
+ UpdateHijackedPlacer(layer, mouseScreen);
+
+ // Otherwise update the debug placer
+ else
+ UpdatePlacer(layer);
+ }
+
+ private void UpdateHijackedPlacer(AtmosPipeLayer layer, ScreenCoordinates mouseScreen)
+ {
+ // Try to get alternative prototypes from the construction prototype
+ var constructionSystem = (pManager.Hijack as ConstructionPlacementHijack)?.CurrentConstructionSystem;
+ var altPrototypes = (pManager.Hijack as ConstructionPlacementHijack)?.CurrentPrototype?.AlternativePrototypes;
+
+ if (constructionSystem == null || altPrototypes == null || (int)layer >= altPrototypes.Length)
+ return;
+
+ var newProtoId = altPrototypes[(int)layer];
+
+ if (!_protoManager.TryIndex(newProtoId, out var newProto))
+ return;
+
+ if (newProto.Type != ConstructionType.Structure)
+ {
+ pManager.Clear();
+ return;
+ }
+
+ if (newProto.ID == (pManager.Hijack as ConstructionPlacementHijack)?.CurrentPrototype?.ID)
+ return;
+
+ // Start placing
+ pManager.BeginPlacing(new PlacementInformation()
+ {
+ IsTile = false,
+ PlacementOption = newProto.PlacementMode,
+ }, new ConstructionPlacementHijack(constructionSystem, newProto));
+
+ if (pManager.CurrentMode is AlignAtmosPipeLayers { } newMode)
+ newMode.RefreshGrid(mouseScreen);
+
+ // Update construction guide
+ constructionSystem.GetGuide(newProto);
+ }
+
+ private void UpdatePlacer(AtmosPipeLayer layer)
+ {
+ // Try to get alternative prototypes from the entity atmos pipe layer component
+ if (pManager.CurrentPermission?.EntityType == null)
+ return;
+
+ if (!_protoManager.TryIndex<EntityPrototype>(pManager.CurrentPermission.EntityType, out var currentProto))
+ return;
+
+ if (!currentProto.TryGetComponent<AtmosPipeLayersComponent>(out var atmosPipeLayers, _entityManager.ComponentFactory))
+ return;
+
+ if (!_pipeLayersSystem.TryGetAlternativePrototype(atmosPipeLayers, layer, out var newProtoId))
+ return;
+
+ if (_protoManager.TryIndex<EntityPrototype>(newProtoId, out var newProto))
+ {
+ // Update the placed prototype
+ pManager.CurrentPermission.EntityType = newProtoId;
+
+ // Update the appearance of the ghost sprite
+ if (newProto.TryGetComponent<SpriteComponent>(out var sprite, _entityManager.ComponentFactory))
+ {
+ var textures = new List<IDirectionalTextureProvider>();
+
+ foreach (var spriteLayer in sprite.AllLayers)
+ {
+ if (spriteLayer.ActualRsi?.Path != null && spriteLayer.RsiState.Name != null)
+ textures.Add(_spriteSystem.RsiStateLike(new SpriteSpecifier.Rsi(spriteLayer.ActualRsi.Path, spriteLayer.RsiState.Name)));
+ }
+
+ pManager.CurrentTextures = textures;
+ }
+ }
+ }
+
+ private void RefreshGrid(ScreenCoordinates mouseScreen)
+ {
+ base.AlignPlacementMode(mouseScreen);
+ }
+}
public int? FocusNetId = null;
private const int ChunkSize = 4;
+ private const float ScaleModifier = 4f;
+
+ private readonly float[] _layerFraction = { 0.5f, 0.75f, 0.25f };
+ private const float LineThickness = 0.05f;
private readonly Color _basePipeNetColor = Color.LightGray;
private readonly Color _unfocusedPipeNetColor = Color.DimGray;
foreach (var chunkedLine in atmosPipeNetwork)
{
var leftTop = ScalePosition(new Vector2
- (Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
- Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
+ (Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - LineThickness,
+ Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - LineThickness)
- offset);
var rightTop = ScalePosition(new Vector2
- (Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
- Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
+ (Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + LineThickness,
+ Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - LineThickness)
- offset);
var leftBottom = ScalePosition(new Vector2
- (Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
- Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
+ (Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - LineThickness,
+ Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + LineThickness)
- offset);
var rightBottom = ScalePosition(new Vector2
- (Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
- Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
+ (Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + LineThickness,
+ Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + LineThickness)
- offset);
if (!pipeVertexUVs.TryGetValue(chunkedLine.Color, out var pipeVertexUV))
if (chunks == null || grid == null)
return decodedOutput;
- // Clear stale look up table values
+ // Clear stale look up table values
_horizLines.Clear();
_horizLinesReversed.Clear();
_vertLines.Clear();
{
var list = new List<AtmosMonitoringConsoleLine>();
- foreach (var ((netId, hexColor), atmosPipeData) in chunk.AtmosPipeData)
+ foreach (var ((netId, layer, hexColor), atmosPipeData) in chunk.AtmosPipeData)
{
// Determine the correct coloration for the pipe
var color = Color.FromHex(hexColor) * _basePipeNetColor;
_vertLinesReversed[color] = vertLinesReversed;
}
+ var layerFraction = _layerFraction[(int)layer];
+ var origin = new Vector2(grid.TileSize * layerFraction, -grid.TileSize * layerFraction);
+
// Loop over the chunk
for (var tileIdx = 0; tileIdx < ChunkSize * ChunkSize; tileIdx++)
{
// Calculate the draw point offsets
var vertLineOrigin = (atmosPipeData & northMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
- new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 1f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
+ new Vector2(grid.TileSize * layerFraction, -grid.TileSize * 1f) : origin;
var vertLineTerminus = (atmosPipeData & southMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
- new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
+ new Vector2(grid.TileSize * layerFraction, -grid.TileSize * 0f) : origin;
var horizLineOrigin = (atmosPipeData & eastMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
- new Vector2(grid.TileSize * 1f, -grid.TileSize * 0.5f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
+ new Vector2(grid.TileSize * 1f, -grid.TileSize * layerFraction) : origin;
var horizLineTerminus = (atmosPipeData & westMask << tileIdx * SharedNavMapSystem.Directions) > 0 ?
- new Vector2(grid.TileSize * 0f, -grid.TileSize * 0.5f) : new Vector2(grid.TileSize * 0.5f, -grid.TileSize * 0.5f);
+ new Vector2(grid.TileSize * 0f, -grid.TileSize * layerFraction) : origin;
- // Since we can have pipe lines that have a length of a half tile,
- // double the vectors and convert to vector2i so we can merge them
- AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + horizLineOrigin, 2), ConvertVector2ToVector2i(tile + horizLineTerminus, 2), horizLines, horizLinesReversed);
- AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + vertLineOrigin, 2), ConvertVector2ToVector2i(tile + vertLineTerminus, 2), vertLines, vertLinesReversed);
+ // Scale up the vectors and convert to vector2i so we can merge them
+ AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + horizLineOrigin, ScaleModifier),
+ ConvertVector2ToVector2i(tile + horizLineTerminus, ScaleModifier), horizLines, horizLinesReversed);
+ AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + vertLineOrigin, ScaleModifier),
+ ConvertVector2ToVector2i(tile + vertLineTerminus, ScaleModifier), vertLines, vertLinesReversed);
}
}
}
foreach (var (origin, terminal) in horizLines)
decodedOutput.Add(new AtmosMonitoringConsoleLine
- (ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB));
+ (ConvertVector2iToVector2(origin, 1f / ScaleModifier), ConvertVector2iToVector2(terminal, 1f / ScaleModifier), sRGB));
}
foreach (var (color, vertLines) in _vertLines)
foreach (var (origin, terminal) in vertLines)
decodedOutput.Add(new AtmosMonitoringConsoleLine
- (ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB));
+ (ConvertVector2iToVector2(origin, 1f / ScaleModifier), ConvertVector2iToVector2(terminal, 1f / ScaleModifier), sRGB));
}
return decodedOutput;
private void OnHandleState(EntityUid uid, AtmosMonitoringConsoleComponent component, ref ComponentHandleState args)
{
- Dictionary<Vector2i, Dictionary<(int, string), ulong>> modifiedChunks;
+ Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> modifiedChunks;
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices;
switch (args.Current)
foreach (var (origin, chunk) in modifiedChunks)
{
var newChunk = new AtmosPipeChunk(origin);
- newChunk.AtmosPipeData = new Dictionary<(int, string), ulong>(chunk);
+ newChunk.AtmosPipeData = new Dictionary<AtmosMonitoringConsoleSubnet, ulong>(chunk);
component.AtmosPipeChunks[origin] = newChunk;
}
using Robust.Shared.Utility;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Numerics;
namespace Content.Client.Atmos.Consoles;
private ProtoId<NavMapBlipPrototype> _navMapConsoleProtoId = "NavMapConsole";
private ProtoId<NavMapBlipPrototype> _gasPipeSensorProtoId = "GasPipeSensor";
+ private readonly Vector2[] _pipeLayerOffsets = { new Vector2(0f, 0f), new Vector2(0.25f, 0.25f), new Vector2(-0.25f, -0.25f) };
+
public AtmosMonitoringConsoleWindow(AtmosMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner)
{
RobustXamlLoader.Load(this);
consoleCoords = xform.Coordinates;
NavMap.MapUid = xform.GridUid;
- // Assign station name
+ // Assign station name
if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
stationName = stationMetaData.EntityName;
var blinks = proto.Blinks || _focusEntity == metaData.NetEntity;
var coords = _entManager.GetCoordinates(metaData.NetCoordinates);
+
+ if (proto.Placement == NavMapBlipPlacement.Offset && metaData.PipeLayer > 0)
+ coords = coords.Offset(_pipeLayerOffsets[(int)metaData.PipeLayer]);
+
var blip = new NavMapBlip(coords, _spriteSystem.Frame0(new SpriteSpecifier.Texture(texture)), color, blinks, proto.Selectable, proto.Scale);
NavMap.TrackedEntities[metaData.NetEntity] = blip;
}
using Content.Client.SubFloor;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
+using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Piping;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Atmos.EntitySystems;
[UsedImplicitly]
-public sealed class AtmosPipeAppearanceSystem : EntitySystem
+public sealed partial class AtmosPipeAppearanceSystem : SharedAtmosPipeAppearanceSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
if (!TryComp(uid, out SpriteComponent? sprite))
return;
+ var numberOfPipeLayers = GetNumberOfPipeLayers(uid, out _);
+
foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>())
{
- var layer = _sprite.LayerMapReserve((uid, sprite), layerKey);
- _sprite.LayerSetRsi((uid, sprite), layer, component.Sprite.RsiPath);
- _sprite.LayerSetRsiState((uid, sprite), layer, component.Sprite.RsiState);
- _sprite.LayerSetDirOffset((uid, sprite), layer, ToOffset(layerKey));
+ for (byte i = 0; i < numberOfPipeLayers; i++)
+ {
+ var layerName = layerKey.ToString() + i.ToString();
+ var layer = _sprite.LayerMapReserve((uid, sprite), layerName);
+ _sprite.LayerSetRsi((uid, sprite), layer, component.Sprite[i].RsiPath);
+ _sprite.LayerSetRsiState((uid, sprite), layer, component.Sprite[i].RsiState);
+ _sprite.LayerSetDirOffset((uid, sprite), layer, ToOffset(layerKey));
+ }
}
}
- private void HideAllPipeConnection(Entity<SpriteComponent> entity)
+ private void HideAllPipeConnection(Entity<SpriteComponent> entity, AtmosPipeLayersComponent? atmosPipeLayers, int numberOfPipeLayers)
{
var sprite = entity.Comp;
foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>())
{
- if (!_sprite.LayerMapTryGet(entity.AsNullable(), layerKey, out var key, false))
- continue;
+ for (byte i = 0; i < numberOfPipeLayers; i++)
+ {
+ var layerName = layerKey.ToString() + i.ToString();
+
+ if (!_sprite.LayerMapTryGet(entity.AsNullable(), layerName, out var key, false))
+ continue;
- var layer = sprite[key];
- layer.Visible = false;
+ var layer = sprite[key];
+ layer.Visible = false;
+ }
}
}
return;
}
- if (!_appearance.TryGetData<PipeDirection>(uid, PipeVisuals.VisualState, out var worldConnectedDirections, args.Component))
+ var numberOfPipeLayers = GetNumberOfPipeLayers(uid, out var atmosPipeLayers);
+
+ if (!_appearance.TryGetData<int>(uid, PipeVisuals.VisualState, out var worldConnectedDirections, args.Component))
{
- HideAllPipeConnection((uid, args.Sprite));
+ HideAllPipeConnection((uid, args.Sprite), atmosPipeLayers, numberOfPipeLayers);
return;
}
if (!_appearance.TryGetData<Color>(uid, PipeColorVisuals.Color, out var color, args.Component))
color = Color.White;
- // transform connected directions to local-coordinates
- var connectedDirections = worldConnectedDirections.RotatePipeDirection(-Transform(uid).LocalRotation);
-
- foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>())
+ for (byte i = 0; i < numberOfPipeLayers; i++)
{
- if (!_sprite.LayerMapTryGet((uid, args.Sprite), layerKey, out var key, false))
- continue;
+ // Extract the cardinal pipe orientations for the current pipe layer
+ // '15' is the four bit mask that is used to extract the pipe orientations of interest from 'worldConnectedDirections'
+ // Fun fact: a collection of four bits is called a 'nibble'! They aren't natively supported :(
+ var pipeLayerConnectedDirections = (PipeDirection)(15 & (worldConnectedDirections >> (PipeDirectionHelpers.PipeDirections * i)));
+
+ // Transform the connected directions to local-coordinates
+ var connectedDirections = pipeLayerConnectedDirections.RotatePipeDirection(-Transform(uid).LocalRotation);
+
+ foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>())
+ {
+ var layerName = layerKey.ToString() + i.ToString();
+
+ if (!_sprite.LayerMapTryGet((uid, args.Sprite), layerName, out var key, false))
+ continue;
- var layer = args.Sprite[key];
- var dir = (PipeDirection)layerKey;
- var visible = connectedDirections.HasDirection(dir);
+ var layer = args.Sprite[key];
+ var dir = (PipeDirection)layerKey;
+ var visible = connectedDirections.HasDirection(dir);
- layer.Visible &= visible;
+ layer.Visible &= visible;
- if (!visible)
- continue;
+ if (!visible)
+ continue;
- layer.Color = color;
+ layer.Color = color;
+ }
}
}
--- /dev/null
+using Content.Shared.Atmos.Components;
+using Content.Shared.Atmos.EntitySystems;
+using Robust.Client.GameObjects;
+using Robust.Client.ResourceManagement;
+using Robust.Shared.Reflection;
+using Robust.Shared.Serialization.TypeSerializers.Implementations;
+using Robust.Shared.Utility;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Content.Client.Atmos.EntitySystems;
+
+/// <summary>
+/// The system responsible for updating the appearance of layered gas pipe
+/// </summary>
+public sealed partial class AtmosPipeLayersSystem : SharedAtmosPipeLayersSystem
+{
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly IReflectionManager _reflection = default!;
+ [Dependency] private readonly IResourceCache _resourceCache = default!;
+ [Dependency] private readonly SpriteSystem _sprite = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<AtmosPipeLayersComponent, AppearanceChangeEvent>(OnAppearanceChange);
+ }
+
+ private void OnAppearanceChange(Entity<AtmosPipeLayersComponent> ent, ref AppearanceChangeEvent ev)
+ {
+ if (!TryComp<SpriteComponent>(ent, out var sprite))
+ return;
+
+ if (_appearance.TryGetData<string>(ent, AtmosPipeLayerVisuals.Sprite, out var spriteRsi) &&
+ _resourceCache.TryGetResource(SpriteSpecifierSerializer.TextureRoot / spriteRsi, out RSIResource? resource))
+ {
+ _sprite.SetBaseRsi((ent, sprite), resource.RSI);
+ }
+
+ if (_appearance.TryGetData<Dictionary<string, string>>(ent, AtmosPipeLayerVisuals.SpriteLayers, out var pipeState))
+ {
+ foreach (var (layerKey, rsiPath) in pipeState)
+ {
+ if (TryParseKey(layerKey, out var @enum))
+ _sprite.LayerSetRsi((ent, sprite), @enum, new ResPath(rsiPath));
+ else
+ _sprite.LayerSetRsi((ent, sprite), layerKey, new ResPath(rsiPath));
+ }
+ }
+ }
+
+ private bool TryParseKey(string keyString, [NotNullWhen(true)] out Enum? @enum)
+ {
+ return _reflection.TryParseEnumReference(keyString, out @enum);
+ }
+}
else
{
// oh shit of fuck its more than 4 this ui isn't gonna look pretty anymore
+ CDeviceMixes.RemoveAllChildren();
for (var i = 1; i < msg.NodeGasMixes.Length; i++)
{
GenerateGasDisplay(msg.NodeGasMixes[i], CDeviceMixes);
-using System.Linq;
+using System.Linq;
using Content.Shared.Construction.Prototypes;
using Robust.Client.GameObjects;
using Robust.Client.Placement;
private readonly ConstructionSystem _constructionSystem;
private readonly ConstructionPrototype? _prototype;
+ public ConstructionSystem? CurrentConstructionSystem { get { return _constructionSystem; } }
+ public ConstructionPrototype? CurrentPrototype { get { return _prototype; } }
+
public override bool CanRotate { get; }
public ConstructionPlacementHijack(ConstructionSystem constructionSystem, ConstructionPrototype? prototype)
return false;
var direction = xform.LocalRotation.GetCardinalDir();
+ var netId = TryGettingFirstPipeNode(uid, out var _, out var firstNetId) ? firstNetId : -1;
+ var color = TryComp<AtmosPipeColorComponent>(uid, out var atmosPipeColor) ? atmosPipeColor.Color : Color.White;
+ var layer = TryComp<AtmosPipeLayersComponent>(uid, out var atmosPipeLayers) ? atmosPipeLayers.CurrentPipeLayer : AtmosPipeLayer.Primary;
- if (!TryGettingFirstPipeNode(uid, out var _, out var netId))
- netId = -1;
-
- var color = Color.White;
-
- if (TryComp<AtmosPipeColorComponent>(uid, out var atmosPipeColor))
- color = atmosPipeColor.Color;
-
- device = new AtmosDeviceNavMapData(GetNetEntity(uid), GetNetCoordinates(xform.Coordinates), netId.Value, component.NavMapBlip.Value, direction, color);
+ device = new AtmosDeviceNavMapData(GetNetEntity(uid), GetNetCoordinates(xform.Coordinates), netId.Value, component.NavMapBlip.Value, direction, color, layer);
return true;
}
if (!TryComp<NodeContainerComponent>(ent, out var entNodeContainer))
continue;
- UpdateAtmosPipeChunk(ent, entNodeContainer, entAtmosPipeColor, tileIdx, ref chunk);
+ var showAbsentConnections = TryComp<AtmosMonitoringConsoleDeviceComponent>(ent, out var device) ? device.ShowAbsentConnections : true;
+
+ UpdateAtmosPipeChunk(ent, entNodeContainer, entAtmosPipeColor, tileIdx, ref chunk, showAbsentConnections);
}
// Add or update the chunk on the associated grid
}
}
- private void UpdateAtmosPipeChunk(EntityUid uid, NodeContainerComponent nodeContainer, AtmosPipeColorComponent pipeColor, int tileIdx, ref AtmosPipeChunk chunk)
+ private void UpdateAtmosPipeChunk
+ (EntityUid uid,
+ NodeContainerComponent nodeContainer,
+ AtmosPipeColorComponent pipeColor,
+ int tileIdx,
+ ref AtmosPipeChunk chunk,
+ bool showAbsentConnections = true)
{
// Entities that are actively being deleted are not to be drawn
if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
foreach ((var id, var node) in nodeContainer.Nodes)
{
- if (node is not PipeNode)
+ if (node is not PipeNode { } pipeNode)
+ continue;
+
+ if (!showAbsentConnections && !pipeNode.ReachableNodes.Any(x => x.Owner != uid))
continue;
- var pipeNode = (PipeNode)node;
var netId = GetPipeNodeNetId(pipeNode);
+ var subnet = new AtmosMonitoringConsoleSubnet(netId, pipeNode.CurrentPipeLayer, pipeColor.Color.ToHex());
var pipeDirection = pipeNode.CurrentPipeDirection;
- chunk.AtmosPipeData.TryGetValue((netId, pipeColor.Color.ToHex()), out var atmosPipeData);
+ chunk.AtmosPipeData.TryGetValue(subnet, out var atmosPipeData);
atmosPipeData |= (ulong)pipeDirection << tileIdx * SharedNavMapSystem.Directions;
- chunk.AtmosPipeData[(netId, pipeColor.Color.ToHex())] = atmosPipeData;
+ chunk.AtmosPipeData[subnet] = atmosPipeData;
}
}
--- /dev/null
+using Content.Server.Atmos.Components;
+using Content.Server.NodeContainer.EntitySystems;
+using Content.Server.NodeContainer.NodeGroups;
+using Content.Server.NodeContainer.Nodes;
+using Content.Shared.Atmos.Components;
+using Content.Shared.Atmos.EntitySystems;
+using Content.Shared.Construction.Components;
+using Content.Shared.NodeContainer;
+using Content.Shared.Popups;
+
+namespace Content.Server.Atmos.EntitySystems;
+
+/// <summary>
+/// The system responsible for checking and adjusting the connection layering of gas pipes
+/// </summary>
+public sealed partial class AtmosPipeLayersSystem : SharedAtmosPipeLayersSystem
+{
+ [Dependency] private readonly NodeGroupSystem _nodeGroup = default!;
+ [Dependency] private readonly PipeRestrictOverlapSystem _pipeRestrictOverlap = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedTransformSystem _xform = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<AtmosPipeLayersComponent, ComponentInit>(OnInit);
+ }
+
+ private void OnInit(Entity<AtmosPipeLayersComponent> ent, ref ComponentInit args)
+ {
+ SetPipeLayer(ent, ent.Comp.CurrentPipeLayer);
+ }
+
+ /// <inheritdoc/>
+ public override void SetPipeLayer(Entity<AtmosPipeLayersComponent> ent, AtmosPipeLayer layer, EntityUid? user = null, EntityUid? used = null)
+ {
+ if (ent.Comp.PipeLayersLocked)
+ return;
+
+ base.SetPipeLayer(ent, layer);
+
+ if (!TryComp<NodeContainerComponent>(ent, out var nodeContainer))
+ return;
+
+ // Update the layer values of all pipe nodes associated with the entity
+ foreach (var (id, node) in nodeContainer.Nodes)
+ {
+ if (node is not PipeNode { } pipeNode)
+ continue;
+
+ if (pipeNode.CurrentPipeLayer == ent.Comp.CurrentPipeLayer)
+ continue;
+
+ pipeNode.CurrentPipeLayer = ent.Comp.CurrentPipeLayer;
+
+ if (pipeNode.NodeGroup != null)
+ _nodeGroup.QueueRemakeGroup((BaseNodeGroup)pipeNode.NodeGroup);
+ }
+
+ // If a user wasn't responsible for unanchoring the pipe, leave it be
+ if (user == null || used == null)
+ return;
+
+ // Unanchor the pipe if its new layer overlaps with another pipe
+ var xform = Transform(ent);
+
+ if (!HasComp<PipeRestrictOverlapComponent>(ent) || !_pipeRestrictOverlap.CheckOverlap((ent, nodeContainer, xform)))
+ return;
+
+ RaiseLocalEvent(ent, new BeforeUnanchoredEvent(user.Value, used.Value));
+ _xform.Unanchor(ent, xform);
+ RaiseLocalEvent(ent, new UserUnanchoredEvent(user.Value, used.Value));
+
+ _popup.PopupEntity(Loc.GetString("pipe-restrict-overlap-popup-blocked", ("pipe", ent)), ent, user.Value);
+ }
+}
using Content.Server.NodeContainer.Nodes;
using Content.Server.Popups;
using Content.Shared.Atmos;
+using Content.Shared.Atmos.Components;
using Content.Shared.Construction.Components;
using Content.Shared.NodeContainer;
using JetBrains.Annotations;
public bool PipeNodesOverlap(Entity<NodeContainerComponent, TransformComponent> ent, Entity<NodeContainerComponent, TransformComponent> other)
{
- var entDirs = GetAllDirections(ent).ToList();
- var otherDirs = GetAllDirections(other).ToList();
+ var entDirsAndLayers = GetAllDirectionsAndLayers(ent).ToList();
+ var otherDirsAndLayers = GetAllDirectionsAndLayers(other).ToList();
- foreach (var dir in entDirs)
+ foreach (var (dir, layer) in entDirsAndLayers)
{
- foreach (var otherDir in otherDirs)
+ foreach (var (otherDir, otherLayer) in otherDirsAndLayers)
{
- if ((dir & otherDir) != 0)
+ if ((dir & otherDir) != 0 && layer == otherLayer)
return true;
}
}
return false;
- IEnumerable<PipeDirection> GetAllDirections(Entity<NodeContainerComponent, TransformComponent> pipe)
+ IEnumerable<(PipeDirection, AtmosPipeLayer)> GetAllDirectionsAndLayers(Entity<NodeContainerComponent, TransformComponent> pipe)
{
foreach (var node in pipe.Comp1.Nodes.Values)
{
// we need to rotate the pipe manually like this because the rotation doesn't update for pipes that are unanchored.
if (node is PipeNode pipeNode)
- yield return pipeNode.OriginalPipeDirection.RotatePipeDirection(pipe.Comp2.LocalRotation);
+ yield return (pipeNode.OriginalPipeDirection.RotatePipeDirection(pipe.Comp2.LocalRotation), pipeNode.CurrentPipeLayer);
}
}
}
--- /dev/null
+namespace Content.Server.Atmos.Piping.Components;
+
+[RegisterComponent]
+public sealed partial class GasPipeManifoldComponent : Component
+{
+ [DataField("inlets")]
+ public HashSet<string> InletNames { get; set; } = new() { "south0", "south1", "south2" };
+
+ [DataField("outlets")]
+ public HashSet<string> OutletNames { get; set; } = new() { "north0", "north1", "north2" };
+}
-using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
+using Content.Shared.Atmos.EntitySystems;
using Content.Shared.NodeContainer;
using Robust.Shared.Map.Components;
namespace Content.Server.Atmos.Piping.EntitySystems;
-public sealed class AtmosPipeAppearanceSystem : EntitySystem
+public sealed partial class AtmosPipeAppearanceSystem : SharedAtmosPipeAppearanceSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedMapSystem _map = default!;
if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
return;
+ var numberOfPipeLayers = GetNumberOfPipeLayers(uid, out var atmosPipeLayers);
+
// get connected entities
var anyPipeNodes = false;
- HashSet<EntityUid> connected = new();
+ HashSet<(EntityUid, AtmosPipeLayer)> connected = new();
+
foreach (var node in container.Nodes.Values)
{
if (node is not PipeNode)
foreach (var connectedNode in node.ReachableNodes)
{
- if (connectedNode is PipeNode)
- connected.Add(connectedNode.Owner);
+ if (connectedNode is PipeNode { } pipeNode)
+ connected.Add((connectedNode.Owner, pipeNode.CurrentPipeLayer));
}
}
return;
// find the cardinal directions of any connected entities
- var netConnectedDirections = PipeDirection.None;
- var tile = _map.TileIndicesFor((xform.GridUid.Value, grid), xform.Coordinates);
- foreach (var neighbour in connected)
+ var connectedDirections = new PipeDirection[numberOfPipeLayers];
+ Array.Fill(connectedDirections, PipeDirection.None);
+
+ var tile = _map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates);
+
+ foreach (var (neighbour, pipeLayer) in connected)
{
- // TODO z-levels, pipes across grids - we shouldn't assume that the neighboring tile's transform is on the same grid
- var otherTile = _map.TileIndicesFor((xform.GridUid.Value, grid), Transform(neighbour).Coordinates);
+ var pipeIndex = (int)pipeLayer;
+
+ if (pipeIndex >= numberOfPipeLayers)
+ continue;
+
+ var otherTile = _map.TileIndicesFor(xform.GridUid.Value, grid, Transform(neighbour).Coordinates);
+ var pipeLayerDirections = connectedDirections[pipeIndex];
- netConnectedDirections |= (otherTile - tile) switch
+ pipeLayerDirections |= (otherTile - tile) switch
{
(0, 1) => PipeDirection.North,
(0, -1) => PipeDirection.South,
(-1, 0) => PipeDirection.West,
_ => PipeDirection.None
};
+
+ connectedDirections[pipeIndex] = pipeLayerDirections;
}
+ // Convert the pipe direction array into a single int for serialization
+ var netConnectedDirections = 0;
+
+ for (var i = numberOfPipeLayers - 1; i >= 0; i--)
+ netConnectedDirections += (int)connectedDirections[i] << (PipeDirectionHelpers.PipeDirections * i);
+
_appearance.SetData(uid, PipeVisuals.VisualState, netConnectedDirections, appearance);
}
}
--- /dev/null
+using Content.Server.Atmos.EntitySystems;
+using Content.Server.Atmos.Piping.Components;
+using Content.Server.NodeContainer.EntitySystems;
+using Content.Server.NodeContainer.Nodes;
+using Content.Shared.Atmos;
+using Content.Shared.NodeContainer;
+using System.Linq;
+
+namespace Content.Server.Atmos.Piping.EntitySystems;
+
+public sealed partial class GasPipeManifoldSystem : EntitySystem
+{
+ [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<GasPipeManifoldComponent, ComponentInit>(OnCompInit);
+ SubscribeLocalEvent<GasPipeManifoldComponent, GasAnalyzerScanEvent>(OnAnalyzed);
+ }
+
+ private void OnCompInit(Entity<GasPipeManifoldComponent> ent, ref ComponentInit args)
+ {
+ if (!TryComp<NodeContainerComponent>(ent, out var nodeContainer))
+ return;
+
+ foreach (var inletName in ent.Comp.InletNames)
+ {
+ if (!_nodeContainer.TryGetNode(nodeContainer, inletName, out PipeNode? inlet))
+ continue;
+
+ foreach (var outletName in ent.Comp.OutletNames)
+ {
+ if (!_nodeContainer.TryGetNode(nodeContainer, outletName, out PipeNode? outlet))
+ continue;
+
+ inlet.AddAlwaysReachable(outlet);
+ outlet.AddAlwaysReachable(inlet);
+ }
+ }
+ }
+
+ private void OnAnalyzed(Entity<GasPipeManifoldComponent> ent, ref GasAnalyzerScanEvent args)
+ {
+ // All inlets and outlets have the same gas mixture
+
+ args.GasMixtures = new List<(string, GasMixture?)>();
+
+ if (!TryComp<NodeContainerComponent>(ent, out var nodeContainer))
+ return;
+
+ var pipeNames = ent.Comp.InletNames.Union(ent.Comp.OutletNames);
+
+ foreach (var pipeName in pipeNames)
+ {
+ if (!_nodeContainer.TryGetNode(nodeContainer, pipeName, out PipeNode? pipe))
+ continue;
+
+ var pipeLocal = pipe.Air.Clone();
+ pipeLocal.Multiply(pipe.Volume / pipe.Air.Volume);
+ pipeLocal.Volume = pipe.Volume;
+
+ args.GasMixtures.Add((Name(ent), pipeLocal));
+ break;
+ }
+ }
+}
-using Content.Server.Atmos;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos;
+using Content.Shared.Atmos.Components;
using Content.Shared.NodeContainer;
-using Content.Shared.NodeContainer.NodeGroups;
-using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
{
/// <summary>
/// Connects with other <see cref="PipeNode"/>s whose <see cref="PipeDirection"/>
- /// correctly correspond.
+ /// and <see cref="CurrentPipeLayer"/> correctly correspond.
/// </summary>
[DataDefinition]
[Virtual]
[DataField("pipeDirection")]
public PipeDirection OriginalPipeDirection;
+ /// <summary>
+ /// The *current* layer to which the pipe node is assigned.
+ /// </summary>
+ [DataField("pipeLayer")]
+ public AtmosPipeLayer CurrentPipeLayer = AtmosPipeLayer.Primary;
+
/// <summary>
/// The *current* pipe directions (accounting for rotation)
/// Used to check if this pipe can connect to another pipe in a given direction.
foreach (var pipe in PipesInDirection(pos, pipeDir, grid, nodeQuery))
{
if (pipe.NodeGroupID == NodeGroupID
+ && pipe.CurrentPipeLayer == CurrentPipeLayer
&& pipe.CurrentPipeDirection.HasDirection(pipeDir.GetOpposite()))
{
yield return pipe;
--- /dev/null
+using Content.Shared.Atmos.EntitySystems;
+using Content.Shared.DoAfter;
+using Content.Shared.Tools;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Atmos.Components;
+
+/// <summary>
+/// Contains layer data for atmos pipes. Layers allow multiple atmos pipes with the
+/// same orientation to be anchored to the same tile without their contents mixing.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedAtmosPipeLayersSystem))]
+public sealed partial class AtmosPipeLayersComponent : Component
+{
+ /// <summary>
+ /// The number of pipe layers this entity supports.
+ /// Must be equal to or less than the number of values
+ /// in <see cref="AtmosPipeLayer"/>.
+ /// </summary>
+ [DataField]
+ public byte NumberOfPipeLayers = 3;
+
+ /// <summary>
+ /// Determines which layer the pipe is currently assigned.
+ /// Only pipes on the same layer can connect with each other.
+ /// </summary>
+ [DataField("pipeLayer"), AutoNetworkedField]
+ [ViewVariables(VVAccess.ReadOnly)]
+ public AtmosPipeLayer CurrentPipeLayer = AtmosPipeLayer.Primary;
+
+ /// <summary>
+ /// The RSI paths that the entity will use to update its sprite when its pipe layer changes;
+ /// if empty, the entity sprite will not update when it pipe layer changes.
+ /// If you want to set specific sprite layers to update when the pipe layer changes, use
+ /// <see cref="SpriteLayersRsiPaths"/> instead.
+ /// </summary>
+ /// <remarks>
+ /// If the dictionary is not empty there should be an entry for each atmos pipe layer.
+ /// </remarks>
+ [DataField]
+ public Dictionary<AtmosPipeLayer, string> SpriteRsiPaths = new();
+
+ /// <summary>
+ /// Used to update specific sprite layers when the entity's pipe layer changes.
+ /// The dictionary key is the name of the sprite layer to be updated, and its value is
+ /// a second dictionary which contains the RSI paths indexed by pipe layer.
+ /// If you want to change the default RSI path used by the entity, use
+ /// <see cref="SpriteRsiPaths"/> instead.
+ /// </summary>
+ /// <remarks>
+ /// If an dictionary is not empty there should be an entry for each pipe layer.
+ /// </remarks>
+ [DataField]
+ public Dictionary<string, Dictionary<AtmosPipeLayer, string>> SpriteLayersRsiPaths = new();
+
+ /// <summary>
+ /// Entity prototypes that will be used to replace the current one when using
+ /// position dependent entity placement via AlignAtmosPipeLayers.
+ /// </summary>
+ /// <remarks>
+ /// If the dictionary is not empty there should be an entry for each atmos pipe layer.
+ /// </remarks>
+ [DataField]
+ public Dictionary<AtmosPipeLayer, EntProtoId> AlternativePrototypes = new();
+
+ /// <summary>
+ /// The pipe layers of this entity cannot be changed when this value is true.
+ /// </summary>
+ [DataField]
+ public bool PipeLayersLocked;
+
+ /// <summary>
+ /// Tool quality required to cause a pipe to change layers
+ /// </summary>
+ [DataField]
+ public ProtoId<ToolQualityPrototype> Tool = "Screwing";
+
+ /// <summary>
+ /// The base delay to use for changing layers.
+ /// </summary>
+ [DataField]
+ public float Delay = 1f;
+}
+
+/// <summary>
+/// Raised when a player attempts to cycle a pipe to its next layer
+/// </summary>
+[Serializable, NetSerializable]
+public sealed partial class TrySetNextPipeLayerCompletedEvent : SimpleDoAfterEvent;
+
+/// <summary>
+/// Raised when a player attempts to set a pipe a specified layer
+/// </summary>
+[Serializable, NetSerializable]
+public sealed partial class TrySettingPipeLayerCompletedEvent : SimpleDoAfterEvent
+{
+ public AtmosPipeLayer PipeLayer;
+
+ public TrySettingPipeLayerCompletedEvent(AtmosPipeLayer pipeLayer)
+ {
+ PipeLayer = pipeLayer;
+ }
+}
+
+[Serializable, NetSerializable]
+public enum AtmosPipeLayerVisuals
+{
+ Sprite,
+ SpriteLayers,
+ DrawDepth,
+}
+
+[Serializable, NetSerializable]
+public enum AtmosPipeLayer
+{
+ Primary,
+ Secondary,
+ Tertiary,
+}
[RegisterComponent]
public sealed partial class PipeAppearanceComponent : Component
{
- [DataField("sprite")]
- public SpriteSpecifier.Rsi Sprite = new(new("Structures/Piping/Atmospherics/pipe.rsi"), "pipeConnector");
+ [DataField]
+ public SpriteSpecifier.Rsi[] Sprite = [new(new("Structures/Piping/Atmospherics/pipe.rsi"), "pipeConnector"),
+ new(new("Structures/Piping/Atmospherics/pipe_alt1.rsi"), "pipeConnector"),
+ new(new("Structures/Piping/Atmospherics/pipe_alt2.rsi"), "pipeConnector")];
}
/// <summary>
/// Bitmask look up for atmos pipes, 1 for occupied and 0 for empty.
- /// Indexed by the color hexcode of the pipe
+ /// Indexed by the net ID, layer and color hexcode of the pipe
/// </summary>
[ViewVariables]
- public Dictionary<(int, string), ulong> AtmosPipeData = new();
+ public Dictionary<AtmosMonitoringConsoleSubnet, ulong> AtmosPipeData = new();
/// <summary>
/// The last game tick that the chunk was updated
public NetCoordinates NetCoordinates;
/// <summary>
- /// The associated pipe network ID
+ /// The associated pipe network ID
/// </summary>
public int NetId = -1;
/// </summary>
public Color PipeColor;
+ /// <summary>
+ /// The pipe layer the entity is on
+ /// </summary>
+ public AtmosPipeLayer PipeLayer;
+
/// <summary>
/// Populate the atmos monitoring console nav map with a single entity
/// </summary>
- public AtmosDeviceNavMapData(NetEntity netEntity, NetCoordinates netCoordinates, int netId, ProtoId<NavMapBlipPrototype> navMapBlip, Direction direction, Color pipeColor)
+ public AtmosDeviceNavMapData(NetEntity netEntity,
+ NetCoordinates netCoordinates,
+ int netId,
+ ProtoId<NavMapBlipPrototype> navMapBlip,
+ Direction direction,
+ Color pipeColor,
+ AtmosPipeLayer pipeLayer)
{
NetEntity = netEntity;
NetCoordinates = netCoordinates;
NavMapBlip = navMapBlip;
Direction = direction;
PipeColor = pipeColor;
+ PipeLayer = pipeLayer;
}
}
public NetCoordinates Coordinates;
/// <summary>
- /// The associated pipe network ID
+ /// The associated pipe network ID
/// </summary>
public int NetId = -1;
public float TotalMolData;
/// <summary>
- /// Mol and percentage for all detected gases
+ /// Mol and percentage for all detected gases
/// </summary>
public Dictionary<Gas, float> GasData = new();
}
}
+/// <summary>
+/// Used to group atmos pipe chunks into subnets based on their properties and
+/// improve the efficiency of rendering these chunks on the atmos monitoring console.
+/// </summary>
+/// <param name="NetId">The associated network ID.</param>
+/// <param name="PipeLayer">The associated pipe layer.</param>
+/// <param name="HexCode">The color of the pipe.</param>
+[Serializable, NetSerializable]
+public record AtmosMonitoringConsoleSubnet(int NetId, AtmosPipeLayer PipeLayer, string HexCode);
+
public enum AtmosPipeChunkDataFacing : byte
{
// Values represent bit shift offsets when retrieving data in the tile array.
namespace Content.Shared.Atmos.Components;
/// <summary>
-/// Entities with this component appear on the
+/// Entities with this component appear on the
/// nav maps of atmos monitoring consoles
/// </summary>
[RegisterComponent, NetworkedComponent]
/// entity on the atmos monitoring console nav map.
/// If null, no blip is drawn (i.e., null for pipes)
/// </summary>
- [DataField, ViewVariables]
+ [DataField]
public ProtoId<NavMapBlipPrototype>? NavMapBlip = null;
+
+ /// <summary>
+ /// Sets whether attached atmos pipes will always be rendered
+ /// on the atmos monitoring console nav map, even if these
+ /// pipes are not connected to any pipes in a neighboring tile.
+ /// </summary>
+ [DataField]
+ public bool ShowAbsentConnections = true;
}
private void OnGetState(EntityUid uid, AtmosMonitoringConsoleComponent component, ref ComponentGetState args)
{
- Dictionary<Vector2i, Dictionary<(int, string), ulong>> chunks;
+ Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> chunks;
// Should this be a full component state or a delta-state?
if (args.FromTick <= component.CreationTick || component.ForceFullUpdate)
[Serializable, NetSerializable]
protected sealed class AtmosMonitoringConsoleState(
- Dictionary<Vector2i, Dictionary<(int, string), ulong>> chunks,
+ Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> chunks,
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices)
: ComponentState
{
- public Dictionary<Vector2i, Dictionary<(int, string), ulong>> Chunks = chunks;
+ public Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> Chunks = chunks;
public Dictionary<NetEntity, AtmosDeviceNavMapData> AtmosDevices = atmosDevices;
}
[Serializable, NetSerializable]
protected sealed class AtmosMonitoringConsoleDeltaState(
- Dictionary<Vector2i, Dictionary<(int, string), ulong>> modifiedChunks,
+ Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> modifiedChunks,
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices,
HashSet<Vector2i> allChunks)
: ComponentState, IComponentDeltaState<AtmosMonitoringConsoleState>
{
- public Dictionary<Vector2i, Dictionary<(int, string), ulong>> ModifiedChunks = modifiedChunks;
+ public Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> ModifiedChunks = modifiedChunks;
public Dictionary<NetEntity, AtmosDeviceNavMapData> AtmosDevices = atmosDevices;
public HashSet<Vector2i> AllChunks = allChunks;
foreach (var (index, data) in ModifiedChunks)
{
- state.Chunks[index] = new Dictionary<(int, string), ulong>(data);
+ state.Chunks[index] = new Dictionary<AtmosMonitoringConsoleSubnet, ulong>(data);
}
state.AtmosDevices.Clear();
public AtmosMonitoringConsoleState CreateNewFullState(AtmosMonitoringConsoleState state)
{
- var chunks = new Dictionary<Vector2i, Dictionary<(int, string), ulong>>(state.Chunks.Count);
+ var chunks = new Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>>(state.Chunks.Count);
foreach (var (index, data) in state.Chunks)
{
continue;
if (ModifiedChunks.ContainsKey(index))
- chunks[index] = new Dictionary<(int, string), ulong>(ModifiedChunks[index]);
+ chunks[index] = new Dictionary<AtmosMonitoringConsoleSubnet, ulong>(ModifiedChunks[index]);
else
- chunks[index] = new Dictionary<(int, string), ulong>(state.Chunks[index]);
+ chunks[index] = new Dictionary<AtmosMonitoringConsoleSubnet, ulong>(state.Chunks[index]);
}
return new AtmosMonitoringConsoleState(chunks, new(AtmosDevices));
--- /dev/null
+using Content.Shared.Atmos.Components;
+
+namespace Content.Shared.Atmos.EntitySystems;
+
+public abstract partial class SharedAtmosPipeAppearanceSystem : EntitySystem
+{
+ /// <summary>
+ /// Returns the max number of pipe layers supported by a entity.
+ /// </summary>
+ /// <param name="uid">The entity being checked.</param>
+ /// <param name="atmosPipeLayers">The entity's <see cref="AtmosPipeLayersComponent"/>, if available.</param>
+ /// <returns>Returns <see cref="AtmosPipeLayersComponent.NumberOfPipeLayers"/>
+ /// if the entity has the component, or 1 if it does not.</returns>
+ protected int GetNumberOfPipeLayers(EntityUid uid, out AtmosPipeLayersComponent? atmosPipeLayers)
+ {
+ return TryComp(uid, out atmosPipeLayers) ? atmosPipeLayers.NumberOfPipeLayers : 1;
+ }
+}
--- /dev/null
+using Content.Shared.Atmos.Components;
+using Content.Shared.Database;
+using Content.Shared.Examine;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Popups;
+using Content.Shared.SubFloor;
+using Content.Shared.Tools;
+using Content.Shared.Tools.Components;
+using Content.Shared.Tools.Systems;
+using Content.Shared.Verbs;
+using Robust.Shared.Prototypes;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Content.Shared.Atmos.EntitySystems;
+
+/// <summary>
+/// The system responsible for checking and adjusting the connection layering of gas pipes
+/// </summary>
+public abstract partial class SharedAtmosPipeLayersSystem : EntitySystem
+{
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly IPrototypeManager _protoManager = default!;
+ [Dependency] private readonly SharedToolSystem _tool = default!;
+ [Dependency] private readonly SharedHandsSystem _hands = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<AtmosPipeLayersComponent, ExaminedEvent>(OnExamined);
+ SubscribeLocalEvent<AtmosPipeLayersComponent, GetVerbsEvent<Verb>>(OnGetVerb);
+ SubscribeLocalEvent<AtmosPipeLayersComponent, InteractUsingEvent>(OnInteractUsing);
+ SubscribeLocalEvent<AtmosPipeLayersComponent, UseInHandEvent>(OnUseInHandEvent);
+ SubscribeLocalEvent<AtmosPipeLayersComponent, TrySetNextPipeLayerCompletedEvent>(OnSetNextPipeLayerCompleted);
+ SubscribeLocalEvent<AtmosPipeLayersComponent, TrySettingPipeLayerCompletedEvent>(OnSettingPipeLayerCompleted);
+ }
+
+ private void OnExamined(Entity<AtmosPipeLayersComponent> ent, ref ExaminedEvent args)
+ {
+ var layerName = GetPipeLayerName(ent.Comp.CurrentPipeLayer);
+ args.PushMarkup(Loc.GetString("atmos-pipe-layers-component-current-layer", ("layerName", layerName)));
+ }
+
+ private void OnGetVerb(Entity<AtmosPipeLayersComponent> ent, ref GetVerbsEvent<Verb> args)
+ {
+ if (!args.CanAccess || !args.CanInteract || !args.CanComplexInteract)
+ return;
+
+ if (ent.Comp.NumberOfPipeLayers <= 1 || ent.Comp.PipeLayersLocked)
+ return;
+
+ if (!_protoManager.TryIndex(ent.Comp.Tool, out var toolProto))
+ return;
+
+ var user = args.User;
+
+ if (TryComp<SubFloorHideComponent>(ent, out var subFloorHide) && subFloorHide.IsUnderCover)
+ {
+ var v = new Verb
+ {
+ Priority = 1,
+ Category = VerbCategory.Adjust,
+ Text = Loc.GetString("atmos-pipe-layers-component-pipes-are-covered"),
+ Disabled = true,
+ Impact = LogImpact.Low,
+ DoContactInteraction = true,
+ };
+
+ args.Verbs.Add(v);
+ }
+
+ else if (!TryGetHeldTool(user, ent.Comp.Tool, out var tool))
+ {
+ var v = new Verb
+ {
+ Priority = 1,
+ Category = VerbCategory.Adjust,
+ Text = Loc.GetString("atmos-pipe-layers-component-tool-missing", ("toolName", Loc.GetString(toolProto.ToolName).ToLower())),
+ Disabled = true,
+ Impact = LogImpact.Low,
+ DoContactInteraction = true,
+ };
+
+ args.Verbs.Add(v);
+ }
+
+ else
+ {
+ for (var i = 0; i < ent.Comp.NumberOfPipeLayers; i++)
+ {
+ var index = i;
+ var layerName = GetPipeLayerName((AtmosPipeLayer)index);
+ var label = Loc.GetString("atmos-pipe-layers-component-select-layer", ("layerName", layerName));
+
+ var v = new Verb
+ {
+ Priority = 1,
+ Category = VerbCategory.Adjust,
+ Text = label,
+ Disabled = index == (int)ent.Comp.CurrentPipeLayer,
+ Impact = LogImpact.Low,
+ DoContactInteraction = true,
+ Act = () =>
+ {
+ _tool.UseTool(tool.Value, user, ent, ent.Comp.Delay, tool.Value.Comp.Qualities, new TrySettingPipeLayerCompletedEvent((AtmosPipeLayer)index));
+ }
+ };
+
+ args.Verbs.Add(v);
+ }
+ }
+ }
+
+ private void OnInteractUsing(Entity<AtmosPipeLayersComponent> ent, ref InteractUsingEvent args)
+ {
+ if (ent.Comp.NumberOfPipeLayers <= 1 || ent.Comp.PipeLayersLocked)
+ return;
+
+ if (TryComp<SubFloorHideComponent>(ent, out var subFloorHide) && subFloorHide.IsUnderCover)
+ {
+ _popup.PopupPredicted(Loc.GetString("atmos-pipe-layers-component-cannot-adjust-pipes"), ent, args.User);
+ return;
+ }
+
+ if (TryComp<ToolComponent>(args.Used, out var tool) && _tool.HasQuality(args.Used, ent.Comp.Tool, tool))
+ _tool.UseTool(args.Used, args.User, ent, ent.Comp.Delay, tool.Qualities, new TrySetNextPipeLayerCompletedEvent());
+ }
+
+ private void OnUseInHandEvent(Entity<AtmosPipeLayersComponent> ent, ref UseInHandEvent args)
+ {
+ if (ent.Comp.NumberOfPipeLayers <= 1 || ent.Comp.PipeLayersLocked)
+ return;
+
+ if (!TryGetHeldTool(args.User, ent.Comp.Tool, out var tool))
+ {
+ if (_protoManager.TryIndex(ent.Comp.Tool, out var toolProto))
+ {
+ var toolName = Loc.GetString(toolProto.ToolName).ToLower();
+ var message = Loc.GetString("atmos-pipe-layers-component-tool-missing", ("toolName", toolName));
+
+ _popup.PopupPredicted(message, ent, args.User);
+ }
+
+ return;
+ }
+
+ _tool.UseTool(tool.Value, args.User, ent, ent.Comp.Delay, tool.Value.Comp.Qualities, new TrySetNextPipeLayerCompletedEvent());
+ }
+
+ private void OnSetNextPipeLayerCompleted(Entity<AtmosPipeLayersComponent> ent, ref TrySetNextPipeLayerCompletedEvent args)
+ {
+ if (args.Cancelled)
+ return;
+
+ SetNextPipeLayer(ent, args.User, args.Used);
+ }
+
+ private void OnSettingPipeLayerCompleted(Entity<AtmosPipeLayersComponent> ent, ref TrySettingPipeLayerCompletedEvent args)
+ {
+ if (args.Cancelled)
+ return;
+
+ SetPipeLayer(ent, args.PipeLayer, args.User, args.Used);
+ }
+
+ /// <summary>
+ /// Increments an entity's pipe layer by 1, wrapping around to 0 if the max pipe layer is reached
+ /// </summary>
+ /// <param name="ent">The pipe entity</param>
+ /// <param name="user">The player entity who adjusting the pipe layer</param>
+ /// <param name="used">The tool used to adjust the pipe layer</param>
+ public void SetNextPipeLayer(Entity<AtmosPipeLayersComponent> ent, EntityUid? user = null, EntityUid? used = null)
+ {
+ var newLayer = ((int)ent.Comp.CurrentPipeLayer + 1) % ent.Comp.NumberOfPipeLayers;
+ SetPipeLayer(ent, (AtmosPipeLayer)newLayer, user, used);
+ }
+
+ /// <summary>
+ /// Sets an entity's pipe layer to a specified value
+ /// </summary>
+ /// <param name="ent">The pipe entity</param>
+ /// <param name="layer">The new layer value</param>
+ /// <param name="user">The player entity who adjusting the pipe layer</param>
+ /// <param name="used">The tool used to adjust the pipe layer</param>
+ public virtual void SetPipeLayer(Entity<AtmosPipeLayersComponent> ent, AtmosPipeLayer layer, EntityUid? user = null, EntityUid? used = null)
+ {
+ if (ent.Comp.PipeLayersLocked)
+ return;
+
+ ent.Comp.CurrentPipeLayer = (AtmosPipeLayer)Math.Clamp((int)layer, 0, ent.Comp.NumberOfPipeLayers - 1);
+ Dirty(ent);
+
+ if (TryComp<AppearanceComponent>(ent, out var appearance))
+ {
+ if (ent.Comp.SpriteRsiPaths.TryGetValue(ent.Comp.CurrentPipeLayer, out var path))
+ _appearance.SetData(ent, AtmosPipeLayerVisuals.Sprite, path, appearance);
+
+ if (ent.Comp.SpriteLayersRsiPaths.Count > 0)
+ {
+ var data = new Dictionary<string, string>();
+
+ foreach (var (layerKey, rsiPaths) in ent.Comp.SpriteLayersRsiPaths)
+ {
+ if (rsiPaths.TryGetValue(ent.Comp.CurrentPipeLayer, out path))
+ data.TryAdd(layerKey, path);
+ }
+
+ _appearance.SetData(ent, AtmosPipeLayerVisuals.SpriteLayers, data, appearance);
+ }
+ }
+
+ if (user != null)
+ {
+ var layerName = GetPipeLayerName(ent.Comp.CurrentPipeLayer);
+ var message = Loc.GetString("atmos-pipe-layers-component-change-layer", ("layerName", layerName));
+
+ _popup.PopupPredicted(message, ent, user);
+ }
+ }
+
+ /// <summary>
+ /// Try to find an entity prototype associated with a specified <see cref="AtmosPipeLayer"/>.
+ /// </summary>
+ /// <param name="component">The <see cref="AtmosPipeLayersComponent"/> with the alternative prototypes data.</param>
+ /// <param name="layer">The atmos pipe layer associated with the entity prototype.</param>
+ /// <param name="proto">The returned entity prototype.</param>
+ /// <returns>True if there was an entity prototype associated with the layer.</returns>
+ public bool TryGetAlternativePrototype(AtmosPipeLayersComponent component, AtmosPipeLayer layer, out EntProtoId proto)
+ {
+ return component.AlternativePrototypes.TryGetValue(layer, out proto);
+ }
+
+ /// <summary>
+ /// Checks a player entity's hands to see if they are holding a tool with a specified quality
+ /// </summary>
+ /// <param name="user">The player entity</param>
+ /// <param name="toolQuality">The tool quality being checked for</param>
+ /// <param name="heldTool">A tool with the specified tool quality</param>
+ /// <returns>True if an appropriate tool was found</returns>
+ private bool TryGetHeldTool(EntityUid user, ProtoId<ToolQualityPrototype> toolQuality, [NotNullWhen(true)] out Entity<ToolComponent>? heldTool)
+ {
+ heldTool = null;
+
+ foreach (var heldItem in _hands.EnumerateHeld(user))
+ {
+ if (TryComp<ToolComponent>(heldItem, out var tool) &&
+ _tool.HasQuality(heldItem, toolQuality, tool))
+ {
+ heldTool = new Entity<ToolComponent>(heldItem, tool);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private string GetPipeLayerName(AtmosPipeLayer layer)
+ {
+ return Loc.GetString("atmos-pipe-layers-component-layer-" + layer.ToString().ToLower());
+ }
+}
[DataField]
public ProtoId<ConstructionPrototype>? Mirror { get; private set; }
+ /// <summary>
+ /// Possible constructions to replace this one with as determined by the placement mode
+ /// </summary>
+ [DataField]
+ public ProtoId<ConstructionPrototype>[] AlternativePrototypes = [];
+
public IReadOnlyList<IConstructionCondition> Conditions => _conditions;
}
/// <summary>
/// This is for sub-floors, the floors you see after prying off a tile.
/// </summary>
- LowFloors = DrawDepthTag.Default - 18,
+ LowFloors = DrawDepthTag.Default - 20,
// various entity types that require different
// draw depths, as to avoid hiding
#region SubfloorEntities
- ThickPipe = DrawDepthTag.Default - 17,
- ThickWire = DrawDepthTag.Default - 16,
+ ThickPipe = DrawDepthTag.Default - 19,
+ ThickWire = DrawDepthTag.Default - 18,
+ ThinPipeAlt2 = DrawDepthTag.Default - 17,
+ ThinPipeAlt1 = DrawDepthTag.Default - 16,
ThinPipe = DrawDepthTag.Default - 15,
ThinWire = DrawDepthTag.Default - 14,
#endregion
/// </summary>
[DataField]
public float Scale { get; private set; } = 1f;
+
+ /// <summary>
+ /// Describes how the blip should be positioned.
+ /// It's up to the individual system to enforce this
+ /// </summary>
+ [DataField]
+ public NavMapBlipPlacement Placement { get; private set; } = NavMapBlipPlacement.Centered;
+}
+
+public enum NavMapBlipPlacement
+{
+ Centered, // The blip appears in the center of the tile
+ Offset // The blip is offset from the center of the tile (determined by the system using the blips)
}
ScannerRevealed,
}
+ [Serializable, NetSerializable]
public enum SubfloorLayers : byte
{
FirstLayer
public static readonly VerbCategory SelectType = new("verb-categories-select-type", null);
public static readonly VerbCategory PowerLevel = new("verb-categories-power-level", null);
+
+ public static readonly VerbCategory Adjust =
+ new("verb-categories-adjust", "/Textures/Interface/VerbIcons/screwdriver.png");
}
}
--- /dev/null
+atmos-pipe-layers-component-layer-primary = primary
+atmos-pipe-layers-component-layer-secondary = secondary
+atmos-pipe-layers-component-layer-tertiary = tertiary
+
+atmos-pipe-layers-component-change-layer = Adjusted to its {$layerName} configuration.
+atmos-pipe-layers-component-current-layer = It is in its {$layerName} configuration.
+atmos-pipe-layers-component-select-layer = {CAPITALIZE($layerName)} configuration
+atmos-pipe-layers-component-tool-missing = Requires {INDEFINITE($toolName)} {$toolName}
+atmos-pipe-layers-component-pipes-are-covered = The pipes are covered
+atmos-pipe-layers-component-cannot-adjust-pipes = You need to uncover the pipes before they can be adjusted.
\ No newline at end of file
verb-categories-select-type = Select Type
verb-categories-fax = Set Destination
verb-categories-power-level = Power Level
+verb-categories-adjust = Adjust
verb-common-toggle-light = Toggle light
verb-common-close = Close
color: "#ffcd00"
texturePaths:
- "/Textures/Interface/NavMap/beveled_star.png"
-
+ placement: Offset
+
- type: navMapBlip
id: GasVentOpening
- scale: 0.6667
+ scale: 0.75
color: LightGray
texturePaths:
- "/Textures/Interface/NavMap/beveled_square.png"
-
+
- type: navMapBlip
id: GasVentScrubber
- scale: 0.6667
+ scale: 0.75
color: LightGray
texturePaths:
- "/Textures/Interface/NavMap/beveled_circle.png"
-
+
- type: navMapBlip
id: GasFlowRegulator
scale: 0.75
- "/Textures/Interface/NavMap/beveled_arrow_east.png"
- "/Textures/Interface/NavMap/beveled_arrow_north.png"
- "/Textures/Interface/NavMap/beveled_arrow_west.png"
-
+ placement: Offset
+
- type: navMapBlip
id: GasValve
scale: 0.6667
texturePaths:
- "/Textures/Interface/NavMap/beveled_diamond_north_south.png"
- "/Textures/Interface/NavMap/beveled_diamond_east_west.png"
- - "/Textures/Interface/NavMap/beveled_diamond_north_south.png"
+ - "/Textures/Interface/NavMap/beveled_diamond_north_south.png"
- "/Textures/Interface/NavMap/beveled_diamond_east_west.png"
-
+ placement: Offset
+
- type: navMapBlip
id: Thermoregulator
- scale: 0.6667
+ scale: 0.75
+ color: LightGray
+ texturePaths:
+ - "/Textures/Interface/NavMap/beveled_hexagon.png"
+
+- type: navMapBlip
+ id: GasPipeManifold
+ scale: 0.9
color: LightGray
texturePaths:
- - "/Textures/Interface/NavMap/beveled_hexagon.png"
\ No newline at end of file
+ - "/Textures/Interface/NavMap/beveled_rectangle_east_west.png"
+ - "/Textures/Interface/NavMap/beveled_rectangle_north_south.png"
+ - "/Textures/Interface/NavMap/beveled_rectangle_east_west.png"
+ - "/Textures/Interface/NavMap/beveled_rectangle_north_south.png"
--- /dev/null
+## This file contains duplicated pipe prototypes with
+## different layer offsets to faciliate mapping
+
+# Layer 1
+- type: entity
+ abstract: true
+ id: GasPipeLayerAlt1
+ components:
+ - type: AtmosPipeLayers
+ pipeLayer: 1
+
+# Layer 2
+- type: entity
+ abstract: true
+ id: GasPipeLayerAlt2
+ components:
+ - type: AtmosPipeLayers
+ pipeLayer: 2
+
+# GasPipeStraight
+- type: entity
+ parent: [GasPipeLayerAlt1, GasPipeStraight]
+ id: GasPipeStraightAlt1
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt1
+ sprite: Structures/Piping/Atmospherics/pipe_alt1.rsi
+ - type: Construction
+ node: straightAlt1
+
+- type: entity
+ parent: [GasPipeLayerAlt2, GasPipeStraight]
+ id: GasPipeStraightAlt2
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt2
+ sprite: Structures/Piping/Atmospherics/pipe_alt2.rsi
+ - type: Construction
+ node: straightAlt2
+
+# GasPipeHalf
+- type: entity
+ parent: [GasPipeLayerAlt1, GasPipeHalf]
+ id: GasPipeHalfAlt1
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt1
+ sprite: Structures/Piping/Atmospherics/pipe_alt1.rsi
+ - type: Construction
+ node: halfAlt1
+
+- type: entity
+ parent: [GasPipeLayerAlt2, GasPipeHalf]
+ id: GasPipeHalfAlt2
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt2
+ sprite: Structures/Piping/Atmospherics/pipe_alt2.rsi
+ - type: Construction
+ node: halfAlt2
+
+# GasPipeBend
+- type: entity
+ parent: [GasPipeLayerAlt1, GasPipeBend]
+ id: GasPipeBendAlt1
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt1
+ sprite: Structures/Piping/Atmospherics/pipe_alt1.rsi
+ - type: Construction
+ node: bendAlt1
+
+- type: entity
+ parent: [GasPipeLayerAlt2, GasPipeBend]
+ id: GasPipeBendAlt2
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt2
+ sprite: Structures/Piping/Atmospherics/pipe_alt2.rsi
+ - type: Construction
+ node: bendAlt2
+
+# GasPipeTJunction
+- type: entity
+ parent: [GasPipeLayerAlt1, GasPipeTJunction]
+ id: GasPipeTJunctionAlt1
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt1
+ sprite: Structures/Piping/Atmospherics/pipe_alt1.rsi
+ - type: Construction
+ node: tjunctionAlt1
+
+- type: entity
+ parent: [GasPipeLayerAlt2, GasPipeTJunction]
+ id: GasPipeTJunctionAlt2
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt2
+ sprite: Structures/Piping/Atmospherics/pipe_alt2.rsi
+ - type: Construction
+ node: tjunctionAlt2
+
+# GasPipeFourway
+- type: entity
+ parent: [GasPipeLayerAlt1, GasPipeFourway]
+ id: GasPipeFourwayAlt1
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt1
+ sprite: Structures/Piping/Atmospherics/pipe_alt1.rsi
+ - type: Construction
+ node: fourwayAlt1
+
+- type: entity
+ parent: [GasPipeLayerAlt2, GasPipeFourway]
+ id: GasPipeFourwayAlt2
+ categories: [ HideSpawnMenu ]
+ components:
+ - type: Sprite
+ drawdepth: ThinPipeAlt2
+ sprite: Structures/Piping/Atmospherics/pipe_alt2.rsi
+ - type: Construction
+ node: fourwayAlt2
!type:PipeNode
nodeGroupID: Pipe
pipeDirection: South
+ - type: AtmosPipeLayers
+ spriteRsiPaths:
+ Primary: Structures/Piping/Atmospherics/pump.rsi
+ Secondary: Structures/Piping/Atmospherics/pump_alt1.rsi
+ Tertiary: Structures/Piping/Atmospherics/pump_alt2.rsi
- type: entity
parent: GasBinaryBase
sprite: Structures/Piping/Atmospherics/gascanisterport.rsi
layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi
- state: pipeHalf
+ state: pipeUnaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ]
- state: gasCanisterPort
map: [ "enum.SubfloorLayers.FirstLayer" ]
+ - type: AtmosPipeLayers
+ spriteRsiPaths: {}
- type: Appearance
- type: PipeColorVisuals
- type: GasPort
sprite: Structures/Piping/Atmospherics/vent.rsi
layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi
- state: pipeStraight
+ state: pipeBinaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ]
- state: vent_off
map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ]
+ - type: AtmosPipeLayers
+ spriteRsiPaths: {}
- type: GenericVisualizer
visuals:
enum.VentPumpVisuals.State:
sprite: Structures/Machines/gasrecycler.rsi
layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi
- state: pipeStraight
+ state: pipeBinaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ]
- state: running
- state: unlit
shader: unshaded
+ - type: AtmosPipeLayers
+ spriteLayersRsiPaths:
+ enum.PipeVisualLayers.Pipe:
+ Primary: Structures/Piping/Atmospherics/pipe.rsi
+ Secondary: Structures/Piping/Atmospherics/pipe_alt1.rsi
+ Tertiary: Structures/Piping/Atmospherics/pipe_alt2.rsi
+ - type: Appearance
- type: GenericVisualizer
visuals:
enum.PumpVisuals.Enabled:
enabled:
True: { state: running }
False: { state: unlit }
- - type: Appearance
- type: PipeColorVisuals
- type: Rotatable
- type: GasRecycler
map: [ "enum.PipeVisualLayers.Pipe" ]
- state: heStraight
map: [ "enum.SubfloorLayers.FirstLayer" ]
+ - type: AtmosPipeLayers
+ spriteRsiPaths: {}
+ numberOfPipeLayers: 1
- type: SubFloorHide
visibleLayers:
- enum.SubfloorLayers.FirstLayer
- type: entity
- parent: [AirSensorBase, GasPipeBase]
+ parent: [AirSensorBase, GasBinaryBase]
id: GasPipeSensor
name: gas pipe sensor
description: Reports on the status of the gas in the attached pipe network.
- map: [ "enum.PowerDeviceVisualLayers.Powered" ]
state: lights
shader: unshaded
+ - type: AtmosPipeLayers
+ spriteRsiPaths:
+ Primary: Structures/Piping/Atmospherics/gas_pipe_sensor.rsi
+ Secondary: Structures/Piping/Atmospherics/gas_pipe_sensor_alt1.rsi
+ Tertiary: Structures/Piping/Atmospherics/gas_pipe_sensor_alt2.rsi
- type: Appearance
- type: GenericVisualizer
visuals:
- type: entity
abstract: true
- id: GasPipeBase
parent: BaseItem
+ id: GasPipeSansLayers
name: gas pipe
description: Holds gas.
placement:
damageModifierSet: Metallic
- type: SubFloorHide
- type: CollideOnAnchor
- - type: PipeAppearance
- type: Anchorable
- type: Rotatable
- type: Destructible
drawdepth: ThinPipe
visible: false
- type: Appearance
+ - type: PipeAppearance
- type: PipeColorVisuals
- type: NodeContainer
- type: PipeRestrictOverlap
- type: StaticPrice
price: 30
+- type: entity
+ abstract: true
+ parent: GasPipeSansLayers
+ id: GasPipeBase
+ components:
+ - type: AtmosPipeLayers
+ spriteLayersRsiPaths:
+ enum.PipeVisualLayers.Pipe:
+ Primary: Structures/Piping/Atmospherics/pipe.rsi
+ Secondary: Structures/Piping/Atmospherics/pipe_alt1.rsi
+ Tertiary: Structures/Piping/Atmospherics/pipe_alt2.rsi
+
#Note: The PipeDirection of the PipeNode should be the south-facing version, because the entity starts at an angle of 0 (south)
- type: entity
parent: GasPipeBase
id: GasPipeHalf
suffix: Half
+ placement:
+ mode: AlignAtmosPipeLayers
components:
- type: NodeContainer
nodes:
pipeDirection: South
- type: Sprite
layers:
- - state: pipeHalf
- map: [ "enum.PipeVisualLayers.Pipe" ]
+ - state: pipeHalf
+ map: [ "enum.PipeVisualLayers.Pipe" ]
+ - type: AtmosPipeLayers
+ alternativePrototypes:
+ Primary: GasPipeHalf
+ Secondary: GasPipeHalfAlt1
+ Tertiary: GasPipeHalfAlt2
- type: Construction
graph: GasPipe
node: half
+ - type: Item
+ size: Small
+ storedSprite:
+ sprite: Structures/Piping/Atmospherics/pipe.rsi
+ state: storageHalf
- type: GuideHelp
guides:
- Pipes
parent: GasPipeBase
id: GasPipeStraight
suffix: Straight
+ placement:
+ mode: AlignAtmosPipeLayers
components:
- type: NodeContainer
nodes:
pipeDirection: Longitudinal
- type: Sprite
layers:
- - state: pipeStraight
- map: [ "enum.PipeVisualLayers.Pipe" ]
+ - state: pipeStraight
+ map: [ "enum.PipeVisualLayers.Pipe" ]
+ - type: AtmosPipeLayers
+ alternativePrototypes:
+ Primary: GasPipeStraight
+ Secondary: GasPipeStraightAlt1
+ Tertiary: GasPipeStraightAlt2
- type: Construction
graph: GasPipe
node: straight
- type: Item
- size: Normal
storedSprite:
sprite: Structures/Piping/Atmospherics/pipe.rsi
state: storageStraight
parent: GasPipeBase
id: GasPipeBend
suffix: Bend
+ placement:
+ mode: AlignAtmosPipeLayers
components:
- type: NodeContainer
nodes:
layers:
- state: pipeBend
map: [ "enum.PipeVisualLayers.Pipe" ]
+ - type: AtmosPipeLayers
+ alternativePrototypes:
+ Primary: GasPipeBend
+ Secondary: GasPipeBendAlt1
+ Tertiary: GasPipeBendAlt2
- type: Construction
graph: GasPipe
node: bend
- type: Item
- size: Small
shape:
- 0,0,1,0
- 1,1,1,1
- type: entity
parent: GasPipeBase
id: GasPipeTJunction
+ placement:
+ mode: AlignAtmosPipeLayers
suffix: TJunction
components:
- type: NodeContainer
layers:
- state: pipeTJunction
map: [ "enum.PipeVisualLayers.Pipe" ]
+ - type: AtmosPipeLayers
+ alternativePrototypes:
+ Primary: GasPipeTJunction
+ Secondary: GasPipeTJunctionAlt1
+ Tertiary: GasPipeTJunctionAlt2
- type: Construction
graph: GasPipe
node: tjunction
- type: Item
- size: Normal
- shape:
- - 0,0,2,0
- - 1,1,1,1
heldPrefix: TJunction
storedSprite:
sprite: Structures/Piping/Atmospherics/pipe.rsi
parent: GasPipeBase
id: GasPipeFourway
suffix: Fourway
+ placement:
+ mode: AlignAtmosPipeLayers
components:
- type: Transform
noRot: true
layers:
- state: pipeFourway
map: [ "enum.PipeVisualLayers.Pipe" ]
+ - type: AtmosPipeLayers
+ alternativePrototypes:
+ Primary: GasPipeFourway
+ Secondary: GasPipeFourwayAlt1
+ Tertiary: GasPipeFourwayAlt2
- type: Construction
graph: GasPipe
node: fourway
- type: Item
- size: Normal
- shape:
- - 1,0,1,2
- - 0,1,2,1
heldPrefix: Fourway
+ storedSprite:
+ sprite: Structures/Piping/Atmospherics/pipe.rsi
+ state: storageFourway
- type: MeleeWeapon
wideAnimationRotation: 90
attackRate: 0.75
layers:
- state: pipeBroken
map: [ "enum.PipeVisualLayers.Pipe" ]
+ - type: AtmosPipeLayers
+ numberOfPipeLayers: 1
- type: Construction
graph: GasPipe
node: broken
+ - type: Item
+ size: Small
- type: Destructible
thresholds: # override parent to avoid recursive destruction
- trigger:
guides:
- Pipes
- PipeNetworks
+
+- type: entity
+ parent: GasPipeSansLayers
+ id: GasPipeManifold
+ name: gas pipe manifold
+ description: Allows gas pipes of different configurations to be connected together.
+ placement:
+ mode: SnapgridCenter
+ components:
+ - type: Sprite
+ sprite: Structures/Piping/Atmospherics/manifold.rsi
+ layers:
+ - state: pipeManifold
+ map: [ "enum.PipeVisualLayers.Pipe" ]
+ - type: AtmosPipeLayers
+ pipeLayersLocked: true
+ - type: PipeAppearance
+ sprite:
+ - { sprite: Structures/Piping/Atmospherics/manifold.rsi, state: pipeConnector }
+ - { sprite: Structures/Piping/Atmospherics/manifold.rsi, state: pipeConnector_alt1 }
+ - { sprite: Structures/Piping/Atmospherics/manifold.rsi, state: pipeConnector_alt2 }
+ - type: Construction
+ graph: GasPipe
+ node: manifold
+ - type: Item
+ size: Small
+ shape:
+ - 0,0,1,0
+ storedSprite:
+ sprite: Structures/Piping/Atmospherics/manifold.rsi
+ state: storageManifold
+ - type: NodeContainer
+ nodes:
+ south0:
+ !type:PipeNode
+ nodeGroupID: Pipe
+ pipeDirection: South
+ pipeLayer: 0
+ south1:
+ !type:PipeNode
+ nodeGroupID: Pipe
+ pipeDirection: South
+ pipeLayer: 1
+ south2:
+ !type:PipeNode
+ nodeGroupID: Pipe
+ pipeDirection: South
+ pipeLayer: 2
+ north0:
+ !type:PipeNode
+ nodeGroupID: Pipe
+ pipeDirection: North
+ pipeLayer: 0
+ north1:
+ !type:PipeNode
+ nodeGroupID: Pipe
+ pipeDirection: North
+ pipeLayer: 1
+ north2:
+ !type:PipeNode
+ nodeGroupID: Pipe
+ pipeDirection: North
+ pipeLayer: 2
+ - type: GasPipeManifold
+ - type: AtmosMonitoringConsoleDevice
+ navMapBlip: GasPipeManifold
+ showAbsentConnections: false
+ - type: AtmosDevice
+ - type: Tag
+ tags:
+ - Unstackable
sprite: Structures/Piping/Atmospherics/vent.rsi
layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi
- state: pipeHalf
+ state: pipeUnaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ]
- state: vent_off
map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ]
sprite: Structures/Piping/Atmospherics/vent.rsi
layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi
- state: pipeHalf
+ state: pipeUnaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ]
- state: vent_passive
map: [ "enum.SubfloorLayers.FirstLayer" ]
sprite: Structures/Piping/Atmospherics/scrubber.rsi
layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi
- state: pipeHalf
+ state: pipeUnaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ]
- state: scrub_off
map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ]
drawdepth: FloorObjects
sprite: Structures/Piping/Atmospherics/outletinjector.rsi
layers:
- - state: pipeHalf
+ - state: pipeUnaryConnectors
sprite: Structures/Piping/Atmospherics/pipe.rsi
map: [ "enum.PipeVisualLayers.Pipe" ]
- state: injector
shader: unshaded
map: [ "enum.LightLayers.Unshaded" ]
color: "#990000"
+ - type: Appearance
- type: GenericVisualizer
visuals:
# toggle color of the unshaded light:
enum.LightLayers.Unshaded:
True: { color: "#5eff5e" }
False: { color: "#990000" }
- - type: Appearance
- type: PipeColorVisuals
- type: GasOutletInjector
- type: Construction
- type: Rotatable
- type: GasThermoMachine
- type: AtmosPipeColor
+ - type: AtmosPipeLayers
+ spriteLayersRsiPaths:
+ enum.PipeVisualLayers.Pipe:
+ Primary: Structures/Piping/Atmospherics/thermomachine.rsi
+ Secondary: Structures/Piping/Atmospherics/thermomachine_alt1.rsi
+ Tertiary: Structures/Piping/Atmospherics/thermomachine_alt2.rsi
- type: AtmosDevice
- type: UserInterface
interfaces:
map: ["enum.SolutionContainerLayers.Fill"]
visible: false
- state: trans
+ - type: Appearance
- type: GenericVisualizer
visuals:
enum.PowerDeviceVisuals.Powered:
- type: SolutionContainerVisuals
maxFillLevels: 7
fillBaseName: fill-
- - type: Appearance
- type: PipeColorVisuals
- type: Rotatable
- type: GasCondenser
- type: AtmosPipeColor
+ - type: AtmosPipeLayers
+ spriteLayersRsiPaths:
+ enum.PipeVisualLayers.Pipe:
+ Primary: Structures/Piping/Atmospherics/condenser.rsi
+ Secondary: Structures/Piping/Atmospherics/condenser_alt1.rsi
+ Tertiary: Structures/Piping/Atmospherics/condenser_alt2.rsi
- type: AtmosDevice
- type: PipeRestrictOverlap
- type: ApcPowerReceiver
- type: GuideHelp
guides:
- GasCondensing
+
amount: 1
doAfter: 1
+ - to: halfAlt1
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
+ - to: halfAlt2
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
- to: straight
steps:
- material: Steel
amount: 1
doAfter: 1
+ - to: straightAlt1
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
+ - to: straightAlt2
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
- to: bend
steps:
- material: Steel
amount: 1
doAfter: 1
+ - to: bendAlt1
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
+ - to: bendAlt2
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
- to: tjunction
steps:
- material: Steel
amount: 1
doAfter: 1
+ - to: tjunctionAlt1
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
+ - to: tjunctionAlt2
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
- to: fourway
steps:
- material: Steel
amount: 1
doAfter: 1
+ - to: fourwayAlt1
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
+ - to: fourwayAlt2
+ steps:
+ - material: Steel
+ amount: 1
+ doAfter: 1
+
+ - to: manifold
+ steps:
+ - material: Steel
+ amount: 2
+ doAfter: 1
+
- node: half
entity: GasPipeHalf
edges:
- tool: Welding
doAfter: 1
+ - node: halfAlt1
+ entity: GasPipeHalfAlt1
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
+ - node: halfAlt2
+ entity: GasPipeHalfAlt2
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
- node: straight
entity: GasPipeStraight
edges:
- tool: Welding
doAfter: 1
+ - node: straightAlt1
+ entity: GasPipeStraightAlt1
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
+ - node: straightAlt2
+ entity: GasPipeStraightAlt2
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
- node: bend
entity: GasPipeBend
edges:
- tool: Welding
doAfter: 1
+ - node: bendAlt1
+ entity: GasPipeBendAlt1
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
+ - node: bendAlt2
+ entity: GasPipeBendAlt2
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
- node: tjunction
entity: GasPipeTJunction
edges:
- tool: Welding
doAfter: 1
+ - node: tjunctionAlt1
+ entity: GasPipeTJunctionAlt1
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
+ - node: tjunctionAlt2
+ entity: GasPipeTJunctionAlt2
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
- node: fourway
entity: GasPipeFourway
edges:
- tool: Welding
doAfter: 1
+ - node: fourwayAlt1
+ entity: GasPipeFourwayAlt1
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
+ - node: fourwayAlt2
+ entity: GasPipeFourwayAlt2
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 1
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
+
- node: broken
entity: GasPipeBroken
edges:
steps:
- tool: Welding
doAfter: 1
+
+ - node: manifold
+ entity: GasPipeManifold
+ edges:
+ - to: start
+ conditions:
+ - !type:EntityAnchored
+ anchored: false
+ completed:
+ - !type:SpawnPrototype
+ prototype: SheetSteel1
+ amount: 2
+ - !type:DeleteEntity
+ steps:
+ - tool: Welding
+ doAfter: 1
startNode: start
targetNode: half
category: construction-category-utilities
- placementMode: SnapgridCenter
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeHalf
+ - GasPipeHalfAlt1
+ - GasPipeHalfAlt2
+
+- type: construction
+ id: GasPipeHalfAlt1
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: halfAlt1
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeHalf
+ - GasPipeHalfAlt1
+ - GasPipeHalfAlt2
+
+- type: construction
+ id: GasPipeHalfAlt2
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: halfAlt2
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeHalf
+ - GasPipeHalfAlt1
+ - GasPipeHalfAlt2
- type: construction
id: GasPipeStraight
startNode: start
targetNode: straight
category: construction-category-utilities
- placementMode: SnapgridCenter
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeStraight
+ - GasPipeStraightAlt1
+ - GasPipeStraightAlt2
+
+- type: construction
+ id: GasPipeStraightAlt1
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: straightAlt1
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeStraight
+ - GasPipeStraightAlt1
+ - GasPipeStraightAlt2
+
+- type: construction
+ id: GasPipeStraightAlt2
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: straightAlt2
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeStraight
+ - GasPipeStraightAlt1
+ - GasPipeStraightAlt2
- type: construction
id: GasPipeBend
startNode: start
targetNode: bend
category: construction-category-utilities
- placementMode: SnapgridCenter
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeBend
+ - GasPipeBendAlt1
+ - GasPipeBendAlt2
+
+- type: construction
+ id: GasPipeBendAlt1
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: bendAlt1
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeBend
+ - GasPipeBendAlt1
+ - GasPipeBendAlt2
+
+- type: construction
+ id: GasPipeBendAlt2
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: bendAlt2
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeBend
+ - GasPipeBendAlt1
+ - GasPipeBendAlt2
- type: construction
id: GasPipeTJunction
startNode: start
targetNode: tjunction
category: construction-category-utilities
- placementMode: SnapgridCenter
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeTJunction
+ - GasPipeTJunctionAlt1
+ - GasPipeTJunctionAlt2
+
+- type: construction
+ id: GasPipeTJunctionAlt1
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: tjunctionAlt1
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeTJunction
+ - GasPipeTJunctionAlt1
+ - GasPipeTJunctionAlt2
+
+- type: construction
+ id: GasPipeTJunctionAlt2
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: tjunctionAlt2
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeTJunction
+ - GasPipeTJunctionAlt1
+ - GasPipeTJunctionAlt2
- type: construction
id: GasPipeFourway
startNode: start
targetNode: fourway
category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeFourway
+ - GasPipeFourwayAlt1
+ - GasPipeFourwayAlt2
+
+- type: construction
+ id: GasPipeFourwayAlt1
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: fourwayAlt1
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeFourway
+ - GasPipeFourwayAlt1
+ - GasPipeFourwayAlt2
+
+- type: construction
+ id: GasPipeFourwayAlt2
+ hide: true
+ graph: GasPipe
+ startNode: start
+ targetNode: fourwayAlt2
+ category: construction-category-utilities
+ placementMode: AlignAtmosPipeLayers
+ canBuildInImpassable: true
+ alternativePrototypes:
+ - GasPipeFourway
+ - GasPipeFourwayAlt1
+ - GasPipeFourwayAlt2
+
+- type: construction
+ id: GasPipeManifold
+ graph: GasPipe
+ startNode: start
+ targetNode: manifold
+ category: construction-category-utilities
placementMode: SnapgridCenter
canBuildInImpassable: true
copyright: "Created by chromiumboy"
source: "https://github.com/chromiumboy"
+- files: ["beveled_rectangle_east_west.png"]
+ license: "CC-BY-SA-3.0"
+ copyright: "Created by chromiumboy"
+ source: "https://github.com/chromiumboy"
+
+- files: ["beveled_rectangle_north_south.png"]
+ license: "CC-BY-SA-3.0"
+ copyright: "Created by chromiumboy"
+ source: "https://github.com/chromiumboy"
+
- files: ["beveled_square.png"]
license: "CC-BY-SA-3.0"
copyright: "Created by chromiumboy"
--- /dev/null
+sample:
+ filter: true
--- /dev/null
+sample:
+ filter: true
bubbles.svg by Lorc under CC BY 3.0
https://game-icons.net/1x1/lorc/bubbles.html
+
+screwdriver.png by Lorc (edited by chromiumboy) under CC BY 3.0
+https://game-icons.net/1x1/lorc/screwdriver.html
--- /dev/null
+sample:
+ filter: true
{
- "version":1,
- "size":
- {
- "x":32,
- "y":32
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
},
- "copyright":"Created by EmoGarbage404 (github) for Space Station 14.",
- "license":"CC0-1.0",
- "states":
- [
+ "copyright": "Created by EmoGarbage404 (github) for Space Station 14, edited by chromiumboy.",
+ "license": "CC0-1.0",
+ "states": [
{
- "name":"off"
+ "name": "off"
},
{
- "name":"on",
- "delays":
- [
+ "name": "on",
+ "delays": [
[
0.05,
0.05,
]
},
{
- "name":"panel"
+ "name": "panel"
},
{
- "name":"trans"
+ "name": "trans"
},
{
- "name":"pipe",
- "directions":4
+ "name": "pipe",
+ "directions": 4
},
{
- "name":"display"
+ "name": "display"
},
{
- "name":"fill-1"
+ "name": "fill-1"
},
{
- "name":"fill-2"
+ "name": "fill-2"
},
{
- "name":"fill-3"
+ "name": "fill-3"
},
{
- "name":"fill-4"
+ "name": "fill-4"
},
{
- "name":"fill-5"
+ "name": "fill-5"
},
{
- "name":"fill-6"
+ "name": "fill-6"
},
{
- "name":"fill-7"
+ "name": "fill-7"
}
]
}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "copyright": "Created by EmoGarbage404 (github) for Space Station 14, edited by chromiumboy.",
+ "license": "CC0-1.0",
+ "states": [
+ {
+ "name": "pipe",
+ "directions": 4
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "copyright": "Created by EmoGarbage404 (github) for Space Station 14, edited by chromiumboy.",
+ "license": "CC0-1.0",
+ "states": [
+ {
+ "name": "pipe",
+ "directions": 4
+ }
+ ]
+}
{
"name": "icon"
},
- {
- "name": "base"
+ {
+ "name": "base",
+ "directions": 4
},
- {
+ {
"name": "blank"
},
{
"name": "lights",
+ "directions": 4,
"delays": [
+ [
+ 1.0,
+ 0.25
+ ],
+ [
+ 1.0,
+ 0.25
+ ],
+ [
+ 1.0,
+ 0.25
+ ],
[
1.0,
0.25
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Created by chromiumboy (github) for SS14, based on the digital valve from /tg/, taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da.",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "base",
+ "directions": 4
+ },
+ {
+ "name": "blank"
+ },
+ {
+ "name": "lights",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0,
+ 0.25
+ ],
+ [
+ 1.0,
+ 0.25
+ ],
+ [
+ 1.0,
+ 0.25
+ ],
+ [
+ 1.0,
+ 0.25
+ ]
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Created by chromiumboy (github) for SS14, based on the digital valve from /tg/, taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da.",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "icon"
+ },
+ {
+ "name": "base",
+ "directions": 4
+ },
+ {
+ "name": "blank"
+ },
+ {
+ "name": "lights",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0,
+ 0.25
+ ],
+ [
+ 1.0,
+ 0.25
+ ],
+ [
+ 1.0,
+ 0.25
+ ],
+ [
+ 1.0,
+ 0.25
+ ]
+ ]
+ }
+ ]
+}
{
- "version":1,
- "size":{"x":32,"y":32},
- "copyright":"Base sprites taken from tgstation, split to display on two layers (machinebody/panel) by Menshin, and recolored and edited by EmoGarbage404 (github)",
- "license":"CC-BY-SA-3.0",
- "states":[
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "copyright": "Base sprites taken from tgstation, split to display on two layers (machinebody/panel) by Menshin, and recolored and edited by EmoGarbage404 (github) and chromiumboy (github)",
+ "license": "CC-BY-SA-3.0",
+ "states": [
{
- "name":"freezerOff",
- "directions":1
+ "name": "freezerOff",
+ "directions": 1
},
{
- "name":"freezerPanelOpen",
- "directions":1
+ "name": "freezerPanelOpen",
+ "directions": 1
},
{
- "name":"freezerOn",
- "directions":1,
- "delays":[ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] ]
+ "name": "freezerOn",
+ "directions": 1,
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
},
{
- "name":"heaterOff",
- "directions":1
+ "name": "heaterOff",
+ "directions": 1
},
{
- "name":"heaterPanelOpen",
- "directions":1
+ "name": "heaterPanelOpen",
+ "directions": 1
},
{
- "name":"heaterOn",
- "directions":1,
- "delays":[ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] ]
+ "name": "heaterOn",
+ "directions": 1,
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
},
{
- "name":"pipe",
- "directions":4
+ "name": "pipe",
+ "directions": 4
}
]
}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "copyright": "Base sprites taken from tgstation at https://github.com/tgstation/tgstation/commit/662c08272acd7be79531550919f56f846726eabb and edited by chromiumboy (github)",
+ "license": "CC-BY-SA-3.0",
+ "states": [
+ {
+ "name": "pipeManifold",
+ "directions": 4
+ },
+ {
+ "name": "storageManifold"
+ },
+ {
+ "name": "pipeConnector",
+ "directions": 4
+ },
+ {
+ "name": "pipeConnector_alt1",
+ "directions": 4
+ },
+ {
+ "name": "pipeConnector_alt2",
+ "directions": 4
+ }
+ ]
+}
{
- "version":1,
- "size":{
- "x":32,
- "y":32
- },
- "license":"CC-BY-SA-3.0",
- "copyright":"Inhand sprites by alzore_(discord) for SS14. pipeTrinaryConnectors made by Menshin for SS14 based on pipeTJunction, the rest is taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da.",
- "states":[
- {
- "name": "inhand-left",
- "directions":4
- },
- {
- "name":"inhand-right",
- "directions":4
- },
- {
- "name": "Bend-inhand-left",
- "directions":4
- },
- {
- "name":"Bend-inhand-right",
- "directions":4
- },
- {
- "name": "TJunction-inhand-left",
- "directions":4
- },
- {
- "name":"TJunction-inhand-right",
- "directions":4
- },
- {
- "name": "Fourway-inhand-left",
- "directions":4
- },
- {
- "name":"Fourway-inhand-right",
- "directions":4
- },
- {
- "name":"pipeBroken",
- "directions":1
- },
- {
- "name":"pipeTJunction",
- "directions":4
- },
- {
- "name":"pipeHalf",
- "directions":4
- },
- {
- "name":"pipeBend",
- "directions":4
- },
- {
- "name": "pipeFourway",
- "directions":4
- },
- {
- "name":"pipeStraight",
- "directions":4
- },
- {
- "name":"pipeConnector",
- "directions":4
- },
- {
- "name":"pipeTrinaryConnectors",
- "directions":4
- },
- {
- "name":"storageStraight",
- "directions":4
- },
- {
- "name":"storageBend",
- "directions":4
- },
- {
- "name":"storageTJunction",
- "directions":4
- }
- ]
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Inhand sprites by alzore_(discord) for SS14. pipeTrinaryConnectors made by Menshin for SS14 based on pipeTJunction, the rest is taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da, edited by chromiumboy.",
+ "states": [
+ {
+ "name": "inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "inhand-right",
+ "directions": 4
+ },
+ {
+ "name": "Bend-inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "Bend-inhand-right",
+ "directions": 4
+ },
+ {
+ "name": "TJunction-inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "TJunction-inhand-right",
+ "directions": 4
+ },
+ {
+ "name": "Fourway-inhand-left",
+ "directions": 4
+ },
+ {
+ "name": "Fourway-inhand-right",
+ "directions": 4
+ },
+ {
+ "name": "pipeBroken",
+ "directions": 1
+ },
+ {
+ "name": "pipeTJunction",
+ "directions": 4
+ },
+ {
+ "name": "pipeHalf",
+ "directions": 4
+ },
+ {
+ "name": "pipeBend",
+ "directions": 4
+ },
+ {
+ "name": "pipeFourway",
+ "directions": 4
+ },
+ {
+ "name": "pipeStraight",
+ "directions": 4
+ },
+ {
+ "name": "pipeConnector",
+ "directions": 4
+ },
+ {
+ "name": "pipeUnaryConnectors",
+ "directions": 4
+ },
+ {
+ "name": "pipeBinaryConnectors",
+ "directions": 4
+ },
+ {
+ "name": "pipeTrinaryConnectors",
+ "directions": 4
+ },
+ {
+ "name": "storageStraight"
+ },
+ {
+ "name": "storageHalf"
+ },
+ {
+ "name": "storageBend"
+ },
+ {
+ "name": "storageTJunction"
+ },
+ {
+ "name": "storageFourway"
+ }
+ ]
}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Inhand sprites by alzore_(discord) for SS14. pipeTrinaryConnectors made by Menshin for SS14 based on pipeTJunction, the rest is taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da, edited by chromiumboy.",
+ "states": [
+ {
+ "name": "pipeBroken",
+ "directions": 1
+ },
+ {
+ "name": "pipeTJunction",
+ "directions": 4
+ },
+ {
+ "name": "pipeHalf",
+ "directions": 4
+ },
+ {
+ "name": "pipeBend",
+ "directions": 4
+ },
+ {
+ "name": "pipeFourway",
+ "directions": 4
+ },
+ {
+ "name": "pipeStraight",
+ "directions": 4
+ },
+ {
+ "name": "pipeConnector",
+ "directions": 4
+ },
+ {
+ "name": "pipeUnaryConnectors",
+ "directions": 4
+ },
+ {
+ "name": "pipeBinaryConnectors",
+ "directions": 4
+ },
+ {
+ "name": "pipeTrinaryConnectors",
+ "directions": 4
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Inhand sprites by alzore_(discord) for SS14. pipeTrinaryConnectors made by Menshin for SS14 based on pipeTJunction, the rest is taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da, edited by chromiumboy.",
+ "states": [
+ {
+ "name": "pipeBroken",
+ "directions": 1
+ },
+ {
+ "name": "pipeTJunction",
+ "directions": 4
+ },
+ {
+ "name": "pipeHalf",
+ "directions": 4
+ },
+ {
+ "name": "pipeBend",
+ "directions": 4
+ },
+ {
+ "name": "pipeFourway",
+ "directions": 4
+ },
+ {
+ "name": "pipeStraight",
+ "directions": 4
+ },
+ {
+ "name": "pipeConnector",
+ "directions": 4
+ },
+ {
+ "name": "pipeUnaryConnectors",
+ "directions": 4
+ },
+ {
+ "name": "pipeBinaryConnectors",
+ "directions": 4
+ },
+ {
+ "name": "pipeTrinaryConnectors",
+ "directions": 4
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da. Signal valve is a digital valve modified by deltanedas. Manual valve modified by Deerstop at https://github.com/space-wizards/space-station-14/pull/34378. Modified by chromiumboy.",
+ "states": [
+ {
+ "name": "pumpDigitalValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpManualValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpManualValveOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpSignalValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpSignalValveOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpPassiveGate",
+ "directions": 4
+ },
+ {
+ "name": "pumpPassiveGateOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressure",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressureOn",
+ "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
+ ]
+ ]
+ },
+ {
+ "name": "pumpVolume",
+ "directions": 4
+ },
+ {
+ "name": "pumpVolumeOn",
+ "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,
+ 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": "pumpVolumeBlocked",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ]
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da. Signal valve is a digital valve modified by deltanedas. Manual valve modified by Deerstop at https://github.com/space-wizards/space-station-14/pull/34378. Modified by chromiumboy.",
+ "states": [
+ {
+ "name": "pumpDigitalValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpManualValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpManualValveOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpSignalValve",
+ "directions": 4
+ },
+ {
+ "name": "pumpSignalValveOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpPassiveGate",
+ "directions": 4
+ },
+ {
+ "name": "pumpPassiveGateOn",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressure",
+ "directions": 4
+ },
+ {
+ "name": "pumpPressureOn",
+ "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
+ ]
+ ]
+ },
+ {
+ "name": "pumpVolume",
+ "directions": 4
+ },
+ {
+ "name": "pumpVolumeOn",
+ "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,
+ 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": "pumpVolumeBlocked",
+ "directions": 4,
+ "delays": [
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ],
+ [
+ 1.0,
+ 1.0
+ ]
+ ]
+ }
+ ]
+}
{
- "version":1,
- "size":{"x":32,"y":32},
- "copyright":"Base sprites taken from tgstation, splitted to display on two layers (machinebody/panel) by Menshin, for SS14",
- "license":"CC-BY-SA-3.0",
- "states":[
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "copyright": "Base sprites taken from tgstation, splitted to display on two layers (machinebody/panel) by Menshin, for SS14, edited by chromiumboy",
+ "license": "CC-BY-SA-3.0",
+ "states": [
{
- "name":"freezerOff",
- "directions":1
+ "name": "freezerOff",
+ "directions": 1
},
{
- "name":"freezerPanelOpen",
- "directions":1
+ "name": "freezerPanelOpen",
+ "directions": 1
},
{
- "name":"freezerOn",
- "directions":1,
- "delays":[ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] ]
+ "name": "freezerOn",
+ "directions": 1,
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
},
{
- "name":"heaterOff",
- "directions":1
+ "name": "heaterOff",
+ "directions": 1
},
{
- "name":"heaterPanelOpen",
- "directions":1
+ "name": "heaterPanelOpen",
+ "directions": 1
},
{
- "name":"heaterOn",
- "directions":1,
- "delays":[ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] ]
+ "name": "heaterOn",
+ "directions": 1,
+ "delays": [
+ [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ ]
},
{
- "name":"pipe",
- "directions":4
+ "name": "pipe",
+ "directions": 4
}
]
-}
\ No newline at end of file
+}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "copyright": "Base sprites taken from tgstation, splitted to display on two layers (machinebody/panel) by Menshin, for SS14, edited by chromiumboy",
+ "license": "CC-BY-SA-3.0",
+ "states": [
+ {
+ "name": "pipe",
+ "directions": 4
+ }
+ ]
+}
--- /dev/null
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "copyright": "Base sprites taken from tgstation, splitted to display on two layers (machinebody/panel) by Menshin, for SS14, edited by chromiumboy",
+ "license": "CC-BY-SA-3.0",
+ "states": [
+ {
+ "name": "pipe",
+ "directions": 4
+ }
+ ]
+}