From f8efa227c0a5c1153aa534b0eb6fdbcb00bd60ea Mon Sep 17 00:00:00 2001 From: haven1433 Date: Sun, 6 Nov 2022 00:05:21 -0500 Subject: [PATCH] updates from test this isn't all the updates needed after the test with wet blanket, but it's most of the small things --- .../Models/Runs/Sprites/BlockmapRun.cs | 10 +-- .../Models/Runs/Sprites/LzTilemapRun.cs | 6 +- .../ViewModels/Map/BlockEditor.cs | 1 + .../ViewModels/Map/BlockMapViewModel.cs | 74 +++++++++++++------ .../ViewModels/Map/EventTemplate.cs | 3 +- .../ViewModels/Map/IEventViewModel.cs | 2 +- .../ViewModels/Map/MapEditorViewModel.cs | 48 ++++++++---- .../ViewModels/Map/MapRepointer.cs | 12 ++- .../ViewModels/Map/MapSlider.cs | 21 +++--- .../ViewModels/Map/MapTutorialsViewModel.cs | 4 +- .../Tools/ComboBoxArrayElementViewModel.cs | 5 +- src/HexManiac.WPF/Controls/MapTab.xaml | 65 +++++++++++++--- src/HexManiac.WPF/Controls/TabView.xaml | 2 +- src/HexManiac.WPF/Controls/TabView.xaml.cs | 2 + .../Controls/TutorialControl.xaml.cs | 1 + src/HexManiac.WPF/Resources/Icons.xaml | 28 ++----- .../Resources/MarkupExtensions.cs | 2 + 17 files changed, 189 insertions(+), 97 deletions(-) diff --git a/src/HexManiac.Core/Models/Runs/Sprites/BlockmapRun.cs b/src/HexManiac.Core/Models/Runs/Sprites/BlockmapRun.cs index 3c900f7f..97a2cb03 100644 --- a/src/HexManiac.Core/Models/Runs/Sprites/BlockmapRun.cs +++ b/src/HexManiac.Core/Models/Runs/Sprites/BlockmapRun.cs @@ -172,7 +172,7 @@ namespace HexManiac.Core.Models.Runs.Sprites { return new SpriteDecorator(basicFormat, sprite, ExpectedDisplayWidth, availableRows); } - public BlockmapRun TryChangeSize(Func tokenFactory, MapDirection direction, int amount) { + public BlockmapRun TryChangeSize(Func tokenFactory, MapDirection direction, int amount, int borderWidth, int borderHeight) { if (amount == 0) return null; int xAmount = 0, yAmount = 0; @@ -207,19 +207,19 @@ namespace HexManiac.Core.Models.Runs.Sprites { // fill new rows/columns for (int y = yOffset - 1; y >= 0; y--) { - for (int x = 0; x < newWidth; x++) newData[x, y] = newData[x, y + 1]; + for (int x = 0; x < newWidth; x++) newData[x, y] = newData[x, y + borderHeight]; } if (yOffset == 0) { for (int y = BlockHeight; y < newHeight; y++) { - for (int x = 0; x < newWidth; x++) newData[x, y] = newData[x, y - 1]; + for (int x = 0; x < newWidth; x++) newData[x, y] = newData[x, y - borderHeight]; } } for (int x = xOffset - 1; x >= 0; x--) { - for (int y = 0; y < newHeight; y++) newData[x, y] = newData[x + 1, y]; + for (int y = 0; y < newHeight; y++) newData[x, y] = newData[x + borderWidth, y]; } if (xOffset == 0) { for (int x = BlockWidth; x < newWidth; x++) { - for (int y = 0; y < newHeight; y++) newData[x, y] = newData[x - 1, y]; + for (int y = 0; y < newHeight; y++) newData[x, y] = newData[x - borderWidth, y]; } } diff --git a/src/HexManiac.Core/Models/Runs/Sprites/LzTilemapRun.cs b/src/HexManiac.Core/Models/Runs/Sprites/LzTilemapRun.cs index cd8b4d62..9ccdaaf1 100644 --- a/src/HexManiac.Core/Models/Runs/Sprites/LzTilemapRun.cs +++ b/src/HexManiac.Core/Models/Runs/Sprites/LzTilemapRun.cs @@ -140,14 +140,14 @@ namespace HavenSoft.HexManiac.Core.Models.Runs.Sprites { return (paletteIndex, hFlip, vFlip, tileIndex); } - public static void WriteTileData(byte[] lzRunData, int index, int paletteIndex, bool hFlip, bool vFlip, int tileIndex) { + public static void WriteTileData(byte[] tileData, int index, int paletteIndex, bool hFlip, bool vFlip, int tileIndex) { int packedData = 0; packedData |= tileIndex; if (hFlip) packedData |= 1 << 10; if (vFlip) packedData |= 1 << 11; packedData |= paletteIndex << 12; - lzRunData[index * 2 + 0] = (byte)packedData; - lzRunData[index * 2 + 1] = (byte)(packedData >> 8); + tileData[index * 2 + 0] = (byte)packedData; + tileData[index * 2 + 1] = (byte)(packedData >> 8); } public static int[,] GetPixels(byte[] mapData, byte[] tiles, TilemapFormat format, int bytesPerTile) { diff --git a/src/HexManiac.Core/ViewModels/Map/BlockEditor.cs b/src/HexManiac.Core/ViewModels/Map/BlockEditor.cs index ae38457f..25934747 100644 --- a/src/HexManiac.Core/ViewModels/Map/BlockEditor.cs +++ b/src/HexManiac.Core/ViewModels/Map/BlockEditor.cs @@ -460,6 +460,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { images[hoverTile].Fill(newImage.PixelData); BlocksChanged?.Invoke(this, blocks); tutorials.Complete(Tutorial.ClickBlock_DrawTile); + } public void GetSelectionFromTile(IPixelViewModel tileImage) { diff --git a/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs b/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs index f463a7db..065e55d3 100644 --- a/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs @@ -364,6 +364,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { element.SetAddress("fish", Pointer.NULL); wildDataIndex = wildTable.ElementCount - 1; if (wildTable.Start != originalStart) InformRepoint(new("Wild", wildTable.Start)); + NotifyPropertyChanged(nameof(HasWildData)); + viewPort.ChangeHistory.ChangeCompleted(); } viewPort.Goto.Execute(wildTable.Start + wildTable.ElementLength * wildDataIndex); tutorials.Complete(Tutorial.ToolbarButton_GotoWildData); @@ -489,17 +491,18 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { blockPixels = null; eventRenders = null; borderBlock = null; + wildDataIndex = int.MinValue; RefreshMapSize(); if (blockEditor != null) { - var oldShowTiles = blockEditor.ShowTiles; - var selection = blockEditor.BlockIndex; blockEditor.BlocksChanged -= HandleBlocksChanged; blockEditor.BlockAttributesChanged -= HandleBlockAttributesChanged; BlockEditor.AutoscrollTiles -= HandleAutoscrollTiles; var oldBlockEditor = blockEditor; blockEditor = null; - BlockEditor.BlockIndex = selection; - BlockEditor.ShowTiles = oldShowTiles; + BlockEditor.BlockIndex = oldBlockEditor.BlockIndex; + (BlockEditor.TileSelectionX, BlockEditor.TileSelectionY) = (oldBlockEditor.TileSelectionX, oldBlockEditor.TileSelectionY); + BlockEditor.PaletteSelection = oldBlockEditor.PaletteSelection; + BlockEditor.ShowTiles = oldBlockEditor.ShowTiles; oldBlockEditor.ShowTiles = false; NotifyPropertyChanged(nameof(BlockEditor)); } @@ -512,8 +515,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { oldBorderEditor.ShowBorderPanel = false; NotifyPropertyChanged(nameof(BorderEditor)); } - NotifyPropertyChanged(nameof(BlockRenders)); - NotifyPropertyChanged(nameof(BlockPixels)); + NotifyPropertiesChanged(nameof(BlockRenders), nameof(BlockPixels), nameof(HasWildData)); } public void RedrawEvents() { @@ -545,7 +547,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var option = MapRepointer.GetMapBankForNewMap( "Maps are organized into banks. The game doesn't care, so you can use the banks however you like." + Environment.NewLine + - "Which map bank do you want use for the new mapa?"); + "Which map bank do you want to use for the new map?"); if (option == -1) return null; var token = tokenFactory(); MapModel thisMap = new(GetMapModel()); @@ -1003,10 +1005,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { // get sliders for size expansion var centerX = (LeftEdge + RightEdge - MapSlider.SliderSize) / 2; var centerY = (TopEdge + BottomEdge - MapSlider.SliderSize) / 2; - yield return new ExpansionSlider(ResizeMapData, id + 0, UpDown, GetConnectionCommands(connections, MapDirection.Up), left: centerX, bottom: TopEdge); - yield return new ExpansionSlider(ResizeMapData, id + 1, UpDown, GetConnectionCommands(connections, MapDirection.Down), left: centerX, top: BottomEdge); - yield return new ExpansionSlider(ResizeMapData, id + 2, LeftRight, GetConnectionCommands(connections, MapDirection.Left), right: LeftEdge, top: centerY); - yield return new ExpansionSlider(ResizeMapData, id + 3, LeftRight, GetConnectionCommands(connections, MapDirection.Right), left: RightEdge, top: centerY); + yield return new ExpansionSlider(ResizeMapData, id + 0, ExtendUp, GetConnectionCommands(connections, MapDirection.Up), left: centerX, bottom: TopEdge); + yield return new ExpansionSlider(ResizeMapData, id + 1, ExtendDown, GetConnectionCommands(connections, MapDirection.Down), left: centerX, top: BottomEdge); + yield return new ExpansionSlider(ResizeMapData, id + 2, ExtendLeft, GetConnectionCommands(connections, MapDirection.Left), right: LeftEdge, top: centerY); + yield return new ExpansionSlider(ResizeMapData, id + 3, ExtendRight, GetConnectionCommands(connections, MapDirection.Right), left: RightEdge, top: centerY); } private IEnumerable GetConnectionCommands(IReadOnlyList connections, MapDirection direction) { @@ -1066,20 +1068,25 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var layout = GetLayout(map); var run = model.GetNextRun(layout.GetAddress("blockmap")) as BlockmapRun; if (run == null) return; - - var newRun = run.TryChangeSize(tokenFactory, direction, amount); + var borderWidth = layout.HasField("borderwidth") ? layout.GetValue("borderwidth") : 2; + var borderHeight = layout.HasField("borderheight") ? layout.GetValue("borderheight") : 2; + var newRun = run.TryChangeSize(tokenFactory, direction, amount, borderWidth, borderHeight); if (newRun != null) { var tileSize = (int)(16 * spriteScale); if (direction == MapDirection.Left) LeftEdge -= amount * tileSize; if (direction == MapDirection.Up) TopEdge -= amount * tileSize; - foreach (var connection in GetConnections(map)) { + foreach (var connection in GetConnections(map, group, this.map)) { if (direction == MapDirection.Left) { if (connection.Direction == MapDirection.Down || connection.Direction == MapDirection.Up) { connection.Offset += amount; + var inverse = connection.GetInverse(); + if (inverse != null) inverse.Offset -= amount; } } else if (direction == MapDirection.Up) { if (connection.Direction == MapDirection.Left || connection.Direction == MapDirection.Right) { connection.Offset += amount; + var inverse = connection.GetInverse(); + if (inverse != null) inverse.Offset -= amount; } } } @@ -1104,7 +1111,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var option = MapRepointer.GetMapBankForNewMap( "Maps are organized into banks. The game doesn't care, so you can use the banks however you like." + Environment.NewLine + - "Which map bank do you want use for the new mapa?"); + "Which map bank do you want to use for the new map?"); if (option == -1) return; var map = GetMapModel(); @@ -1115,7 +1122,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { if (connections.Start != originalConnectionStart) InformRepoint(new("Connections", connections.Start)); connectionsAndCount.SetValue("count", connections.ElementCount + 1); var table = new ModelTable(model, connections.Start, tokenFactory, connections); - var newConnection = new ConnectionModel(table[connections.ElementCount]); + var newConnection = new ConnectionModel(table[connections.ElementCount], group, this.map); newConnection.Offset = info.Offset; newConnection.Direction = info.Direction; @@ -1131,6 +1138,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { RefreshMapSize(); NeighborsChanged.Raise(this); + viewPort.ChangeHistory.ChangeCompleted(); } private BlockMapViewModel CreateNewMap(ModelDelta token, int bank, int width, int height) { @@ -1197,12 +1205,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { RefreshMapSize(); NeighborsChanged.Raise(this); + viewPort.ChangeHistory.ChangeCompleted(); } private void RemoveConnections(IReadOnlyList toRemove) { var token = tokenFactory(); var map = GetMapModel(); - var connections = GetConnections(map); + var connections = GetConnections(map, group, this.map); for (int i = 0; i < toRemove.Count; i++) { for (int j = toRemove[i] - i + 1; j < connections.Count - i; j++) { connections[j - 1].Direction = connections[j].Direction; @@ -1223,6 +1232,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { RefreshMapSize(); NeighborsChanged.Raise(this); + viewPort.ChangeHistory.ChangeCompleted(); } private ConnectionModel AddConnection(ConnectionInfo info) { @@ -1255,7 +1265,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { model.ObserveRunWritten(token, connections); var table = new ModelTable(model, connections.Start, tokenFactory, connections); - var newConnection = new ConnectionModel(table[count]); + var newConnection = new ConnectionModel(table[count], group, this.map); token.ChangeData(model, table[count].Start, new byte[12]); newConnection.Direction = info.Direction; return newConnection; @@ -1532,7 +1542,16 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { for (int y = 0; y < height + border.North + border.South; y++) { for (int x = 0; x < width + border.West + border.East; x++) { if (y < border.North || x < border.West || y >= border.North + height || x >= border.West + width) { - if (x % borderWidth == 0 && y % borderHeight == 0) canvas.Draw(borderBlock, x * 16, y * 16); + var (xEdge, yEdge) = (x - border.West - width, y - border.North - height); + var (rightEdge, bottomEdge) = (xEdge >= 0, yEdge >= 0); + // top/left + if (!rightEdge && !bottomEdge && x % borderWidth == 0 && y % borderHeight == 0) canvas.Draw(borderBlock, x * 16, y * 16); + // right edge + if (rightEdge && !bottomEdge && xEdge % borderWidth == 0 && y % borderHeight == 0) canvas.Draw(borderBlock, x * 16, y * 16); + // bottom edge + if (!rightEdge && bottomEdge && x % borderWidth == 0 && yEdge % borderHeight == 0) canvas.Draw(borderBlock, x * 16, y * 16); + // bottom right corner + if (rightEdge && bottomEdge && xEdge % borderWidth == 0 && yEdge % borderHeight == 0) canvas.Draw(borderBlock, x * 16, y * 16); continue; } var data = model.ReadMultiByteValue(start + ((y - border.North) * width + x - border.West) * 2, 2); @@ -1634,9 +1653,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private IReadOnlyList GetConnections() { var map = GetMapModel(model, group, this.map, tokenFactory); - return GetConnections(map); + return GetConnections(map, group, this.map); } - public static IReadOnlyList GetConnections(ModelArrayElement map) { + public static IReadOnlyList GetConnections(ModelArrayElement map, int bankNum, int mapNum) { if (map == null) return null; var connectionsAndCountTable = map.GetSubTable("connections"); var list = new List(); @@ -1646,7 +1665,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { if (count == 0) return list; var connections = connectionsAndCount.GetSubTable("connections"); if (connections == null) return new ConnectionModel[0]; - for (int i = 0; i < count; i++) list.Add(new(connections[i])); + for (int i = 0; i < count; i++) list.Add(new(connections[i], bankNum, mapNum)); return list; } @@ -1781,6 +1800,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { blockPixels = null; pixelData = null; NotifyPropertiesChanged(nameof(BlockPixels), nameof(PixelData), nameof(BlockRenders)); + viewPort.ChangeHistory.ChangeCompleted(); } private void HandleBorderChanged(object sender, EventArgs e) { @@ -1977,9 +1997,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public class ConnectionModel { private readonly ModelArrayElement connection; + private readonly int sourceGroup, sourceMap; public IDataModel Model => connection.Model; public Func Tokens => () => connection.Token; - public ConnectionModel(ModelArrayElement connection) => this.connection = connection; + public ConnectionModel(ModelArrayElement connection, int sourceGroup, int sourceMap) => (this.connection, this.sourceGroup, this.sourceMap) = (connection, sourceGroup, sourceMap); public MapDirection Direction { get => (MapDirection)connection.GetValue("direction"); @@ -2003,6 +2024,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { set => connection.SetValue("mapNum", value); } + public ConnectionModel GetInverse() { + var direction = Direction.Reverse(); + var map = BlockMapViewModel.GetMapModel(Model, MapGroup, MapNum, Tokens); + var neighbors = BlockMapViewModel.GetConnections(map, MapGroup, MapNum); + return neighbors.FirstOrDefault(c => c.MapGroup == sourceGroup && c.MapNum == sourceMap && c.Direction == direction); + } + public void Clear(IDataModel model, ModelDelta token) { token.ChangeData(model, connection.Start, connection.Length.Range(i => (byte)0xFF).ToList()); } diff --git a/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs b/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs index 25e0d9f1..dfc6b15d 100644 --- a/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs +++ b/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs @@ -57,6 +57,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public EventTemplate(IDataModel model, ScriptParser parser, IReadOnlyList owGraphics) { (this.model, this.parser) = (model, parser); RefreshLists(owGraphics); + if (model.IsFRLG()) UseNationalDex = true; } public void RefreshLists(IReadOnlyList owGraphics) { @@ -70,7 +71,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { AvailableTemplateTypes.Add(TemplateType.Tutor); GraphicsOptions.Clear(); - for (int i = 0; i < owGraphics.Count; i++) GraphicsOptions.Add(VisualComboOption.CreateFromSprite(i.ToString(), owGraphics[i].PixelData, owGraphics[i].PixelWidth, i)); + for (int i = 0; i < owGraphics.Count; i++) GraphicsOptions.Add(VisualComboOption.CreateFromSprite(i.ToString(), owGraphics[i].PixelData, owGraphics[i].PixelWidth, i, 2)); TypeOptions.Clear(); foreach (var type in model.GetTableModel(HardcodeTablesModel.TypesTableName)) { diff --git a/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs b/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs index 7b9a1366..fff0218d 100644 --- a/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs @@ -691,7 +691,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public ObjectEventViewModel(Action gotoAddress, ModelArrayElement objectEvent, IReadOnlyList sprites) : base(objectEvent, "objectCount") { this.gotoAddress = gotoAddress; - for (int i = 0; i < sprites.Count; i++) Options.Add(VisualComboOption.CreateFromSprite(i.ToString(), sprites[i].PixelData, sprites[i].PixelWidth, i)); + for (int i = 0; i < sprites.Count; i++) Options.Add(VisualComboOption.CreateFromSprite(i.ToString(), sprites[i].PixelData, sprites[i].PixelWidth, i, 2)); objectEvent.Model.TryGetList("FacingOptions", out var list); foreach (var item in list) FacingOptions.Add(item); foreach (var item in objectEvent.Model.GetOptions(HardcodeTablesModel.TrainerClassNamesTable)) ClassOptions.Add(item); diff --git a/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs b/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs index 73be92f2..379b643d 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs @@ -10,9 +10,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using System.Windows.Input; -using static IronPython.Modules._ast; namespace HavenSoft.HexManiac.Core.ViewModels.Map { /// @@ -62,6 +60,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { NotifyPropertyChanged(); NotifyPropertyChanged(nameof(ShowEventPanel)); ShowHeaderPanel = false; + if (selectedEvent == null) primaryMap.DeselectEvent(); primaryMap.BlockEditor.ShowTiles = false; DrawBlockIndex = -1; CollisionIndex = -1; @@ -88,8 +87,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private int PrimaryTiles { get; } - private string hoverPoint; + private string hoverPoint, zoomLevel; public string HoverPoint { get => hoverPoint; set => Set(ref hoverPoint, value); } + public string ZoomLevel { get => zoomLevel; set => Set(ref zoomLevel, value); } public MapTutorialsViewModel Tutorials { get; } @@ -296,6 +296,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { templates = new(model, viewPort.Tools.CodeTool.ScriptParser, map.AllOverworldSprites); UpdatePrimaryMap(map); for (int i = 0; i < 0x40; i++) CollisionOptions.Add(i.ToString("X2")); + + ZoomLevel = "1x Zoom"; } #endregion @@ -331,6 +333,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { primaryMap.AutoscrollTiles -= HandleAutoscrollTiles; primaryMap.HideSidePanels -= HandleHideSidePanels; primaryMap.RequestChangeMap -= HandleMapChangeRequest; + primaryMap.DeselectEvent(); } primaryMap = map; primaryMap.BlockEditor.BlockIndex = drawBlockIndex; @@ -438,6 +441,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { while (Math.Abs(p.Y - drawSource.Y) % tilesToDraw.GetLength(1) != 0) p -= new Point(0, 1); } UpdateHover(p.X, p.Y, tilesToDraw.GetLength(0), tilesToDraw.GetLength(1)); + HoverPoint = string.Empty; } else { var map = MapUnderCursor(x, y); if (map == null) return EmptyTooltip; @@ -516,7 +520,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { EventDown(x, y, ev, click); return; } else { - primaryMap.DeselectEvent(); SelectedEvent = null; Tutorials.Complete(Tutorial.ClickMap_UnselectEvent); } @@ -583,7 +586,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private void RectangleDrawMove(double x, double y) { var map = MapUnderCursor(x, y); - if (map != null) { + if (tilesToDraw == null && drawBlockIndex < 0 && collisionIndex < 0) { + interactionType = PrimaryInteractionType.None; + } else if (map != null) { ResetFromRectangleBackup(); lastDraw = ToTilePosition(x, y); if (lastDraw != drawSource) Tutorials.Complete(Tutorial.ControlClick_FillRect); @@ -763,7 +768,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { int[,] tilesToDraw; Point selectDownPosition; private bool drawMultipleTiles; - public bool DrawMultipleTiles { get => drawMultipleTiles; private set => Set(ref drawMultipleTiles, value); } + public bool DrawMultipleTiles { + get => drawMultipleTiles; + private set => Set(ref drawMultipleTiles, value, arg => { if (!drawMultipleTiles) tilesToDraw = null; }); + } private bool blockEditorVisible; public bool BlockEditorVisible { get => blockEditorVisible; private set => Set(ref blockEditorVisible, value); } @@ -837,20 +845,30 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { tilesToDraw = new int[width, height]; var scale = (width < 4 && height < 4) ? 2 : 1; var canvas = new CanvasPixelViewModel(width * 16, height * 16) { SpriteScale = scale }; + bool fillError = false; for (int xx = 0; xx < width; xx++) { for (int yy = 0; yy < height; yy++) { var (tX, tY) = ToMapPosition(left + xx, top + yy); var block = primaryMap.GetBlock(tX, tY); + if (block.blockIndex == -1 || block.collisionIndex == -1) { + fillError = true; + break; + } tilesToDraw[xx, yy] = (block.collisionIndex << 10) | block.blockIndex; canvas.Draw(primaryMap.BlockRenders[block.blockIndex], xx * 16, yy * 16); } } - Tutorials.Complete(Tutorial.RightDragMap_SelectBlocks); - MultiTileDrawRender = canvas; - DrawMultipleTiles = true; - BlockEditorVisible = false; - PrimaryMap.BlockEditor.ShowTiles = false; - UpdateHover(left, top, width, height); + if (fillError) { + tilesToDraw = null; + UpdateHover(selectMovePosition.X, selectMovePosition.Y, 1, 1); + } else { + Tutorials.Complete(Tutorial.RightDragMap_SelectBlocks); + MultiTileDrawRender = canvas; + DrawMultipleTiles = true; + BlockEditorVisible = false; + PrimaryMap.BlockEditor.ShowTiles = false; + UpdateHover(left, top, width, height); + } } private Point ToTilePosition(double x, double y) { @@ -1003,6 +1021,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { } } } + if (address == Pointer.NULL) tips.Add("(no script)"); return tips; } @@ -1079,6 +1098,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { BlockMapViewModel newMap = primaryMap.CreateMapForWarp(warpContext); if (newMap == null) return; NavigateTo(newMap.MapID); + history.ChangeCompleted(); } public void DeleteCurrentEvent() { @@ -1095,6 +1115,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { map.Scale(x, y, enlarge); map.IncludeBorders = map.SpriteScale <= 1; UpdatePrimaryMap(map); + if (map.SpriteScale >= 1) ZoomLevel = $"{(int)map.SpriteScale}x Zoom"; + else ZoomLevel = $"1/{(int)Math.Round(1 / map.SpriteScale)}x Zoom"; Tutorials.Complete(Tutorial.Wheel_ZoomMap); } @@ -1122,8 +1144,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { if (selectedEvent == null) return; selectedEvent.Delete(); SelectedEvent = null; - primaryMap.DeselectEvent(); primaryMap.RedrawEvents(); + history.ChangeCompleted(); } public void Cancel() { diff --git a/src/HexManiac.Core/ViewModels/Map/MapRepointer.cs b/src/HexManiac.Core/ViewModels/Map/MapRepointer.cs index 0af375ef..c455736b 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapRepointer.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapRepointer.cs @@ -198,7 +198,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { } private void ExecuteDuplicateMap() { - var option = GetMapBankForNewMap("Duplicate map into which bank?"); + var option = GetMapBankForNewMap( + "Maps are organized into banks. The game doesn't care, so you can use the banks however you like." + + Environment.NewLine + + "Duplicate map into which bank?" + ); if (option == -1) return; var table = AddNewMapToBank(option); var newMap = CreateNewMap(history.CurrentChange); @@ -511,9 +515,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public int GetMapBankForNewMap(string prompt) { var tokenFactory = () => history.CurrentChange; var mapBanks = new ModelTable(model, model.GetTable(HardcodeTablesModel.MapBankTable).Start, tokenFactory); - var enumViewModel = new EnumViewModel(mapBanks.Count.Range(i => i.ToString()).ToArray()); + var options = mapBanks.Count.Range(i => i.ToString()).ToList(); + options.Add("Create New Bank"); + var enumViewModel = new EnumViewModel(options.ToArray()); var option = fileSystem.ShowOptions( - "Pick a group", + "Pick a bank", prompt, new[] { new[] { enumViewModel } }, new VisualOption { Index = 1, Option = "OK", ShortDescription = "Insert New Map" }); diff --git a/src/HexManiac.Core/ViewModels/Map/MapSlider.cs b/src/HexManiac.Core/ViewModels/Map/MapSlider.cs index fea6368c..e0b68e15 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapSlider.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapSlider.cs @@ -51,13 +51,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { } public enum MapSliderIcons { - None, LeftRight, UpDown, X + LeftRight, UpDown, ExtendLeft, ExtendRight, ExtendUp, ExtendDown, } public class ConnectionSlider : MapSlider { private readonly MapTutorialsViewModel tutorials; private Action notify; - private ConnectionModel connection, inverse; + private ConnectionModel connection; public override string Tooltip => "Drag to adjust the connection between the maps."; @@ -66,14 +66,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { this.tutorials = tutorials; var group = connection.MapGroup; var index = connection.MapNum; - - var direction = connection.Direction.Reverse(); - var map = BlockMapViewModel.GetMapModel(connection.Model, group, index, connection.Tokens); - var neighbors = BlockMapViewModel.GetConnections(map); - inverse = neighbors.FirstOrDefault(c => c.MapGroup == sourceMapInfo.group && c.MapNum == sourceMapInfo.num && c.Direction == direction); } public override void Move(int x, int y) { + var inverse = connection.GetInverse(); if (Icon == MapSliderIcons.LeftRight) { connection.Offset += x; if (inverse != null) inverse.Offset = -connection.Offset; @@ -107,7 +103,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public class ExpansionSlider : MapSlider { private Action resize; - public override string Tooltip => $"Drag to change the {(Icon == MapSliderIcons.LeftRight ? "width" : "height")} of the map"; + public override string Tooltip => $"Drag to change the {(Icon == MapSliderIcons.LeftRight ? "width" : "height")} of the map." + Environment.NewLine + + "Right-Click to add or remove a connection."; public ExpansionSlider(Action resize, int id, MapSliderIcons icon, IEnumerable contextItems, int left = int.MinValue, int top = int.MinValue, int right = int.MinValue, int bottom = int.MinValue) : base(id, icon, left, top, right, bottom) { foreach (var item in contextItems) ContextItems.Add(item); @@ -115,13 +112,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { } public override void Move(int x, int y) { - if (Icon == MapSliderIcons.LeftRight && !AnchorLeftEdge) { + if (Icon == MapSliderIcons.ExtendLeft && !AnchorLeftEdge) { resize(MapDirection.Left, -x); - } else if (Icon == MapSliderIcons.LeftRight && AnchorLeftEdge) { + } else if (Icon == MapSliderIcons.ExtendRight && AnchorLeftEdge) { resize(MapDirection.Right, x); - } else if (Icon == MapSliderIcons.UpDown && !AnchorTopEdge) { + } else if (Icon == MapSliderIcons.ExtendUp && !AnchorTopEdge) { resize(MapDirection.Up, -y); - } else if (Icon == MapSliderIcons.UpDown && AnchorTopEdge) { + } else if (Icon == MapSliderIcons.ExtendDown && AnchorTopEdge) { resize(MapDirection.Down, y); } } diff --git a/src/HexManiac.Core/ViewModels/Map/MapTutorialsViewModel.cs b/src/HexManiac.Core/ViewModels/Map/MapTutorialsViewModel.cs index 12d896c1..d3a4e6e9 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapTutorialsViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapTutorialsViewModel.cs @@ -78,10 +78,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { Tutorials.Add(new(LeftClick, "Select Block", "Click a block in the Blocks panel to select it.")); Tutorials.Add(new(LeftClick, "Draw Block", "Click/Drag over the map to draw with the selected block.")); Tutorials.Add(new("UndoArrow", "Undo", "Use the Undo button in the toolbar to undo any mistakes.")); - Tutorials.Add(new("FillBucket", "Paint Blocks", "Double-Click over the map to paint with the selected block.")); + Tutorials.Add(new("FillBucket", "Paint Blocks", "Double-Click over the map to paint an area with the selected block.")); Tutorials.Add(new("CtrlKey", "Fill Rectangle", "Hold Control, then left-click and drag over an area to create a rectangle of blocks.")); Tutorials.Add(new("EscapeKey", "Unselect Block", "Press the escape key to unselect a block.")); - Tutorials.Add(new(LeftClick, "Select Blocks", "Left-Click and drag on the Blocks panel to select multiple blocks. When multiple blocks are selected, you can draw, but not paint.")); + Tutorials.Add(new(LeftClick, "Select Blocks", "Left-Click and drag on the Blocks panel to select multiple blocks. When multiple blocks are selected, you can draw, but not double-click fill.")); } // editing blocks diff --git a/src/HexManiac.Core/ViewModels/Tools/ComboBoxArrayElementViewModel.cs b/src/HexManiac.Core/ViewModels/Tools/ComboBoxArrayElementViewModel.cs index dacad618..65086565 100644 --- a/src/HexManiac.Core/ViewModels/Tools/ComboBoxArrayElementViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Tools/ComboBoxArrayElementViewModel.cs @@ -31,14 +31,15 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { public int PixelWidth { get; private set; } public int PixelHeight { get; private set; } public short[] PixelData { get; private set; } - public double SpriteScale => 1; + public double SpriteScale { get; init; } = 1; public override bool DisplayAsText => false; private VisualComboOption(string text, int index) : base(text, index) { PixelData = new short[0]; } - public static VisualComboOption CreateFromSprite(string text, short[] pixelData, int width, int index) => new VisualComboOption(text, index) { + public static VisualComboOption CreateFromSprite(string text, short[] pixelData, int width, int index, double scale = 1) => new VisualComboOption(text, index) { PixelData = pixelData, PixelWidth = width, PixelHeight = pixelData.Length / width, + SpriteScale = scale }; } diff --git a/src/HexManiac.WPF/Controls/MapTab.xaml b/src/HexManiac.WPF/Controls/MapTab.xaml index 4bebe04b..69766a00 100644 --- a/src/HexManiac.WPF/Controls/MapTab.xaml +++ b/src/HexManiac.WPF/Controls/MapTab.xaml @@ -40,6 +40,7 @@ + @@ -162,8 +163,31 @@ + + + + + + + + + + - + @@ -757,7 +781,7 @@ - @@ -776,6 +800,18 @@ + + + + + + + + + + + + @@ -793,7 +829,10 @@ - + + + + - + + + + + @@ -934,11 +977,11 @@ - - - @@ -1039,7 +1082,7 @@ - + @@ -1057,7 +1100,7 @@