diff --git a/Content.Client/IconSmoothing/IconSmoothComponent.cs b/Content.Client/IconSmoothing/IconSmoothComponent.cs
index b39d4b33fe..a469cbc634 100644
--- a/Content.Client/IconSmoothing/IconSmoothComponent.cs
+++ b/Content.Client/IconSmoothing/IconSmoothComponent.cs
@@ -1,20 +1,8 @@
-using System;
-using System.Collections.Generic;
using JetBrains.Annotations;
-using Robust.Client.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Log;
using Robust.Shared.Map;
-using Robust.Shared.Maths;
-using Robust.Shared.Serialization.Manager.Attributes;
-using static Robust.Client.GameObjects.SpriteComponent;
namespace Content.Client.IconSmoothing
{
- // TODO: Potential improvements:
- // Defer updating of these.
- // Get told by somebody to use a loop.
///
/// Makes sprites of other grid-aligned entities like us connect.
///
@@ -24,17 +12,8 @@ namespace Content.Client.IconSmoothing
/// Any objects with the same key will connect.
///
[RegisterComponent]
- [Virtual]
- public class IconSmoothComponent : Component
+ public sealed class IconSmoothComponent : Component
{
- [Dependency] private readonly IEntityManager _entMan = default!;
- [Dependency] private readonly IMapManager _mapManager = default!;
-
- [DataField("mode")]
- private IconSmoothingMode _mode = IconSmoothingMode.Corners;
-
- internal ISpriteComponent? Sprite { get; private set; }
-
public (GridId, Vector2i)? LastPosition;
///
@@ -52,284 +31,13 @@ namespace Content.Client.IconSmoothing
///
/// Mode that controls how the icon should be selected.
///
- public IconSmoothingMode Mode => _mode;
+ [DataField("mode")]
+ public IconSmoothingMode Mode = IconSmoothingMode.Corners;
///
/// Used by to reduce redundant updates.
///
internal int UpdateGeneration { get; set; }
-
- protected override void Initialize()
- {
- base.Initialize();
-
- Sprite = _entMan.GetComponent(Owner);
- }
-
- ///
- protected override void Startup()
- {
- base.Startup();
-
- if (_entMan.GetComponent(Owner).Anchored)
- {
- // ensures lastposition initial value is populated on spawn. Just calling
- // the hook here would cause a dirty event to fire needlessly
- UpdateLastPosition();
- EntitySystem.Get().UpdateSmoothing(Owner, this);
- }
-
- if (Sprite != null && Mode == IconSmoothingMode.Corners)
- {
- var state0 = $"{StateBase}0";
- Sprite.LayerMapSet(CornerLayers.SE, Sprite.AddLayerState(state0));
- Sprite.LayerSetDirOffset(CornerLayers.SE, DirectionOffset.None);
- Sprite.LayerMapSet(CornerLayers.NE, Sprite.AddLayerState(state0));
- Sprite.LayerSetDirOffset(CornerLayers.NE, DirectionOffset.CounterClockwise);
- Sprite.LayerMapSet(CornerLayers.NW, Sprite.AddLayerState(state0));
- Sprite.LayerSetDirOffset(CornerLayers.NW, DirectionOffset.Flip);
- Sprite.LayerMapSet(CornerLayers.SW, Sprite.AddLayerState(state0));
- Sprite.LayerSetDirOffset(CornerLayers.SW, DirectionOffset.Clockwise);
- }
- }
-
- private void UpdateLastPosition()
- {
- var transform = _entMan.GetComponent(Owner);
-
- if (_mapManager.TryGetGrid(transform.GridID, out var grid))
- {
- LastPosition = (transform.GridID, grid.TileIndicesFor(transform.Coordinates));
- }
- else
- {
- // When this is called during component startup, the transform can end up being with an invalid grid ID.
- // In that case, use this.
- LastPosition = (GridId.Invalid, new Vector2i(0, 0));
- }
- }
-
- internal virtual void CalculateNewSprite()
- {
- var transform = _entMan.GetComponent(Owner);
-
- if (!transform.Anchored)
- {
- CalculateNewSprite(null);
- return;
- }
-
- if (!_mapManager.TryGetGrid(transform.GridID, out var grid))
- {
- Logger.Error($"Failed to calculate IconSmoothComponent sprite in {Owner} because grid {transform.GridID} was missing.");
- return;
- }
- CalculateNewSprite(grid);
- }
-
- internal virtual void CalculateNewSprite(IMapGrid? grid)
- {
- switch (Mode)
- {
- case IconSmoothingMode.Corners:
- CalculateNewSpriteCorners(grid);
- break;
- case IconSmoothingMode.CardinalFlags:
- CalculateNewSpriteCardinal(grid);
- break;
- case IconSmoothingMode.NoSprite:
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
-
- private void CalculateNewSpriteCardinal(IMapGrid? grid)
- {
- if (Sprite == null)
- {
- return;
- }
-
- var dirs = CardinalConnectDirs.None;
-
- if (grid == null)
- {
- Sprite.LayerSetState(0, $"{StateBase}{(int) dirs}");
- return;
- }
-
- var position = _entMan.GetComponent(Owner).Coordinates;
- if (MatchingEntity(grid.GetInDir(position, Direction.North)))
- dirs |= CardinalConnectDirs.North;
- if (MatchingEntity(grid.GetInDir(position, Direction.South)))
- dirs |= CardinalConnectDirs.South;
- if (MatchingEntity(grid.GetInDir(position, Direction.East)))
- dirs |= CardinalConnectDirs.East;
- if (MatchingEntity(grid.GetInDir(position, Direction.West)))
- dirs |= CardinalConnectDirs.West;
-
- Sprite.LayerSetState(0, $"{StateBase}{(int) dirs}");
- }
-
- private void CalculateNewSpriteCorners(IMapGrid? grid)
- {
- if (Sprite == null)
- {
- return;
- }
-
- var (cornerNE, cornerNW, cornerSW, cornerSE) = CalculateCornerFill(grid);
-
- Sprite.LayerSetState(CornerLayers.NE, $"{StateBase}{(int) cornerNE}");
- Sprite.LayerSetState(CornerLayers.SE, $"{StateBase}{(int) cornerSE}");
- Sprite.LayerSetState(CornerLayers.SW, $"{StateBase}{(int) cornerSW}");
- Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
- }
-
- protected (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(IMapGrid? grid)
- {
- if (grid == null)
- {
- return (CornerFill.None, CornerFill.None, CornerFill.None, CornerFill.None);
- }
-
- var position = _entMan.GetComponent(Owner).Coordinates;
- var n = MatchingEntity(grid.GetInDir(position, Direction.North));
- var ne = MatchingEntity(grid.GetInDir(position, Direction.NorthEast));
- var e = MatchingEntity(grid.GetInDir(position, Direction.East));
- var se = MatchingEntity(grid.GetInDir(position, Direction.SouthEast));
- var s = MatchingEntity(grid.GetInDir(position, Direction.South));
- var sw = MatchingEntity(grid.GetInDir(position, Direction.SouthWest));
- var w = MatchingEntity(grid.GetInDir(position, Direction.West));
- var nw = MatchingEntity(grid.GetInDir(position, Direction.NorthWest));
-
- // ReSharper disable InconsistentNaming
- var cornerNE = CornerFill.None;
- var cornerSE = CornerFill.None;
- var cornerSW = CornerFill.None;
- var cornerNW = CornerFill.None;
- // ReSharper restore InconsistentNaming
-
- if (n)
- {
- cornerNE |= CornerFill.CounterClockwise;
- cornerNW |= CornerFill.Clockwise;
- }
-
- if (ne)
- {
- cornerNE |= CornerFill.Diagonal;
- }
-
- if (e)
- {
- cornerNE |= CornerFill.Clockwise;
- cornerSE |= CornerFill.CounterClockwise;
- }
-
- if (se)
- {
- cornerSE |= CornerFill.Diagonal;
- }
-
- if (s)
- {
- cornerSE |= CornerFill.Clockwise;
- cornerSW |= CornerFill.CounterClockwise;
- }
-
- if (sw)
- {
- cornerSW |= CornerFill.Diagonal;
- }
-
- if (w)
- {
- cornerSW |= CornerFill.Clockwise;
- cornerNW |= CornerFill.CounterClockwise;
- }
-
- if (nw)
- {
- cornerNW |= CornerFill.Diagonal;
- }
-
- // Local is fine as we already know it's parented to the grid (due to the way anchoring works).
- switch (_entMan.GetComponent(Owner).LocalRotation.GetCardinalDir())
- {
- case Direction.North:
- return (cornerSW, cornerSE, cornerNE, cornerNW);
- case Direction.West:
- return (cornerSE, cornerNE, cornerNW, cornerSW);
- case Direction.South:
- return (cornerNE, cornerNW, cornerSW, cornerSE);
- default:
- return (cornerNW, cornerSW, cornerSE, cornerNE);
- }
- }
-
- ///
- protected override void Shutdown()
- {
- base.Shutdown();
-
- EntitySystem.Get().UpdateSmoothing(Owner, this);
- }
-
- [System.Diagnostics.Contracts.Pure]
- protected bool MatchingEntity(IEnumerable candidates)
- {
- foreach (var entity in candidates)
- {
- if (!_entMan.TryGetComponent(entity, out IconSmoothComponent? other))
- {
- continue;
- }
-
- if (other.SmoothKey == SmoothKey)
- {
- return true;
- }
- }
-
- return false;
- }
-
- [Flags]
- private enum CardinalConnectDirs : byte
- {
- None = 0,
- North = 1,
- South = 2,
- East = 4,
- West = 8
- }
-
- [Flags]
- public enum CornerFill : byte
- {
- // These values are pulled from Baystation12.
- // I'm too lazy to convert the state names.
- None = 0,
-
- // The cardinal tile counter-clockwise of this corner is filled.
- CounterClockwise = 1,
-
- // The diagonal tile in the direction of this corner.
- Diagonal = 2,
-
- // The cardinal tile clockwise of this corner is filled.
- Clockwise = 4,
- }
-
- public enum CornerLayers : byte
- {
- SE,
- NE,
- NW,
- SW,
- }
}
///
diff --git a/Content.Client/IconSmoothing/IconSmoothSystem.cs b/Content.Client/IconSmoothing/IconSmoothSystem.cs
index e30cc8ef86..e368b402e1 100644
--- a/Content.Client/IconSmoothing/IconSmoothSystem.cs
+++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs
@@ -1,12 +1,11 @@
-using System.Collections.Generic;
using JetBrains.Annotations;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
+using Robust.Client.GameObjects;
using Robust.Shared.Map;
-using Robust.Shared.Maths;
+using static Robust.Client.GameObjects.SpriteComponent;
namespace Content.Client.IconSmoothing
{
+ // TODO: just make this set appearance data?
///
/// Entity system implementing the logic for
///
@@ -16,43 +15,100 @@ namespace Content.Client.IconSmoothing
[Dependency] private readonly IMapManager _mapManager = default!;
private readonly Queue _dirtyEntities = new();
+ private readonly Queue _anchorChangedEntities = new();
private int _generation;
- ///
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnAnchorChanged);
+ SubscribeLocalEvent(OnShutdown);
+ SubscribeLocalEvent(OnStartup);
+ }
+
+ private void OnStartup(EntityUid uid, IconSmoothComponent component, ComponentStartup args)
+ {
+ var xform = Transform(uid);
+ if (xform.Anchored)
+ {
+ component.LastPosition = _mapManager.TryGetGrid(xform.GridID, out var grid)
+ ? (xform.GridID, grid.TileIndicesFor(xform.Coordinates))
+ : (GridId.Invalid, new Vector2i(0, 0));
+
+ DirtyNeighbours(uid, component);
+ }
+
+ if (component.Mode != IconSmoothingMode.Corners || !TryComp(uid, out SpriteComponent? sprite))
+ return;
+
+ var state0 = $"{component.StateBase}0";
+ sprite.LayerMapSet(CornerLayers.SE, sprite.AddLayerState(state0));
+ sprite.LayerSetDirOffset(CornerLayers.SE, DirectionOffset.None);
+ sprite.LayerMapSet(CornerLayers.NE, sprite.AddLayerState(state0));
+ sprite.LayerSetDirOffset(CornerLayers.NE, DirectionOffset.CounterClockwise);
+ sprite.LayerMapSet(CornerLayers.NW, sprite.AddLayerState(state0));
+ sprite.LayerSetDirOffset(CornerLayers.NW, DirectionOffset.Flip);
+ sprite.LayerMapSet(CornerLayers.SW, sprite.AddLayerState(state0));
+ sprite.LayerSetDirOffset(CornerLayers.SW, DirectionOffset.Clockwise);
+ }
+
+ private void OnShutdown(EntityUid uid, IconSmoothComponent component, ComponentShutdown args)
+ {
+ DirtyNeighbours(uid, component);
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
- if (_dirtyEntities.Count == 0)
+ var xformQuery = GetEntityQuery();
+ var smoothQuery = GetEntityQuery();
+
+ // first process anchor state changes.
+ while (_anchorChangedEntities.TryDequeue(out var uid))
{
- return;
+ if (!xformQuery.TryGetComponent(uid, out var xform))
+ continue;
+
+ if (xform.MapID == MapId.Nullspace)
+ {
+ // in null-space. Almost certainly because it left PVS. If something ever gets sent to null-space
+ // for reasons other than this (or entity deletion), then maybe we still need to update ex-neighbor
+ // smoothing here.
+ continue;
+ }
+
+ DirtyNeighbours(uid, comp: null, xform, smoothQuery);
}
+ // Next, update actual sprites.
+ if (_dirtyEntities.Count == 0)
+ return;
+
_generation += 1;
+ var spriteQuery = GetEntityQuery();
+
// Performance: This could be spread over multiple updates, or made parallel.
- while (_dirtyEntities.Count > 0)
+ while (_dirtyEntities.TryDequeue(out var uid))
{
- CalculateNewSprite(_dirtyEntities.Dequeue());
+ CalculateNewSprite(uid, spriteQuery, smoothQuery, xformQuery);
}
}
- public void UpdateSmoothing(EntityUid uid, IconSmoothComponent? comp = null)
+ public void DirtyNeighbours(EntityUid uid, IconSmoothComponent? comp = null, TransformComponent? transform = null, EntityQuery? smoothQuery = null)
{
- if (!Resolve(uid, ref comp))
+ smoothQuery ??= GetEntityQuery();
+ if (!smoothQuery.Value.Resolve(uid, ref comp))
return;
_dirtyEntities.Enqueue(uid);
- var transform = Transform(uid);
+ if (!Resolve(uid, ref transform))
+ return;
+
Vector2i pos;
if (transform.Anchored && _mapManager.TryGetGrid(transform.GridID, out var grid))
@@ -72,52 +128,253 @@ namespace Content.Client.IconSmoothing
}
// Yes, we updates ALL smoothing entities surrounding us even if they would never smooth with us.
- // This is simpler to implement. If you want to optimize it be my guest.
- AddValidEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, 0)));
- AddValidEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, 0)));
- AddValidEntities(grid.GetAnchoredEntities(pos + new Vector2i(0, 1)));
- AddValidEntities(grid.GetAnchoredEntities(pos + new Vector2i(0, -1)));
+ DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, 0)));
+ DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, 0)));
+ DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(0, 1)));
+ DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(0, -1)));
if (comp.Mode == IconSmoothingMode.Corners)
{
- AddValidEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, 1)));
- AddValidEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, -1)));
- AddValidEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, 1)));
- AddValidEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, -1)));
+ DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, 1)));
+ DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, -1)));
+ DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(-1, 1)));
+ DirtyEntities(grid.GetAnchoredEntities(pos + new Vector2i(1, -1)));
+ }
+ }
+
+ private void DirtyEntities(IEnumerable entities)
+ {
+ // Instead of doing HasComp -> Enqueue -> TryGetComp, we will just enqueue all entities. Generally when
+ // dealing with walls neighboring anchored entities will also be walls, and in those instances that will
+ // require one less component fetch/check.
+ foreach (var entity in entities)
+ {
+ _dirtyEntities.Enqueue(entity);
}
}
private void OnAnchorChanged(EntityUid uid, IconSmoothComponent component, ref AnchorStateChangedEvent args)
{
- UpdateSmoothing(uid, component);
+ // Defer updating to next frame update. We do this because this unanchoring might be caused by a detach to
+ // null due to leaving PVS, in which case we can be a bit lazy. However, this event is raised before being
+ // sent to null-space, hence deferring.
+ _anchorChangedEntities.Enqueue(uid);
}
- private void AddValidEntities(IEnumerable candidates)
- {
- foreach (var entity in candidates)
- {
- if (EntityManager.HasComponent(entity))
- {
- _dirtyEntities.Enqueue(entity);
- }
- }
- }
-
- private void CalculateNewSprite(EntityUid euid)
+ private void CalculateNewSprite(EntityUid uid,
+ EntityQuery spriteQuery,
+ EntityQuery smoothQuery,
+ EntityQuery xformQuery,
+ IconSmoothComponent? smooth = null)
{
// The generation check prevents updating an entity multiple times per tick.
// As it stands now, it's totally possible for something to get queued twice.
// Generation on the component is set after an update so we can cull updates that happened this generation.
- if (!EntityManager.EntityExists(euid)
- || !EntityManager.TryGetComponent(euid, out IconSmoothComponent? smoothing)
- || smoothing.UpdateGeneration == _generation)
+ if (!smoothQuery.Resolve(uid, ref smooth, false)
+ || smooth.Mode == IconSmoothingMode.NoSprite
+ || smooth.UpdateGeneration == _generation)
{
return;
}
+ smooth.UpdateGeneration = _generation;
- smoothing.CalculateNewSprite();
+ if (!spriteQuery.TryGetComponent(uid, out var sprite))
+ {
+ Logger.Error($"Encountered a icon-smoothing entity without a sprite: {ToPrettyString(uid)}");
+ RemComp(uid, smooth);
+ return;
+ }
- smoothing.UpdateGeneration = _generation;
+ var xform = xformQuery.GetComponent(uid);
+
+ IMapGrid? grid = null;
+
+ if (xform.Anchored)
+ {
+ if (!_mapManager.TryGetGrid(xform.GridID, out grid))
+ {
+ Logger.Error($"Failed to calculate IconSmoothComponent sprite in {uid} because grid {xform.GridID} was missing.");
+ return;
+ }
+ }
+
+ switch (smooth.Mode)
+ {
+ case IconSmoothingMode.Corners:
+ CalculateNewSpriteCorners(grid, smooth, sprite, xform, smoothQuery);
+ break;
+ case IconSmoothingMode.CardinalFlags:
+ CalculateNewSpriteCardinal(grid, smooth, sprite, xform, smoothQuery);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ private void CalculateNewSpriteCardinal(IMapGrid? grid, IconSmoothComponent smooth, SpriteComponent sprite, TransformComponent xform, EntityQuery smoothQuery)
+ {
+ var dirs = CardinalConnectDirs.None;
+
+ if (grid == null)
+ {
+ sprite.LayerSetState(0, $"{smooth.StateBase}{(int) dirs}");
+ return;
+ }
+
+ var pos = grid.TileIndicesFor(xform.Coordinates);
+ if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.North)), smoothQuery))
+ dirs |= CardinalConnectDirs.North;
+ if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.South)), smoothQuery))
+ dirs |= CardinalConnectDirs.South;
+ if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.East)), smoothQuery))
+ dirs |= CardinalConnectDirs.East;
+ if (MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.West)), smoothQuery))
+ dirs |= CardinalConnectDirs.West;
+
+ sprite.LayerSetState(0, $"{smooth.StateBase}{(int) dirs}");
+ }
+
+ protected bool MatchingEntity(IconSmoothComponent smooth, IEnumerable candidates, EntityQuery smoothQuery)
+ {
+ foreach (var entity in candidates)
+ {
+ if (smoothQuery.TryGetComponent(entity, out var other) && other.SmoothKey == smooth.SmoothKey)
+ return true;
+ }
+
+ return false;
+ }
+
+ private void CalculateNewSpriteCorners(IMapGrid? grid, IconSmoothComponent smooth, SpriteComponent sprite, TransformComponent xform, EntityQuery smoothQuery)
+ {
+ var (cornerNE, cornerNW, cornerSW, cornerSE) = grid == null
+ ? (CornerFill.None, CornerFill.None, CornerFill.None, CornerFill.None)
+ : CalculateCornerFill(grid, smooth, xform, smoothQuery);
+
+ // TODO figure out a better way to set multiple sprite layers.
+ // This will currently re-calculate the sprite bounding box 4 times.
+ // It will also result in 4-8 sprite update events being raised when it only needs to be 1-2.
+ // At the very least each event currently only queues a sprite for updating.
+ // Oh god sprite component is a mess.
+ sprite.LayerSetState(CornerLayers.NE, $"{smooth.StateBase}{(int) cornerNE}");
+ sprite.LayerSetState(CornerLayers.SE, $"{smooth.StateBase}{(int) cornerSE}");
+ sprite.LayerSetState(CornerLayers.SW, $"{smooth.StateBase}{(int) cornerSW}");
+ sprite.LayerSetState(CornerLayers.NW, $"{smooth.StateBase}{(int) cornerNW}");
+ }
+
+ private (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(IMapGrid grid, IconSmoothComponent smooth, TransformComponent xform, EntityQuery smoothQuery)
+ {
+ var pos = grid.TileIndicesFor(xform.Coordinates);
+ var n = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.North)), smoothQuery);
+ var ne = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.NorthEast)), smoothQuery);
+ var e = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.East)), smoothQuery);
+ var se = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.SouthEast)), smoothQuery);
+ var s = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.South)), smoothQuery);
+ var sw = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.SouthWest)), smoothQuery);
+ var w = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.West)), smoothQuery);
+ var nw = MatchingEntity(smooth, grid.GetAnchoredEntities(pos.Offset(Direction.NorthWest)), smoothQuery);
+
+ // ReSharper disable InconsistentNaming
+ var cornerNE = CornerFill.None;
+ var cornerSE = CornerFill.None;
+ var cornerSW = CornerFill.None;
+ var cornerNW = CornerFill.None;
+ // ReSharper restore InconsistentNaming
+
+ if (n)
+ {
+ cornerNE |= CornerFill.CounterClockwise;
+ cornerNW |= CornerFill.Clockwise;
+ }
+
+ if (ne)
+ {
+ cornerNE |= CornerFill.Diagonal;
+ }
+
+ if (e)
+ {
+ cornerNE |= CornerFill.Clockwise;
+ cornerSE |= CornerFill.CounterClockwise;
+ }
+
+ if (se)
+ {
+ cornerSE |= CornerFill.Diagonal;
+ }
+
+ if (s)
+ {
+ cornerSE |= CornerFill.Clockwise;
+ cornerSW |= CornerFill.CounterClockwise;
+ }
+
+ if (sw)
+ {
+ cornerSW |= CornerFill.Diagonal;
+ }
+
+ if (w)
+ {
+ cornerSW |= CornerFill.Clockwise;
+ cornerNW |= CornerFill.CounterClockwise;
+ }
+
+ if (nw)
+ {
+ cornerNW |= CornerFill.Diagonal;
+ }
+
+ // Local is fine as we already know it's parented to the grid (due to the way anchoring works).
+ switch (xform.LocalRotation.GetCardinalDir())
+ {
+ case Direction.North:
+ return (cornerSW, cornerSE, cornerNE, cornerNW);
+ case Direction.West:
+ return (cornerSE, cornerNE, cornerNW, cornerSW);
+ case Direction.South:
+ return (cornerNE, cornerNW, cornerSW, cornerSE);
+ default:
+ return (cornerNW, cornerSW, cornerSE, cornerNE);
+ }
+ }
+
+ // TODO consider changing this to use DirectionFlags?
+ // would require re-labelling all the RSI states.
+ [Flags]
+ private enum CardinalConnectDirs : byte
+ {
+ None = 0,
+ North = 1,
+ South = 2,
+ East = 4,
+ West = 8
+ }
+
+
+ [Flags]
+ private enum CornerFill : byte
+ {
+ // These values are pulled from Baystation12.
+ // I'm too lazy to convert the state names.
+ None = 0,
+
+ // The cardinal tile counter-clockwise of this corner is filled.
+ CounterClockwise = 1,
+
+ // The diagonal tile in the direction of this corner.
+ Diagonal = 2,
+
+ // The cardinal tile clockwise of this corner is filled.
+ Clockwise = 4,
+ }
+
+ private enum CornerLayers : byte
+ {
+ SE,
+ NE,
+ NW,
+ SW,
}
}
}
diff --git a/Content.Client/Wall/Components/ReinforcedWallComponent.cs b/Content.Client/Wall/Components/ReinforcedWallComponent.cs
deleted file mode 100644
index c8673405d2..0000000000
--- a/Content.Client/Wall/Components/ReinforcedWallComponent.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using Content.Client.IconSmoothing;
-using Robust.Shared.GameObjects;
-using Robust.Shared.Map;
-using Robust.Shared.Serialization.Manager.Attributes;
-using Robust.Shared.ViewVariables;
-using static Robust.Client.GameObjects.SpriteComponent;
-
-namespace Content.Client.Wall.Components
-{
- [RegisterComponent]
- [ComponentReference(typeof(IconSmoothComponent))]
- public sealed class ReinforcedWallComponent : IconSmoothComponent // whyyyyyyyyy
- {
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("reinforcedBase")]
- private string? _reinforcedStateBase = default;
-
- protected override void Startup()
- {
- base.Startup();
-
- if (Sprite != null)
- {
- var state0 = $"{_reinforcedStateBase}0";
- Sprite.LayerMapSet(ReinforcedCornerLayers.SE, Sprite.AddLayerState(state0));
- Sprite.LayerSetDirOffset(ReinforcedCornerLayers.SE, DirectionOffset.None);
- Sprite.LayerMapSet(ReinforcedCornerLayers.NE, Sprite.AddLayerState(state0));
- Sprite.LayerSetDirOffset(ReinforcedCornerLayers.NE, DirectionOffset.CounterClockwise);
- Sprite.LayerMapSet(ReinforcedCornerLayers.NW, Sprite.AddLayerState(state0));
- Sprite.LayerSetDirOffset(ReinforcedCornerLayers.NW, DirectionOffset.Flip);
- Sprite.LayerMapSet(ReinforcedCornerLayers.SW, Sprite.AddLayerState(state0));
- Sprite.LayerSetDirOffset(ReinforcedCornerLayers.SW, DirectionOffset.Clockwise);
- Sprite.LayerMapSet(ReinforcedWallVisualLayers.Deconstruction, Sprite.AddBlankLayer());
- }
- }
-
- internal override void CalculateNewSprite(IMapGrid? grid)
- {
- base.CalculateNewSprite(grid);
-
- var (cornerNE, cornerNW, cornerSW, cornerSE) = CalculateCornerFill(grid);
-
- if (Sprite != null)
- {
- Sprite.LayerSetState(ReinforcedCornerLayers.NE, $"{_reinforcedStateBase}{(int) cornerNE}");
- Sprite.LayerSetState(ReinforcedCornerLayers.SE, $"{_reinforcedStateBase}{(int) cornerSE}");
- Sprite.LayerSetState(ReinforcedCornerLayers.SW, $"{_reinforcedStateBase}{(int) cornerSW}");
- Sprite.LayerSetState(ReinforcedCornerLayers.NW, $"{_reinforcedStateBase}{(int) cornerNW}");
- }
- }
-
- public enum ReinforcedCornerLayers : byte
- {
- SE,
- NE,
- NW,
- SW,
- }
- }
-}
diff --git a/Content.Client/Wall/ReinforcedWallVisualizer.cs b/Content.Client/Wall/ReinforcedWallVisualizer.cs
index fe2435df7c..9ee1660d79 100644
--- a/Content.Client/Wall/ReinforcedWallVisualizer.cs
+++ b/Content.Client/Wall/ReinforcedWallVisualizer.cs
@@ -1,4 +1,4 @@
-using Content.Shared.Wall;
+using Content.Shared.Wall;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
@@ -26,14 +26,16 @@ namespace Content.Client.Wall
var entities = IoCManager.Resolve();
if (!entities.TryGetComponent(entity, out ISpriteComponent? sprite)) return;
+ var index = sprite.LayerMapReserveBlank(ReinforcedWallVisualLayers.Deconstruction);
+
if (stage < 0)
{
- sprite.LayerSetVisible(ReinforcedWallVisualLayers.Deconstruction, false);
+ sprite.LayerSetVisible(index, false);
return;
}
- sprite.LayerSetVisible(ReinforcedWallVisualLayers.Deconstruction, true);
- sprite.LayerSetState(ReinforcedWallVisualLayers.Deconstruction, $"reinf_construct-{stage}");
+ sprite.LayerSetVisible(index, true);
+ sprite.LayerSetState(index, $"reinf_construct-{stage}");
}
}
diff --git a/Content.Server/Entry/IgnoredComponents.cs b/Content.Server/Entry/IgnoredComponents.cs
index f48d64b299..47354f87e2 100644
--- a/Content.Server/Entry/IgnoredComponents.cs
+++ b/Content.Server/Entry/IgnoredComponents.cs
@@ -6,7 +6,6 @@ namespace Content.Server.Entry
public static string[] List => new [] {
"ConstructionGhost",
"IconSmooth",
- "ReinforcedWall",
"StasisBedVisuals",
"InteractionOutline",
"MeleeWeaponArcAnimation",
diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml
index 94dfb6cea2..c7ec79d908 100644
--- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml
+++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml
@@ -360,10 +360,9 @@
node: girder
- !type:DoActsBehavior
acts: ["Destruction"]
- - type: ReinforcedWall
+ - type: IconSmooth
key: walls
- base: solid
- reinforcedBase: reinf_over
+ base: reinf_over
- type: Appearance
visuals:
- type: ReinforcedWallVisualizer