Layering for atmospheric pipes (#36124)

Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
Co-authored-by: Kevin Zheng <kevinz5000@gmail.com>
This commit is contained in:
chromiumboy
2025-06-02 00:01:43 -05:00
committed by GitHub
parent aa5ca90a3b
commit 45012cbe1d
134 changed files with 2671 additions and 306 deletions

View File

@@ -0,0 +1,203 @@
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);
}
}

View File

@@ -17,6 +17,10 @@ public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
public int? FocusNetId = null; public int? FocusNetId = null;
private const int ChunkSize = 4; 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 _basePipeNetColor = Color.LightGray;
private readonly Color _unfocusedPipeNetColor = Color.DimGray; private readonly Color _unfocusedPipeNetColor = Color.DimGray;
@@ -95,23 +99,23 @@ public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
foreach (var chunkedLine in atmosPipeNetwork) foreach (var chunkedLine in atmosPipeNetwork)
{ {
var leftTop = ScalePosition(new Vector2 var leftTop = ScalePosition(new Vector2
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f, (Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - LineThickness,
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f) Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - LineThickness)
- offset); - offset);
var rightTop = ScalePosition(new Vector2 var rightTop = ScalePosition(new Vector2
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f, (Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + LineThickness,
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f) Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - LineThickness)
- offset); - offset);
var leftBottom = ScalePosition(new Vector2 var leftBottom = ScalePosition(new Vector2
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f, (Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - LineThickness,
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f) Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + LineThickness)
- offset); - offset);
var rightBottom = ScalePosition(new Vector2 var rightBottom = ScalePosition(new Vector2
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f, (Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + LineThickness,
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f) Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + LineThickness)
- offset); - offset);
if (!pipeVertexUVs.TryGetValue(chunkedLine.Color, out var pipeVertexUV)) if (!pipeVertexUVs.TryGetValue(chunkedLine.Color, out var pipeVertexUV))
@@ -158,7 +162,7 @@ public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
{ {
var list = new List<AtmosMonitoringConsoleLine>(); 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 // Determine the correct coloration for the pipe
var color = Color.FromHex(hexColor) * _basePipeNetColor; var color = Color.FromHex(hexColor) * _basePipeNetColor;
@@ -191,6 +195,9 @@ public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
_vertLinesReversed[color] = vertLinesReversed; _vertLinesReversed[color] = vertLinesReversed;
} }
var layerFraction = _layerFraction[(int)layer];
var origin = new Vector2(grid.TileSize * layerFraction, -grid.TileSize * layerFraction);
// Loop over the chunk // Loop over the chunk
for (var tileIdx = 0; tileIdx < ChunkSize * ChunkSize; tileIdx++) for (var tileIdx = 0; tileIdx < ChunkSize * ChunkSize; tileIdx++)
{ {
@@ -208,21 +215,22 @@ public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
// Calculate the draw point offsets // Calculate the draw point offsets
var vertLineOrigin = (atmosPipeData & northMask << tileIdx * SharedNavMapSystem.Directions) > 0 ? 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 ? 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 ? 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 ? 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, // Scale up the vectors and convert to vector2i so we can merge them
// double the vectors and convert to vector2i so we can merge them AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + horizLineOrigin, ScaleModifier),
AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + horizLineOrigin, 2), ConvertVector2ToVector2i(tile + horizLineTerminus, 2), horizLines, horizLinesReversed); ConvertVector2ToVector2i(tile + horizLineTerminus, ScaleModifier), horizLines, horizLinesReversed);
AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + vertLineOrigin, 2), ConvertVector2ToVector2i(tile + vertLineTerminus, 2), vertLines, vertLinesReversed); AddOrUpdateNavMapLine(ConvertVector2ToVector2i(tile + vertLineOrigin, ScaleModifier),
ConvertVector2ToVector2i(tile + vertLineTerminus, ScaleModifier), vertLines, vertLinesReversed);
} }
} }
} }
@@ -235,7 +243,7 @@ public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
foreach (var (origin, terminal) in horizLines) foreach (var (origin, terminal) in horizLines)
decodedOutput.Add(new AtmosMonitoringConsoleLine 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 (color, vertLines) in _vertLines)
@@ -245,7 +253,7 @@ public sealed partial class AtmosMonitoringConsoleNavMapControl : NavMapControl
foreach (var (origin, terminal) in vertLines) foreach (var (origin, terminal) in vertLines)
decodedOutput.Add(new AtmosMonitoringConsoleLine decodedOutput.Add(new AtmosMonitoringConsoleLine
(ConvertVector2iToVector2(origin, 0.5f), ConvertVector2iToVector2(terminal, 0.5f), sRGB)); (ConvertVector2iToVector2(origin, 1f / ScaleModifier), ConvertVector2iToVector2(terminal, 1f / ScaleModifier), sRGB));
} }
return decodedOutput; return decodedOutput;

View File

@@ -15,7 +15,7 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS
private void OnHandleState(EntityUid uid, AtmosMonitoringConsoleComponent component, ref ComponentHandleState args) 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; Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices;
switch (args.Current) switch (args.Current)
@@ -54,7 +54,7 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS
foreach (var (origin, chunk) in modifiedChunks) foreach (var (origin, chunk) in modifiedChunks)
{ {
var newChunk = new AtmosPipeChunk(origin); var newChunk = new AtmosPipeChunk(origin);
newChunk.AtmosPipeData = new Dictionary<(int, string), ulong>(chunk); newChunk.AtmosPipeData = new Dictionary<AtmosMonitoringConsoleSubnet, ulong>(chunk);
component.AtmosPipeChunks[origin] = newChunk; component.AtmosPipeChunks[origin] = newChunk;
} }

View File

@@ -13,6 +13,7 @@ using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Numerics;
namespace Content.Client.Atmos.Consoles; namespace Content.Client.Atmos.Consoles;
@@ -33,6 +34,8 @@ public sealed partial class AtmosMonitoringConsoleWindow : FancyWindow
private ProtoId<NavMapBlipPrototype> _navMapConsoleProtoId = "NavMapConsole"; private ProtoId<NavMapBlipPrototype> _navMapConsoleProtoId = "NavMapConsole";
private ProtoId<NavMapBlipPrototype> _gasPipeSensorProtoId = "GasPipeSensor"; 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) public AtmosMonitoringConsoleWindow(AtmosMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -238,6 +241,10 @@ public sealed partial class AtmosMonitoringConsoleWindow : FancyWindow
var blinks = proto.Blinks || _focusEntity == metaData.NetEntity; var blinks = proto.Blinks || _focusEntity == metaData.NetEntity;
var coords = _entManager.GetCoordinates(metaData.NetCoordinates); 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); var blip = new NavMapBlip(coords, _spriteSystem.Frame0(new SpriteSpecifier.Texture(texture)), color, blinks, proto.Selectable, proto.Scale);
NavMap.TrackedEntities[metaData.NetEntity] = blip; NavMap.TrackedEntities[metaData.NetEntity] = blip;
} }

View File

@@ -1,6 +1,7 @@
using Content.Client.SubFloor; using Content.Client.SubFloor;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Piping; using Content.Shared.Atmos.Piping;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -8,7 +9,7 @@ using Robust.Client.GameObjects;
namespace Content.Client.Atmos.EntitySystems; namespace Content.Client.Atmos.EntitySystems;
[UsedImplicitly] [UsedImplicitly]
public sealed class AtmosPipeAppearanceSystem : EntitySystem public sealed partial class AtmosPipeAppearanceSystem : SharedAtmosPipeAppearanceSystem
{ {
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!; [Dependency] private readonly SpriteSystem _sprite = default!;
@@ -26,28 +27,39 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
if (!TryComp(uid, out SpriteComponent? sprite)) if (!TryComp(uid, out SpriteComponent? sprite))
return; return;
var numberOfPipeLayers = GetNumberOfPipeLayers(uid, out _);
foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>()) foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>())
{ {
var layer = _sprite.LayerMapReserve((uid, sprite), layerKey); for (byte i = 0; i < numberOfPipeLayers; i++)
_sprite.LayerSetRsi((uid, sprite), layer, component.Sprite.RsiPath); {
_sprite.LayerSetRsiState((uid, sprite), layer, component.Sprite.RsiState); 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)); _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; var sprite = entity.Comp;
foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>()) foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>())
{ {
if (!_sprite.LayerMapTryGet(entity.AsNullable(), layerKey, out var key, false)) for (byte i = 0; i < numberOfPipeLayers; i++)
{
var layerName = layerKey.ToString() + i.ToString();
if (!_sprite.LayerMapTryGet(entity.AsNullable(), layerName, out var key, false))
continue; continue;
var layer = sprite[key]; var layer = sprite[key];
layer.Visible = false; layer.Visible = false;
} }
} }
}
private void OnAppearanceChanged(EntityUid uid, PipeAppearanceComponent component, ref AppearanceChangeEvent args) private void OnAppearanceChanged(EntityUid uid, PipeAppearanceComponent component, ref AppearanceChangeEvent args)
{ {
@@ -61,21 +73,32 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
return; 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; return;
} }
if (!_appearance.TryGetData<Color>(uid, PipeColorVisuals.Color, out var color, args.Component)) if (!_appearance.TryGetData<Color>(uid, PipeColorVisuals.Color, out var color, args.Component))
color = Color.White; color = Color.White;
// transform connected directions to local-coordinates for (byte i = 0; i < numberOfPipeLayers; i++)
var connectedDirections = worldConnectedDirections.RotatePipeDirection(-Transform(uid).LocalRotation); {
// 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>()) foreach (var layerKey in Enum.GetValues<PipeConnectionLayer>())
{ {
if (!_sprite.LayerMapTryGet((uid, args.Sprite), layerKey, out var key, false)) var layerName = layerKey.ToString() + i.ToString();
if (!_sprite.LayerMapTryGet((uid, args.Sprite), layerName, out var key, false))
continue; continue;
var layer = args.Sprite[key]; var layer = args.Sprite[key];
@@ -90,6 +113,7 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
layer.Color = color; layer.Color = color;
} }
} }
}
private SpriteComponent.DirectionOffset ToOffset(PipeConnectionLayer layer) private SpriteComponent.DirectionOffset ToOffset(PipeConnectionLayer layer)
{ {

View File

@@ -0,0 +1,56 @@
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);
}
}

View File

@@ -136,6 +136,7 @@ namespace Content.Client.Atmos.UI
else else
{ {
// oh shit of fuck its more than 4 this ui isn't gonna look pretty anymore // 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++) for (var i = 1; i < msg.NodeGasMixes.Length; i++)
{ {
GenerateGasDisplay(msg.NodeGasMixes[i], CDeviceMixes); GenerateGasDisplay(msg.NodeGasMixes[i], CDeviceMixes);

View File

@@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using Content.Shared.Construction.Prototypes; using Content.Shared.Construction.Prototypes;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Placement; using Robust.Client.Placement;
@@ -13,6 +13,9 @@ namespace Content.Client.Construction
private readonly ConstructionSystem _constructionSystem; private readonly ConstructionSystem _constructionSystem;
private readonly ConstructionPrototype? _prototype; private readonly ConstructionPrototype? _prototype;
public ConstructionSystem? CurrentConstructionSystem { get { return _constructionSystem; } }
public ConstructionPrototype? CurrentPrototype { get { return _prototype; } }
public override bool CanRotate { get; } public override bool CanRotate { get; }
public ConstructionPlacementHijack(ConstructionSystem constructionSystem, ConstructionPrototype? prototype) public ConstructionPlacementHijack(ConstructionSystem constructionSystem, ConstructionPrototype? prototype)

View File

@@ -282,16 +282,11 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS
return false; return false;
var direction = xform.LocalRotation.GetCardinalDir(); 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)) device = new AtmosDeviceNavMapData(GetNetEntity(uid), GetNetCoordinates(xform.Coordinates), netId.Value, component.NavMapBlip.Value, direction, color, layer);
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);
return true; return true;
} }
@@ -371,7 +366,9 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS
if (!TryComp<NodeContainerComponent>(ent, out var entNodeContainer)) if (!TryComp<NodeContainerComponent>(ent, out var entNodeContainer))
continue; 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 // Add or update the chunk on the associated grid
@@ -393,7 +390,13 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS
} }
} }
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 // Entities that are actively being deleted are not to be drawn
if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating) if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
@@ -401,16 +404,19 @@ public sealed class AtmosMonitoringConsoleSystem : SharedAtmosMonitoringConsoleS
foreach ((var id, var node) in nodeContainer.Nodes) 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; continue;
var pipeNode = (PipeNode)node;
var netId = GetPipeNodeNetId(pipeNode); var netId = GetPipeNodeNetId(pipeNode);
var subnet = new AtmosMonitoringConsoleSubnet(netId, pipeNode.CurrentPipeLayer, pipeColor.Color.ToHex());
var pipeDirection = pipeNode.CurrentPipeDirection; 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; atmosPipeData |= (ulong)pipeDirection << tileIdx * SharedNavMapSystem.Directions;
chunk.AtmosPipeData[(netId, pipeColor.Color.ToHex())] = atmosPipeData; chunk.AtmosPipeData[subnet] = atmosPipeData;
} }
} }

View File

@@ -0,0 +1,77 @@
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);
}
}

View File

@@ -4,6 +4,7 @@ using Content.Server.NodeContainer;
using Content.Server.NodeContainer.Nodes; using Content.Server.NodeContainer.Nodes;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Construction.Components; using Content.Shared.Construction.Components;
using Content.Shared.NodeContainer; using Content.Shared.NodeContainer;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -97,27 +98,27 @@ public sealed class PipeRestrictOverlapSystem : EntitySystem
public bool PipeNodesOverlap(Entity<NodeContainerComponent, TransformComponent> ent, Entity<NodeContainerComponent, TransformComponent> other) public bool PipeNodesOverlap(Entity<NodeContainerComponent, TransformComponent> ent, Entity<NodeContainerComponent, TransformComponent> other)
{ {
var entDirs = GetAllDirections(ent).ToList(); var entDirsAndLayers = GetAllDirectionsAndLayers(ent).ToList();
var otherDirs = GetAllDirections(other).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 true;
} }
} }
return false; 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) 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. // 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) if (node is PipeNode pipeNode)
yield return pipeNode.OriginalPipeDirection.RotatePipeDirection(pipe.Comp2.LocalRotation); yield return (pipeNode.OriginalPipeDirection.RotatePipeDirection(pipe.Comp2.LocalRotation), pipeNode.CurrentPipeLayer);
} }
} }
} }

View File

@@ -0,0 +1,11 @@
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" };
}

View File

@@ -1,14 +1,14 @@
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes; using Content.Server.NodeContainer.Nodes;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.NodeContainer; using Content.Shared.NodeContainer;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
namespace Content.Server.Atmos.Piping.EntitySystems; 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 SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly SharedMapSystem _map = default!;
@@ -34,9 +34,12 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
if (!TryComp<MapGridComponent>(xform.GridUid, out var grid)) if (!TryComp<MapGridComponent>(xform.GridUid, out var grid))
return; return;
var numberOfPipeLayers = GetNumberOfPipeLayers(uid, out var atmosPipeLayers);
// get connected entities // get connected entities
var anyPipeNodes = false; var anyPipeNodes = false;
HashSet<EntityUid> connected = new(); HashSet<(EntityUid, AtmosPipeLayer)> connected = new();
foreach (var node in container.Nodes.Values) foreach (var node in container.Nodes.Values)
{ {
if (node is not PipeNode) if (node is not PipeNode)
@@ -46,8 +49,8 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
foreach (var connectedNode in node.ReachableNodes) foreach (var connectedNode in node.ReachableNodes)
{ {
if (connectedNode is PipeNode) if (connectedNode is PipeNode { } pipeNode)
connected.Add(connectedNode.Owner); connected.Add((connectedNode.Owner, pipeNode.CurrentPipeLayer));
} }
} }
@@ -55,14 +58,22 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
return; return;
// find the cardinal directions of any connected entities // find the cardinal directions of any connected entities
var netConnectedDirections = PipeDirection.None; var connectedDirections = new PipeDirection[numberOfPipeLayers];
var tile = _map.TileIndicesFor((xform.GridUid.Value, grid), xform.Coordinates); Array.Fill(connectedDirections, PipeDirection.None);
foreach (var neighbour 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);
netConnectedDirections |= (otherTile - tile) switch var tile = _map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates);
foreach (var (neighbour, pipeLayer) in connected)
{
var pipeIndex = (int)pipeLayer;
if (pipeIndex >= numberOfPipeLayers)
continue;
var otherTile = _map.TileIndicesFor(xform.GridUid.Value, grid, Transform(neighbour).Coordinates);
var pipeLayerDirections = connectedDirections[pipeIndex];
pipeLayerDirections |= (otherTile - tile) switch
{ {
(0, 1) => PipeDirection.North, (0, 1) => PipeDirection.North,
(0, -1) => PipeDirection.South, (0, -1) => PipeDirection.South,
@@ -70,8 +81,16 @@ public sealed class AtmosPipeAppearanceSystem : EntitySystem
(-1, 0) => PipeDirection.West, (-1, 0) => PipeDirection.West,
_ => PipeDirection.None _ => 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); _appearance.SetData(uid, PipeVisuals.VisualState, netConnectedDirections, appearance);
} }
} }

View File

@@ -0,0 +1,68 @@
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;
}
}
}

View File

@@ -1,10 +1,8 @@
using Content.Server.Atmos;
using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.NodeContainer; using Content.Shared.NodeContainer;
using Content.Shared.NodeContainer.NodeGroups;
using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -12,7 +10,7 @@ namespace Content.Server.NodeContainer.Nodes
{ {
/// <summary> /// <summary>
/// Connects with other <see cref="PipeNode"/>s whose <see cref="PipeDirection"/> /// Connects with other <see cref="PipeNode"/>s whose <see cref="PipeDirection"/>
/// correctly correspond. /// and <see cref="CurrentPipeLayer"/> correctly correspond.
/// </summary> /// </summary>
[DataDefinition] [DataDefinition]
[Virtual] [Virtual]
@@ -24,6 +22,12 @@ namespace Content.Server.NodeContainer.Nodes
[DataField("pipeDirection")] [DataField("pipeDirection")]
public PipeDirection OriginalPipeDirection; public PipeDirection OriginalPipeDirection;
/// <summary>
/// The *current* layer to which the pipe node is assigned.
/// </summary>
[DataField("pipeLayer")]
public AtmosPipeLayer CurrentPipeLayer = AtmosPipeLayer.Primary;
/// <summary> /// <summary>
/// The *current* pipe directions (accounting for rotation) /// The *current* pipe directions (accounting for rotation)
/// Used to check if this pipe can connect to another pipe in a given direction. /// Used to check if this pipe can connect to another pipe in a given direction.
@@ -204,6 +208,7 @@ namespace Content.Server.NodeContainer.Nodes
foreach (var pipe in PipesInDirection(pos, pipeDir, grid, nodeQuery)) foreach (var pipe in PipesInDirection(pos, pipeDir, grid, nodeQuery))
{ {
if (pipe.NodeGroupID == NodeGroupID if (pipe.NodeGroupID == NodeGroupID
&& pipe.CurrentPipeLayer == CurrentPipeLayer
&& pipe.CurrentPipeDirection.HasDirection(pipeDir.GetOpposite())) && pipe.CurrentPipeDirection.HasDirection(pipeDir.GetOpposite()))
{ {
yield return pipe; yield return pipe;

View File

@@ -0,0 +1,122 @@
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,
}

View File

@@ -5,6 +5,8 @@ namespace Content.Shared.Atmos.Components;
[RegisterComponent] [RegisterComponent]
public sealed partial class PipeAppearanceComponent : Component public sealed partial class PipeAppearanceComponent : Component
{ {
[DataField("sprite")] [DataField]
public SpriteSpecifier.Rsi Sprite = new(new("Structures/Piping/Atmospherics/pipe.rsi"), "pipeConnector"); 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")];
} }

View File

@@ -64,10 +64,10 @@ public struct AtmosPipeChunk(Vector2i origin)
/// <summary> /// <summary>
/// Bitmask look up for atmos pipes, 1 for occupied and 0 for empty. /// 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> /// </summary>
[ViewVariables] [ViewVariables]
public Dictionary<(int, string), ulong> AtmosPipeData = new(); public Dictionary<AtmosMonitoringConsoleSubnet, ulong> AtmosPipeData = new();
/// <summary> /// <summary>
/// The last game tick that the chunk was updated /// The last game tick that the chunk was updated
@@ -109,10 +109,21 @@ public struct AtmosDeviceNavMapData
/// </summary> /// </summary>
public Color PipeColor; public Color PipeColor;
/// <summary>
/// The pipe layer the entity is on
/// </summary>
public AtmosPipeLayer PipeLayer;
/// <summary> /// <summary>
/// Populate the atmos monitoring console nav map with a single entity /// Populate the atmos monitoring console nav map with a single entity
/// </summary> /// </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; NetEntity = netEntity;
NetCoordinates = netCoordinates; NetCoordinates = netCoordinates;
@@ -120,6 +131,7 @@ public struct AtmosDeviceNavMapData
NavMapBlip = navMapBlip; NavMapBlip = navMapBlip;
Direction = direction; Direction = direction;
PipeColor = pipeColor; PipeColor = pipeColor;
PipeLayer = pipeLayer;
} }
} }
@@ -216,6 +228,16 @@ public struct AtmosMonitoringConsoleEntry
} }
} }
/// <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 public enum AtmosPipeChunkDataFacing : byte
{ {
// Values represent bit shift offsets when retrieving data in the tile array. // Values represent bit shift offsets when retrieving data in the tile array.

View File

@@ -16,6 +16,14 @@ public sealed partial class AtmosMonitoringConsoleDeviceComponent : Component
/// entity on the atmos monitoring console nav map. /// entity on the atmos monitoring console nav map.
/// If null, no blip is drawn (i.e., null for pipes) /// If null, no blip is drawn (i.e., null for pipes)
/// </summary> /// </summary>
[DataField, ViewVariables] [DataField]
public ProtoId<NavMapBlipPrototype>? NavMapBlip = null; 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;
} }

View File

@@ -15,7 +15,7 @@ public abstract class SharedAtmosMonitoringConsoleSystem : EntitySystem
private void OnGetState(EntityUid uid, AtmosMonitoringConsoleComponent component, ref ComponentGetState args) 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? // Should this be a full component state or a delta-state?
if (args.FromTick <= component.CreationTick || component.ForceFullUpdate) if (args.FromTick <= component.CreationTick || component.ForceFullUpdate)
@@ -52,22 +52,22 @@ public abstract class SharedAtmosMonitoringConsoleSystem : EntitySystem
[Serializable, NetSerializable] [Serializable, NetSerializable]
protected sealed class AtmosMonitoringConsoleState( protected sealed class AtmosMonitoringConsoleState(
Dictionary<Vector2i, Dictionary<(int, string), ulong>> chunks, Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> chunks,
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices) Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices)
: ComponentState : ComponentState
{ {
public Dictionary<Vector2i, Dictionary<(int, string), ulong>> Chunks = chunks; public Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> Chunks = chunks;
public Dictionary<NetEntity, AtmosDeviceNavMapData> AtmosDevices = atmosDevices; public Dictionary<NetEntity, AtmosDeviceNavMapData> AtmosDevices = atmosDevices;
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]
protected sealed class AtmosMonitoringConsoleDeltaState( protected sealed class AtmosMonitoringConsoleDeltaState(
Dictionary<Vector2i, Dictionary<(int, string), ulong>> modifiedChunks, Dictionary<Vector2i, Dictionary<AtmosMonitoringConsoleSubnet, ulong>> modifiedChunks,
Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices, Dictionary<NetEntity, AtmosDeviceNavMapData> atmosDevices,
HashSet<Vector2i> allChunks) HashSet<Vector2i> allChunks)
: ComponentState, IComponentDeltaState<AtmosMonitoringConsoleState> : 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 Dictionary<NetEntity, AtmosDeviceNavMapData> AtmosDevices = atmosDevices;
public HashSet<Vector2i> AllChunks = allChunks; public HashSet<Vector2i> AllChunks = allChunks;
@@ -81,7 +81,7 @@ public abstract class SharedAtmosMonitoringConsoleSystem : EntitySystem
foreach (var (index, data) in ModifiedChunks) 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(); state.AtmosDevices.Clear();
@@ -93,7 +93,7 @@ public abstract class SharedAtmosMonitoringConsoleSystem : EntitySystem
public AtmosMonitoringConsoleState CreateNewFullState(AtmosMonitoringConsoleState state) 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) foreach (var (index, data) in state.Chunks)
{ {
@@ -101,10 +101,10 @@ public abstract class SharedAtmosMonitoringConsoleSystem : EntitySystem
continue; continue;
if (ModifiedChunks.ContainsKey(index)) if (ModifiedChunks.ContainsKey(index))
chunks[index] = new Dictionary<(int, string), ulong>(ModifiedChunks[index]); chunks[index] = new Dictionary<AtmosMonitoringConsoleSubnet, ulong>(ModifiedChunks[index]);
else 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)); return new AtmosMonitoringConsoleState(chunks, new(AtmosDevices));

View File

@@ -0,0 +1,18 @@
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;
}
}

View File

@@ -0,0 +1,264 @@
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());
}
}

View File

@@ -85,6 +85,12 @@ public sealed partial class ConstructionPrototype : IPrototype
[DataField] [DataField]
public ProtoId<ConstructionPrototype>? Mirror { get; private set; } 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; public IReadOnlyList<IConstructionCondition> Conditions => _conditions;
} }

View File

@@ -9,13 +9,15 @@ namespace Content.Shared.DrawDepth
/// <summary> /// <summary>
/// This is for sub-floors, the floors you see after prying off a tile. /// This is for sub-floors, the floors you see after prying off a tile.
/// </summary> /// </summary>
LowFloors = DrawDepthTag.Default - 18, LowFloors = DrawDepthTag.Default - 20,
// various entity types that require different // various entity types that require different
// draw depths, as to avoid hiding // draw depths, as to avoid hiding
#region SubfloorEntities #region SubfloorEntities
ThickPipe = DrawDepthTag.Default - 17, ThickPipe = DrawDepthTag.Default - 19,
ThickWire = DrawDepthTag.Default - 16, ThickWire = DrawDepthTag.Default - 18,
ThinPipeAlt2 = DrawDepthTag.Default - 17,
ThinPipeAlt1 = DrawDepthTag.Default - 16,
ThinPipe = DrawDepthTag.Default - 15, ThinPipe = DrawDepthTag.Default - 15,
ThinWire = DrawDepthTag.Default - 14, ThinWire = DrawDepthTag.Default - 14,
#endregion #endregion

View File

@@ -39,4 +39,17 @@ public sealed partial class NavMapBlipPrototype : IPrototype
/// </summary> /// </summary>
[DataField] [DataField]
public float Scale { get; private set; } = 1f; 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)
} }

View File

@@ -225,6 +225,7 @@ namespace Content.Shared.SubFloor
ScannerRevealed, ScannerRevealed,
} }
[Serializable, NetSerializable]
public enum SubfloorLayers : byte public enum SubfloorLayers : byte
{ {
FirstLayer FirstLayer

View File

@@ -85,5 +85,8 @@ namespace Content.Shared.Verbs
public static readonly VerbCategory SelectType = new("verb-categories-select-type", null); 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 PowerLevel = new("verb-categories-power-level", null);
public static readonly VerbCategory Adjust =
new("verb-categories-adjust", "/Textures/Interface/VerbIcons/screwdriver.png");
} }
} }

View File

@@ -0,0 +1,10 @@
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.

View File

@@ -28,6 +28,7 @@ verb-categories-lever = Lever
verb-categories-select-type = Select Type verb-categories-select-type = Select Type
verb-categories-fax = Set Destination verb-categories-fax = Set Destination
verb-categories-power-level = Power Level verb-categories-power-level = Power Level
verb-categories-adjust = Adjust
verb-common-toggle-light = Toggle light verb-common-toggle-light = Toggle light
verb-common-close = Close verb-common-close = Close

View File

@@ -13,17 +13,18 @@
color: "#ffcd00" color: "#ffcd00"
texturePaths: texturePaths:
- "/Textures/Interface/NavMap/beveled_star.png" - "/Textures/Interface/NavMap/beveled_star.png"
placement: Offset
- type: navMapBlip - type: navMapBlip
id: GasVentOpening id: GasVentOpening
scale: 0.6667 scale: 0.75
color: LightGray color: LightGray
texturePaths: texturePaths:
- "/Textures/Interface/NavMap/beveled_square.png" - "/Textures/Interface/NavMap/beveled_square.png"
- type: navMapBlip - type: navMapBlip
id: GasVentScrubber id: GasVentScrubber
scale: 0.6667 scale: 0.75
color: LightGray color: LightGray
texturePaths: texturePaths:
- "/Textures/Interface/NavMap/beveled_circle.png" - "/Textures/Interface/NavMap/beveled_circle.png"
@@ -37,6 +38,7 @@
- "/Textures/Interface/NavMap/beveled_arrow_east.png" - "/Textures/Interface/NavMap/beveled_arrow_east.png"
- "/Textures/Interface/NavMap/beveled_arrow_north.png" - "/Textures/Interface/NavMap/beveled_arrow_north.png"
- "/Textures/Interface/NavMap/beveled_arrow_west.png" - "/Textures/Interface/NavMap/beveled_arrow_west.png"
placement: Offset
- type: navMapBlip - type: navMapBlip
id: GasValve id: GasValve
@@ -47,10 +49,21 @@
- "/Textures/Interface/NavMap/beveled_diamond_east_west.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" - "/Textures/Interface/NavMap/beveled_diamond_east_west.png"
placement: Offset
- type: navMapBlip - type: navMapBlip
id: Thermoregulator id: Thermoregulator
scale: 0.6667 scale: 0.75
color: LightGray color: LightGray
texturePaths: texturePaths:
- "/Textures/Interface/NavMap/beveled_hexagon.png" - "/Textures/Interface/NavMap/beveled_hexagon.png"
- type: navMapBlip
id: GasPipeManifold
scale: 0.9
color: LightGray
texturePaths:
- "/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"

View File

@@ -0,0 +1,133 @@
## 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

View File

@@ -22,6 +22,11 @@
!type:PipeNode !type:PipeNode
nodeGroupID: Pipe nodeGroupID: Pipe
pipeDirection: South 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 - type: entity
parent: GasBinaryBase parent: GasBinaryBase
@@ -325,10 +330,12 @@
sprite: Structures/Piping/Atmospherics/gascanisterport.rsi sprite: Structures/Piping/Atmospherics/gascanisterport.rsi
layers: layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi - sprite: Structures/Piping/Atmospherics/pipe.rsi
state: pipeHalf state: pipeUnaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- state: gasCanisterPort - state: gasCanisterPort
map: [ "enum.SubfloorLayers.FirstLayer" ] map: [ "enum.SubfloorLayers.FirstLayer" ]
- type: AtmosPipeLayers
spriteRsiPaths: {}
- type: Appearance - type: Appearance
- type: PipeColorVisuals - type: PipeColorVisuals
- type: GasPort - type: GasPort
@@ -363,10 +370,12 @@
sprite: Structures/Piping/Atmospherics/vent.rsi sprite: Structures/Piping/Atmospherics/vent.rsi
layers: layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi - sprite: Structures/Piping/Atmospherics/pipe.rsi
state: pipeStraight state: pipeBinaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- state: vent_off - state: vent_off
map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ] map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ]
- type: AtmosPipeLayers
spriteRsiPaths: {}
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:
enum.VentPumpVisuals.State: enum.VentPumpVisuals.State:
@@ -423,18 +432,24 @@
sprite: Structures/Machines/gasrecycler.rsi sprite: Structures/Machines/gasrecycler.rsi
layers: layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi - sprite: Structures/Piping/Atmospherics/pipe.rsi
state: pipeStraight state: pipeBinaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- state: running - state: running
- state: unlit - state: unlit
shader: unshaded 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 - type: GenericVisualizer
visuals: visuals:
enum.PumpVisuals.Enabled: enum.PumpVisuals.Enabled:
enabled: enabled:
True: { state: running } True: { state: running }
False: { state: unlit } False: { state: unlit }
- type: Appearance
- type: PipeColorVisuals - type: PipeColorVisuals
- type: Rotatable - type: Rotatable
- type: GasRecycler - type: GasRecycler
@@ -494,6 +509,9 @@
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- state: heStraight - state: heStraight
map: [ "enum.SubfloorLayers.FirstLayer" ] map: [ "enum.SubfloorLayers.FirstLayer" ]
- type: AtmosPipeLayers
spriteRsiPaths: {}
numberOfPipeLayers: 1
- type: SubFloorHide - type: SubFloorHide
visibleLayers: visibleLayers:
- enum.SubfloorLayers.FirstLayer - enum.SubfloorLayers.FirstLayer

View File

@@ -1,5 +1,5 @@
- type: entity - type: entity
parent: [AirSensorBase, GasPipeBase] parent: [AirSensorBase, GasBinaryBase]
id: GasPipeSensor id: GasPipeSensor
name: gas pipe sensor name: gas pipe sensor
description: Reports on the status of the gas in the attached pipe network. description: Reports on the status of the gas in the attached pipe network.
@@ -18,6 +18,11 @@
- map: [ "enum.PowerDeviceVisualLayers.Powered" ] - map: [ "enum.PowerDeviceVisualLayers.Powered" ]
state: lights state: lights
shader: unshaded 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: Appearance
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:

View File

@@ -1,7 +1,7 @@
- type: entity - type: entity
abstract: true abstract: true
id: GasPipeBase
parent: BaseItem parent: BaseItem
id: GasPipeSansLayers
name: gas pipe name: gas pipe
description: Holds gas. description: Holds gas.
placement: placement:
@@ -18,7 +18,6 @@
damageModifierSet: Metallic damageModifierSet: Metallic
- type: SubFloorHide - type: SubFloorHide
- type: CollideOnAnchor - type: CollideOnAnchor
- type: PipeAppearance
- type: Anchorable - type: Anchorable
- type: Rotatable - type: Rotatable
- type: Destructible - type: Destructible
@@ -51,6 +50,7 @@
drawdepth: ThinPipe drawdepth: ThinPipe
visible: false visible: false
- type: Appearance - type: Appearance
- type: PipeAppearance
- type: PipeColorVisuals - type: PipeColorVisuals
- type: NodeContainer - type: NodeContainer
- type: PipeRestrictOverlap - type: PipeRestrictOverlap
@@ -66,12 +66,26 @@
- type: StaticPrice - type: StaticPrice
price: 30 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) #Note: The PipeDirection of the PipeNode should be the south-facing version, because the entity starts at an angle of 0 (south)
- type: entity - type: entity
parent: GasPipeBase parent: GasPipeBase
id: GasPipeHalf id: GasPipeHalf
suffix: Half suffix: Half
placement:
mode: AlignAtmosPipeLayers
components: components:
- type: NodeContainer - type: NodeContainer
nodes: nodes:
@@ -83,9 +97,19 @@
layers: layers:
- state: pipeHalf - state: pipeHalf
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- type: AtmosPipeLayers
alternativePrototypes:
Primary: GasPipeHalf
Secondary: GasPipeHalfAlt1
Tertiary: GasPipeHalfAlt2
- type: Construction - type: Construction
graph: GasPipe graph: GasPipe
node: half node: half
- type: Item
size: Small
storedSprite:
sprite: Structures/Piping/Atmospherics/pipe.rsi
state: storageHalf
- type: GuideHelp - type: GuideHelp
guides: guides:
- Pipes - Pipes
@@ -95,6 +119,8 @@
parent: GasPipeBase parent: GasPipeBase
id: GasPipeStraight id: GasPipeStraight
suffix: Straight suffix: Straight
placement:
mode: AlignAtmosPipeLayers
components: components:
- type: NodeContainer - type: NodeContainer
nodes: nodes:
@@ -106,11 +132,15 @@
layers: layers:
- state: pipeStraight - state: pipeStraight
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- type: AtmosPipeLayers
alternativePrototypes:
Primary: GasPipeStraight
Secondary: GasPipeStraightAlt1
Tertiary: GasPipeStraightAlt2
- type: Construction - type: Construction
graph: GasPipe graph: GasPipe
node: straight node: straight
- type: Item - type: Item
size: Normal
storedSprite: storedSprite:
sprite: Structures/Piping/Atmospherics/pipe.rsi sprite: Structures/Piping/Atmospherics/pipe.rsi
state: storageStraight state: storageStraight
@@ -132,6 +162,8 @@
parent: GasPipeBase parent: GasPipeBase
id: GasPipeBend id: GasPipeBend
suffix: Bend suffix: Bend
placement:
mode: AlignAtmosPipeLayers
components: components:
- type: NodeContainer - type: NodeContainer
nodes: nodes:
@@ -143,11 +175,15 @@
layers: layers:
- state: pipeBend - state: pipeBend
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- type: AtmosPipeLayers
alternativePrototypes:
Primary: GasPipeBend
Secondary: GasPipeBendAlt1
Tertiary: GasPipeBendAlt2
- type: Construction - type: Construction
graph: GasPipe graph: GasPipe
node: bend node: bend
- type: Item - type: Item
size: Small
shape: shape:
- 0,0,1,0 - 0,0,1,0
- 1,1,1,1 - 1,1,1,1
@@ -175,6 +211,8 @@
- type: entity - type: entity
parent: GasPipeBase parent: GasPipeBase
id: GasPipeTJunction id: GasPipeTJunction
placement:
mode: AlignAtmosPipeLayers
suffix: TJunction suffix: TJunction
components: components:
- type: NodeContainer - type: NodeContainer
@@ -187,14 +225,15 @@
layers: layers:
- state: pipeTJunction - state: pipeTJunction
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- type: AtmosPipeLayers
alternativePrototypes:
Primary: GasPipeTJunction
Secondary: GasPipeTJunctionAlt1
Tertiary: GasPipeTJunctionAlt2
- type: Construction - type: Construction
graph: GasPipe graph: GasPipe
node: tjunction node: tjunction
- type: Item - type: Item
size: Normal
shape:
- 0,0,2,0
- 1,1,1,1
heldPrefix: TJunction heldPrefix: TJunction
storedSprite: storedSprite:
sprite: Structures/Piping/Atmospherics/pipe.rsi sprite: Structures/Piping/Atmospherics/pipe.rsi
@@ -216,6 +255,8 @@
parent: GasPipeBase parent: GasPipeBase
id: GasPipeFourway id: GasPipeFourway
suffix: Fourway suffix: Fourway
placement:
mode: AlignAtmosPipeLayers
components: components:
- type: Transform - type: Transform
noRot: true noRot: true
@@ -229,15 +270,19 @@
layers: layers:
- state: pipeFourway - state: pipeFourway
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- type: AtmosPipeLayers
alternativePrototypes:
Primary: GasPipeFourway
Secondary: GasPipeFourwayAlt1
Tertiary: GasPipeFourwayAlt2
- type: Construction - type: Construction
graph: GasPipe graph: GasPipe
node: fourway node: fourway
- type: Item - type: Item
size: Normal
shape:
- 1,0,1,2
- 0,1,2,1
heldPrefix: Fourway heldPrefix: Fourway
storedSprite:
sprite: Structures/Piping/Atmospherics/pipe.rsi
state: storageFourway
- type: MeleeWeapon - type: MeleeWeapon
wideAnimationRotation: 90 wideAnimationRotation: 90
attackRate: 0.75 attackRate: 0.75
@@ -264,9 +309,13 @@
layers: layers:
- state: pipeBroken - state: pipeBroken
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- type: AtmosPipeLayers
numberOfPipeLayers: 1
- type: Construction - type: Construction
graph: GasPipe graph: GasPipe
node: broken node: broken
- type: Item
size: Small
- type: Destructible - type: Destructible
thresholds: # override parent to avoid recursive destruction thresholds: # override parent to avoid recursive destruction
- trigger: - trigger:
@@ -279,3 +328,74 @@
guides: guides:
- Pipes - Pipes
- PipeNetworks - 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

View File

@@ -44,7 +44,7 @@
sprite: Structures/Piping/Atmospherics/vent.rsi sprite: Structures/Piping/Atmospherics/vent.rsi
layers: layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi - sprite: Structures/Piping/Atmospherics/pipe.rsi
state: pipeHalf state: pipeUnaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- state: vent_off - state: vent_off
map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ] map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ]
@@ -94,7 +94,7 @@
sprite: Structures/Piping/Atmospherics/vent.rsi sprite: Structures/Piping/Atmospherics/vent.rsi
layers: layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi - sprite: Structures/Piping/Atmospherics/pipe.rsi
state: pipeHalf state: pipeUnaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- state: vent_passive - state: vent_passive
map: [ "enum.SubfloorLayers.FirstLayer" ] map: [ "enum.SubfloorLayers.FirstLayer" ]
@@ -134,7 +134,7 @@
sprite: Structures/Piping/Atmospherics/scrubber.rsi sprite: Structures/Piping/Atmospherics/scrubber.rsi
layers: layers:
- sprite: Structures/Piping/Atmospherics/pipe.rsi - sprite: Structures/Piping/Atmospherics/pipe.rsi
state: pipeHalf state: pipeUnaryConnectors
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- state: scrub_off - state: scrub_off
map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ] map: [ "enabled", "enum.SubfloorLayers.FirstLayer" ]
@@ -180,7 +180,7 @@
drawdepth: FloorObjects drawdepth: FloorObjects
sprite: Structures/Piping/Atmospherics/outletinjector.rsi sprite: Structures/Piping/Atmospherics/outletinjector.rsi
layers: layers:
- state: pipeHalf - state: pipeUnaryConnectors
sprite: Structures/Piping/Atmospherics/pipe.rsi sprite: Structures/Piping/Atmospherics/pipe.rsi
map: [ "enum.PipeVisualLayers.Pipe" ] map: [ "enum.PipeVisualLayers.Pipe" ]
- state: injector - state: injector
@@ -189,6 +189,7 @@
shader: unshaded shader: unshaded
map: [ "enum.LightLayers.Unshaded" ] map: [ "enum.LightLayers.Unshaded" ]
color: "#990000" color: "#990000"
- type: Appearance
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:
# toggle color of the unshaded light: # toggle color of the unshaded light:
@@ -196,7 +197,6 @@
enum.LightLayers.Unshaded: enum.LightLayers.Unshaded:
True: { color: "#5eff5e" } True: { color: "#5eff5e" }
False: { color: "#990000" } False: { color: "#990000" }
- type: Appearance
- type: PipeColorVisuals - type: PipeColorVisuals
- type: GasOutletInjector - type: GasOutletInjector
- type: Construction - type: Construction
@@ -228,6 +228,12 @@
- type: Rotatable - type: Rotatable
- type: GasThermoMachine - type: GasThermoMachine
- type: AtmosPipeColor - 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: AtmosDevice
- type: UserInterface - type: UserInterface
interfaces: interfaces:
@@ -405,6 +411,7 @@
map: ["enum.SolutionContainerLayers.Fill"] map: ["enum.SolutionContainerLayers.Fill"]
visible: false visible: false
- state: trans - state: trans
- type: Appearance
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:
enum.PowerDeviceVisuals.Powered: enum.PowerDeviceVisuals.Powered:
@@ -414,11 +421,16 @@
- type: SolutionContainerVisuals - type: SolutionContainerVisuals
maxFillLevels: 7 maxFillLevels: 7
fillBaseName: fill- fillBaseName: fill-
- type: Appearance
- type: PipeColorVisuals - type: PipeColorVisuals
- type: Rotatable - type: Rotatable
- type: GasCondenser - type: GasCondenser
- type: AtmosPipeColor - 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: AtmosDevice
- type: PipeRestrictOverlap - type: PipeRestrictOverlap
- type: ApcPowerReceiver - type: ApcPowerReceiver
@@ -470,3 +482,4 @@
- type: GuideHelp - type: GuideHelp
guides: guides:
- GasCondensing - GasCondensing

View File

@@ -10,30 +10,96 @@
amount: 1 amount: 1
doAfter: 1 doAfter: 1
- to: halfAlt1
steps:
- material: Steel
amount: 1
doAfter: 1
- to: halfAlt2
steps:
- material: Steel
amount: 1
doAfter: 1
- to: straight - to: straight
steps: steps:
- material: Steel - material: Steel
amount: 1 amount: 1
doAfter: 1 doAfter: 1
- to: straightAlt1
steps:
- material: Steel
amount: 1
doAfter: 1
- to: straightAlt2
steps:
- material: Steel
amount: 1
doAfter: 1
- to: bend - to: bend
steps: steps:
- material: Steel - material: Steel
amount: 1 amount: 1
doAfter: 1 doAfter: 1
- to: bendAlt1
steps:
- material: Steel
amount: 1
doAfter: 1
- to: bendAlt2
steps:
- material: Steel
amount: 1
doAfter: 1
- to: tjunction - to: tjunction
steps: steps:
- material: Steel - material: Steel
amount: 1 amount: 1
doAfter: 1 doAfter: 1
- to: tjunctionAlt1
steps:
- material: Steel
amount: 1
doAfter: 1
- to: tjunctionAlt2
steps:
- material: Steel
amount: 1
doAfter: 1
- to: fourway - to: fourway
steps: steps:
- material: Steel - material: Steel
amount: 1 amount: 1
doAfter: 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 - node: half
entity: GasPipeHalf entity: GasPipeHalf
edges: edges:
@@ -50,6 +116,38 @@
- tool: Welding - tool: Welding
doAfter: 1 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 - node: straight
entity: GasPipeStraight entity: GasPipeStraight
edges: edges:
@@ -66,6 +164,38 @@
- tool: Welding - tool: Welding
doAfter: 1 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 - node: bend
entity: GasPipeBend entity: GasPipeBend
edges: edges:
@@ -82,6 +212,38 @@
- tool: Welding - tool: Welding
doAfter: 1 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 - node: tjunction
entity: GasPipeTJunction entity: GasPipeTJunction
edges: edges:
@@ -98,6 +260,38 @@
- tool: Welding - tool: Welding
doAfter: 1 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 - node: fourway
entity: GasPipeFourway entity: GasPipeFourway
edges: edges:
@@ -114,6 +308,38 @@
- tool: Welding - tool: Welding
doAfter: 1 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 - node: broken
entity: GasPipeBroken entity: GasPipeBroken
edges: edges:
@@ -129,3 +355,19 @@
steps: steps:
- tool: Welding - tool: Welding
doAfter: 1 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

View File

@@ -281,8 +281,40 @@
startNode: start startNode: start
targetNode: half targetNode: half
category: construction-category-utilities category: construction-category-utilities
placementMode: SnapgridCenter placementMode: AlignAtmosPipeLayers
canBuildInImpassable: true 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 - type: construction
id: GasPipeStraight id: GasPipeStraight
@@ -290,8 +322,40 @@
startNode: start startNode: start
targetNode: straight targetNode: straight
category: construction-category-utilities category: construction-category-utilities
placementMode: SnapgridCenter placementMode: AlignAtmosPipeLayers
canBuildInImpassable: true 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 - type: construction
id: GasPipeBend id: GasPipeBend
@@ -299,8 +363,40 @@
startNode: start startNode: start
targetNode: bend targetNode: bend
category: construction-category-utilities category: construction-category-utilities
placementMode: SnapgridCenter placementMode: AlignAtmosPipeLayers
canBuildInImpassable: true 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 - type: construction
id: GasPipeTJunction id: GasPipeTJunction
@@ -308,8 +404,40 @@
startNode: start startNode: start
targetNode: tjunction targetNode: tjunction
category: construction-category-utilities category: construction-category-utilities
placementMode: SnapgridCenter placementMode: AlignAtmosPipeLayers
canBuildInImpassable: true 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 - type: construction
id: GasPipeFourway id: GasPipeFourway
@@ -317,6 +445,47 @@
startNode: start startNode: start
targetNode: fourway targetNode: fourway
category: construction-category-utilities 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 placementMode: SnapgridCenter
canBuildInImpassable: true canBuildInImpassable: true

View File

@@ -43,6 +43,16 @@
copyright: "Created by chromiumboy" copyright: "Created by chromiumboy"
source: "https://github.com/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"] - files: ["beveled_square.png"]
license: "CC-BY-SA-3.0" license: "CC-BY-SA-3.0"
copyright: "Created by chromiumboy" copyright: "Created by chromiumboy"

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

View File

@@ -0,0 +1,2 @@
sample:
filter: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

View File

@@ -0,0 +1,2 @@
sample:
filter: true

View File

@@ -10,3 +10,6 @@ https://game-icons.net/1x1/delapouite/padlock-open.html
bubbles.svg by Lorc under CC BY 3.0 bubbles.svg by Lorc under CC BY 3.0
https://game-icons.net/1x1/lorc/bubbles.html 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

View File

@@ -0,0 +1,2 @@
sample:
filter: true

View File

@@ -1,21 +1,18 @@
{ {
"version": 1, "version": 1,
"size": "size": {
{
"x": 32, "x": 32,
"y": 32 "y": 32
}, },
"copyright":"Created by EmoGarbage404 (github) for Space Station 14.", "copyright": "Created by EmoGarbage404 (github) for Space Station 14, edited by chromiumboy.",
"license": "CC0-1.0", "license": "CC0-1.0",
"states": "states": [
[
{ {
"name": "off" "name": "off"
}, },
{ {
"name": "on", "name": "on",
"delays": "delays": [
[
[ [
0.05, 0.05,
0.05, 0.05,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

After

Width:  |  Height:  |  Size: 279 B

View File

@@ -0,0 +1,15 @@
{
"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
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

View File

@@ -0,0 +1,15 @@
{
"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
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 B

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -11,14 +11,28 @@
"name": "icon" "name": "icon"
}, },
{ {
"name": "base" "name": "base",
"directions": 4
}, },
{ {
"name": "blank" "name": "blank"
}, },
{ {
"name": "lights", "name": "lights",
"directions": 4,
"delays": [ "delays": [
[
1.0,
0.25
],
[
1.0,
0.25
],
[
1.0,
0.25
],
[ [
1.0, 1.0,
0.25 0.25

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

View File

@@ -0,0 +1,43 @@
{
"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
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

View File

@@ -0,0 +1,43 @@
{
"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
]
]
}
]
}

View File

@@ -1,7 +1,10 @@
{ {
"version": 1, "version": 1,
"size":{"x":32,"y":32}, "size": {
"copyright":"Base sprites taken from tgstation, split to display on two layers (machinebody/panel) by Menshin, and recolored and edited by EmoGarbage404 (github)", "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", "license": "CC-BY-SA-3.0",
"states": [ "states": [
{ {
@@ -15,7 +18,20 @@
{ {
"name": "freezerOn", "name": "freezerOn",
"directions": 1, "directions": 1,
"delays":[ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] ] "delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
]
]
}, },
{ {
"name": "heaterOff", "name": "heaterOff",
@@ -28,7 +44,20 @@
{ {
"name": "heaterOn", "name": "heaterOn",
"directions": 1, "directions": 1,
"delays":[ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] ] "delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
]
]
}, },
{ {
"name": "pipe", "name": "pipe",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -0,0 +1,30 @@
{
"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
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -5,7 +5,7 @@
"y": 32 "y": 32
}, },
"license": "CC-BY-SA-3.0", "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.", "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": [ "states": [
{ {
"name": "inhand-left", "name": "inhand-left",
@@ -67,21 +67,32 @@
"name": "pipeConnector", "name": "pipeConnector",
"directions": 4 "directions": 4
}, },
{
"name": "pipeUnaryConnectors",
"directions": 4
},
{
"name": "pipeBinaryConnectors",
"directions": 4
},
{ {
"name": "pipeTrinaryConnectors", "name": "pipeTrinaryConnectors",
"directions": 4 "directions": 4
}, },
{ {
"name":"storageStraight", "name": "storageStraight"
"directions":4
}, },
{ {
"name":"storageBend", "name": "storageHalf"
"directions":4
}, },
{ {
"name":"storageTJunction", "name": "storageBend"
"directions":4 },
{
"name": "storageTJunction"
},
{
"name": "storageFourway"
} }
] ]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 B

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 333 B

View File

@@ -0,0 +1,51 @@
{
"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
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 972 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

View File

@@ -0,0 +1,51 @@
{
"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
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Some files were not shown because too many files have changed in this diff Show More