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