diff --git a/Resources/Locale/en-US/custom-controls.ftl b/Resources/Locale/en-US/custom-controls.ftl index abf04e79d6b..b5aa5f11197 100644 --- a/Resources/Locale/en-US/custom-controls.ftl +++ b/Resources/Locale/en-US/custom-controls.ftl @@ -9,6 +9,7 @@ entity-spawn-window-override-menu-tooltip = Override placement ## TileSpawnWindow tile-spawn-window-title = Place Tiles +tile-spawn-window-mirror-button-text = Mirror Tiles ## Console diff --git a/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs b/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs index 5c0a3efff5d..8ae101d4256 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs @@ -168,10 +168,50 @@ private void _updateChunkMesh(Entity grid, MapChunk chunk, Map var gy = y + cScaled.Y; var vIdx = i * 4; - vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, region.Left, region.Bottom, Color.White); - vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, region.Right, region.Bottom, Color.White); - vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, region.Right, region.Top, Color.White); - vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, region.Left, region.Top, Color.White); + + var rLeftBottom = (region.Left, region.Bottom); + var rRightBottom = (region.Right, region.Bottom); + var rRightTop = (region.Right, region.Top); + var rLeftTop = (region.Left, region.Top); + + // Rotate the tile + for (int r = 0; r < tile.Rotation; r++) + { + (rLeftBottom, rRightBottom, rRightTop, rLeftTop) = + (rLeftTop, rLeftBottom, rRightBottom, rRightTop); + } + + // Mirror on the x-axis + if (tile.Mirrored) + { + if (tile.Rotation % 2 == 0) + { + rLeftBottom = (rLeftBottom.Item1.Equals(region.Left) ? region.Right : region.Left, + rLeftBottom.Item2); + rRightBottom = (rRightBottom.Item1.Equals(region.Left) ? region.Right : region.Left, + rRightBottom.Item2); + rRightTop = (rRightTop.Item1.Equals(region.Left) ? region.Right : region.Left, + rRightTop.Item2); + rLeftTop = (rLeftTop.Item1.Equals(region.Left) ? region.Right : region.Left, + rLeftTop.Item2); + } + else + { + rLeftBottom = (rLeftBottom.Item1, + rLeftBottom.Item2.Equals(region.Bottom) ? region.Top : region.Bottom); + rRightBottom = (rRightBottom.Item1, + rRightBottom.Item2.Equals(region.Bottom) ? region.Top : region.Bottom); + rRightTop = (rRightTop.Item1, + rRightTop.Item2.Equals(region.Bottom) ? region.Top : region.Bottom); + rLeftTop = (rLeftTop.Item1, + rLeftTop.Item2.Equals(region.Bottom) ? region.Top : region.Bottom); + } + } + + vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, rLeftBottom.Item1, rLeftBottom.Item2, Color.White); + vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, rRightBottom.Item1, rRightBottom.Item2, Color.White); + vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, rRightTop.Item1, rRightTop.Item2, Color.White); + vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, rLeftTop.Item1, rLeftTop.Item2, Color.White); var nIdx = i * GetQuadBatchIndexCount(); var tIdx = (ushort)(i * 4); QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx); diff --git a/Robust.Client/Placement/IPlacementManager.cs b/Robust.Client/Placement/IPlacementManager.cs index f93da194194..5a708135002 100644 --- a/Robust.Client/Placement/IPlacementManager.cs +++ b/Robust.Client/Placement/IPlacementManager.cs @@ -24,10 +24,20 @@ public interface IPlacementManager Direction Direction { get; set; } /// - /// Gets called when Direction changed (presently for EntitySpawnWindow UI) + /// Whether a tile placement should be mirrored or not. + /// + bool Mirrored { get; set; } + + /// + /// Gets called when Direction changed (presently for EntitySpawnWindow/TileSpawnWindow UI) /// event EventHandler DirectionChanged; + /// + /// Gets called when Mirrored changed (presently for TileSpawnWindow UI) + /// + event EventHandler MirroredChanged; + /// /// Gets called when the PlacementManager changed its build/erase mode or when the hijacks changed /// diff --git a/Robust.Client/Placement/PlacementManager.cs b/Robust.Client/Placement/PlacementManager.cs index f48ccda6587..c67a34f6483 100644 --- a/Robust.Client/Placement/PlacementManager.cs +++ b/Robust.Client/Placement/PlacementManager.cs @@ -174,6 +174,18 @@ public Box2 ColliderAABB private Direction _direction = Direction.South; + private bool _mirrored; + + public bool Mirrored + { + get => _mirrored; + set + { + _mirrored = value; + MirroredChanged?.Invoke(this, EventArgs.Empty); + } + } + /// public Direction Direction { @@ -188,6 +200,9 @@ public Direction Direction /// public event EventHandler? DirectionChanged; + /// + public event EventHandler? MirroredChanged; + private PlacementOverlay _drawOverlay = default!; private bool _isActive; @@ -772,7 +787,10 @@ private void RequestPlacement(EntityCoordinates coordinates) var grid = EntityManager.GetComponent(gridId); // no point changing the tile to the same thing. - if (Maps.GetTileRef(gridId, grid, coordinates).Tile.TypeId == CurrentPermission.TileType) + var tileRef = Maps.GetTileRef(gridId, grid, coordinates).Tile; + if (tileRef.TypeId == CurrentPermission.TileType && + tileRef.Mirrored == Mirrored && + tileRef.Rotation == Tile.DirectionToByte(Direction)) return; } @@ -796,9 +814,14 @@ private void RequestPlacement(EntityCoordinates coordinates) }; if (CurrentPermission.IsTile) + { message.TileType = CurrentPermission.TileType; + message.Mirrored = Mirrored; + } else + { message.EntityTemplateName = CurrentPermission.EntityType; + } // world x and y message.NetCoordinates = EntityManager.GetNetCoordinates(coordinates); diff --git a/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs b/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs index ef3376eb334..52956dacfd8 100644 --- a/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs +++ b/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs @@ -28,12 +28,15 @@ public sealed class TileSpawningUIController : UIController private readonly List _shownTiles = new(); private bool _clearingTileSelections; private bool _eraseTile; + private bool _mirroredTile; public override void Initialize() { DebugTools.Assert(_init == false); _init = true; _placement.PlacementChanged += ClearTileSelection; + _placement.DirectionChanged += OnDirectionChanged; + _placement.MirroredChanged += OnMirroredChanged; } private void StartTilePlacement(int tileType) @@ -67,6 +70,21 @@ private void OnTileEraseToggled(ButtonToggledEventArgs args) args.Button.Pressed = args.Pressed; } + private void OnTileMirroredToggled(ButtonToggledEventArgs args) + { + if (_window == null || _window.Disposed) + return; + + if (args.Pressed) + _placement.Mirrored = true; + else + _placement.Mirrored = false; + + _mirroredTile = _placement.Mirrored; + + args.Button.Pressed = args.Pressed; + } + public void ToggleWindow() { EnsureWindow(); @@ -78,6 +96,9 @@ public void ToggleWindow() else { _window.Open(); + UpdateEntityDirectionLabel(); + UpdateMirroredButton(); + _window.SearchBar.GrabKeyboardFocus(); } } @@ -94,6 +115,8 @@ private void EnsureWindow() _window.TileList.OnItemDeselected += OnTileItemDeselected; _window.EraseButton.Pressed = _eraseTile; _window.EraseButton.OnToggled += OnTileEraseToggled; + _window.MirroredButton.Pressed = _mirroredTile; + _window.MirroredButton.OnToggled += OnTileMirroredToggled; BuildTileList(); } @@ -111,6 +134,7 @@ private void ClearTileSelection(object? sender, EventArgs e) _window.TileList.ClearSelected(); _clearingTileSelections = false; _window.EraseButton.Pressed = false; + _window.MirroredButton.Pressed = _placement.Mirrored; } private void OnTileClearPressed(ButtonEventArgs args) @@ -150,6 +174,33 @@ private void OnTileItemDeselected(ItemList.ItemListDeselectedEventArgs args) _placement.Clear(); } + private void OnDirectionChanged(object? sender, EventArgs e) + { + UpdateEntityDirectionLabel(); + } + + private void UpdateEntityDirectionLabel() + { + if (_window == null || _window.Disposed) + return; + + _window.RotationLabel.Text = _placement.Direction.ToString(); + } + + private void OnMirroredChanged(object? sender, EventArgs e) + { + UpdateMirroredButton(); + } + + private void UpdateMirroredButton() + { + if (_window == null || _window.Disposed) + return; + + _mirroredTile = _placement.Mirrored; + _window.MirroredButton.Pressed = _mirroredTile; + } + private void BuildTileList(string? searchStr = null) { if (_window == null || _window.Disposed) return; diff --git a/Robust.Client/UserInterface/CustomControls/TileSpawnWindow.xaml b/Robust.Client/UserInterface/CustomControls/TileSpawnWindow.xaml index 95dbcdc242e..11f770e7817 100644 --- a/Robust.Client/UserInterface/CustomControls/TileSpawnWindow.xaml +++ b/Robust.Client/UserInterface/CustomControls/TileSpawnWindow.xaml @@ -10,7 +10,9 @@ +