updates from test

this isn't all the updates needed after the test with wet blanket, but it's most of the small things
This commit is contained in:
haven1433
2022-11-06 00:05:21 -05:00
parent 4e8fab433e
commit f8efa227c0
17 changed files with 189 additions and 97 deletions

View File

@@ -172,7 +172,7 @@ namespace HexManiac.Core.Models.Runs.Sprites {
return new SpriteDecorator(basicFormat, sprite, ExpectedDisplayWidth, availableRows);
}
public BlockmapRun TryChangeSize(Func<ModelDelta> tokenFactory, MapDirection direction, int amount) {
public BlockmapRun TryChangeSize(Func<ModelDelta> 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];
}
}

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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<IMenuCommand> GetConnectionCommands(IReadOnlyList<ConnectionModel> 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<int> 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<ConnectionModel> GetConnections() {
var map = GetMapModel(model, group, this.map, tokenFactory);
return GetConnections(map);
return GetConnections(map, group, this.map);
}
public static IReadOnlyList<ConnectionModel> GetConnections(ModelArrayElement map) {
public static IReadOnlyList<ConnectionModel> GetConnections(ModelArrayElement map, int bankNum, int mapNum) {
if (map == null) return null;
var connectionsAndCountTable = map.GetSubTable("connections");
var list = new List<ConnectionModel>();
@@ -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<ModelDelta> 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());
}

View File

@@ -57,6 +57,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public EventTemplate(IDataModel model, ScriptParser parser, IReadOnlyList<IPixelViewModel> owGraphics) {
(this.model, this.parser) = (model, parser);
RefreshLists(owGraphics);
if (model.IsFRLG()) UseNationalDex = true;
}
public void RefreshLists(IReadOnlyList<IPixelViewModel> 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)) {

View File

@@ -691,7 +691,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public ObjectEventViewModel(Action<int> gotoAddress, ModelArrayElement objectEvent, IReadOnlyList<IPixelViewModel> 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);

View File

@@ -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 {
/// <summary>
@@ -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() {

View File

@@ -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" });

View File

@@ -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<MapDirection, int> 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<MapDirection, int> resize, int id, MapSliderIcons icon, IEnumerable<IMenuCommand> 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);
}
}

View File

@@ -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

View File

@@ -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
};
}

View File

@@ -40,6 +40,7 @@
<!-- Blocks Panel -->
<DockPanel Grid.Column="0" Grid.RowSpan="2" Panel.ZIndex="1" Name="BlockPanel" Background="{DynamicResource Background}">
<Rectangle Fill="{DynamicResource Backlight}" Width="1" DockPanel.Dock="Right" />
<local:AngleBorder DockPanel.Dock="Top" Content="Blocks" Direction="Out" Width="100" HorizontalContentAlignment="Center" Margin="0,0,0,4" />
<Grid Width="{Binding Blocks.ScaledWidth}" MinHeight="100" DockPanel.Dock="Top">
<Grid.RowDefinitions>
@@ -162,8 +163,31 @@
<Decorator Visibility="{Binding BlockEditorVisible, Converter={StaticResource BoolToVisibility}}" Margin="0,0,10,0">
<local:SelectedBlockEditor x:Name="BlockEditor" DataContext="{Binding PrimaryMap.BlockEditor}"/>
</Decorator>
<TextBlock Foreground="{DynamicResource Secondary}" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding BlockEditorVisible}" Value="False" />
<Condition Binding="{Binding DrawMultipleTiles}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Visibility" Value="Visible" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<Run Text="(No Block Selected)" FontStyle="Italic" />
<LineBreak />
<Run Text="Click a block below to select it, or" />
<LineBreak />
<Run Text="Right-Click the map to select a block." />
</TextBlock>
</Grid>
<ScrollViewer Name="BlockViewer">
<ScrollViewer Name="BlockViewer" Margin="0,0,-1,0">
<Canvas Width="{Binding Blocks.ScaledWidth}" Height="{Binding Blocks.ScaledHeight}">
<local:PixelImage DataContext="{Binding Blocks}" MouseDown="BlocksDown" MouseMove="BlocksMove" MouseUp="BlocksUp" />
<ContentControl IsHitTestVisible="False" Canvas.Left="{Binding HighlightBlockX}" Canvas.Top="{Binding HighlightBlockY}" Margin="-1,-1,0,0">
@@ -757,7 +781,7 @@
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border MouseLeftButtonDown="ShifterDown" MouseMove="ShifterMove" MouseLeftButtonUp="ShifterUp" Width="20" Height="20"
<Border Name="PathBackground" MouseLeftButtonDown="ShifterDown" MouseMove="ShifterMove" MouseLeftButtonUp="ShifterUp" Width="20" Height="20"
Background="{DynamicResource Backlight}" ToolTipService.InitialShowDelay="0">
<Path Name="Path" Fill="{DynamicResource Secondary}" Stretch="Uniform" />
<Border.ContextMenu>
@@ -776,6 +800,18 @@
</Border.ToolTip>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Icon}" Value="ExtendLeft">
<Setter TargetName="Path" Property="Data" Value="{res:Icon LineArrowLeft}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Icon}" Value="ExtendUp">
<Setter TargetName="Path" Property="Data" Value="{res:Icon LineArrowUp}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Icon}" Value="ExtendRight">
<Setter TargetName="Path" Property="Data" Value="{res:Icon LineArrowRight}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Icon}" Value="ExtendDown">
<Setter TargetName="Path" Property="Data" Value="{res:Icon LineArrowDown}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Icon}" Value="LeftRight">
<Setter TargetName="Path" Property="Data" Value="{res:Icon ArrowsLeftRight}"/>
</DataTrigger>
@@ -793,7 +829,10 @@
<TranslateTransform X="{Binding HighlightCursorX, Mode=OneWay}" Y="{Binding HighlightCursorY, Mode=OneWay}"/>
</Rectangle.RenderTransform>
</Rectangle>
<TextBlock Width="100" Text="{Binding HoverPoint}" Background="{DynamicResource Background}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
<Canvas Width="160" Height="16" Background="{DynamicResource Background}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<TextBlock Canvas.Left="20" Text="{Binding HoverPoint}" Background="{DynamicResource Background}" />
<TextBlock Canvas.Left="90" Text="{Binding ZoomLevel}" Background="{DynamicResource Background}" />
</Canvas>
<!-- Tile Panel -->
<Border DataContext="{Binding PrimaryMap.BlockEditor}" Grid.Column="1" Grid.RowSpan="2" Panel.ZIndex="3" HorizontalAlignment="Left"
@@ -807,7 +846,11 @@
<local:AngleBorder HorizontalAlignment="Center" HorizontalContentAlignment="Center" Width="100" Content="Tiles" Direction="Out" />
</Grid>
<StackPanel DockPanel.Dock="Top" HorizontalAlignment="Center" Orientation="Horizontal" Margin="0,0,0,4">
<local:PixelImage DataContext="{Binding DrawTileRender}" Margin="0,0,50,0" TransparentBrush="{DynamicResource Background}" />
<TextBlock Text="Selected Tile:" VerticalAlignment="Center" Margin="5,0"/>
<Border Padding="2" BorderBrush="{DynamicResource Secondary}" BorderThickness="1" Margin="0,0,30,0" VerticalAlignment="Center">
<local:PixelImage DataContext="{Binding DrawTileRender}" TransparentBrush="{DynamicResource Background}" />
</Border>
<TextBlock Text="Selected Palette:" VerticalAlignment="Center" Margin="5,0"/>
<ComboBox ItemsSource="{Binding Palettes}" SelectedIndex="{Binding PaletteSelection}" />
</StackPanel>
<ScrollViewer Name="TileViewer" VerticalAlignment="Top">
@@ -934,11 +977,11 @@
<!-- Toolbar -->
<StackPanel Grid.Column="1" Orientation="Horizontal" Background="{DynamicResource Background}" Margin="1,0,1,5" Panel.ZIndex="2">
<StackPanel Margin="5,0">
<Button Width="12" Height="12" Padding="1" Command="{res:MethodCommand Zoom}" CommandParameter="{x:Static map:ZoomDirection.Enlarge}">
<StackPanel Margin="5,0" HorizontalAlignment="Center">
<Button Width="12" Height="12" Padding="1" Command="{res:MethodCommand Zoom}" CommandParameter="{x:Static map:ZoomDirection.Enlarge}" ToolTip="Zoom In">
<Path Stretch="Uniform" Fill="{DynamicResource Secondary}" Data="{res:Icon ThinPlus}" />
</Button>
<Button Width="12" Height="12" Padding="1" Command="{res:MethodCommand Zoom}" CommandParameter="{x:Static map:ZoomDirection.Shrink}">
<Button Width="12" Height="12" Padding="1" Command="{res:MethodCommand Zoom}" CommandParameter="{x:Static map:ZoomDirection.Shrink}" ToolTip="Zoom Out">
<Path Stretch="Uniform" Fill="{DynamicResource Secondary}" Data="{res:Icon ThinMinus}" />
</Button>
</StackPanel>
@@ -1039,7 +1082,7 @@
</Style.Triggers>
</Style>
</StackPanel.Style>
<!-- This trainer prefers to use a level <15> <Bug> pokemon from first <50> pokemon in the <Regional> pokedex. -->
<!-- This trainer prefers to use a level <15> <Bug> pokemon from first <50> pokemon in the <National> pokedex. -->
<StackPanel Orientation="Horizontal">
<TextBlock Text="This trainer prefers to use" />
</StackPanel>
@@ -1057,7 +1100,7 @@
<local:AngleButton Width="60" Padding="0" Height="18" Direction="Out" IsChecked="{Binding UseNationalDex}">
<local:AngleButton.Style>
<Style TargetType="local:AngleButton" BasedOn="{StaticResource {x:Type local:AngleButton}}">
<Setter Property="Content" Value="Regional" />
<Setter Property="Content" Value="Hoenn" />
<Style.Triggers>
<DataTrigger Binding="{Binding UseNationalDex}" Value="True">
<Setter Property="Content" Value="National" />
@@ -1140,11 +1183,13 @@
ToolTipService.InitialShowDelay="0" ToolTip="Drag to Create New Fly Spot">
<TextBlock Text="F" HorizontalAlignment="Center" VerticalAlignment="Center" FontStyle="Italic" />
</Border>
<local:AngleBorder Direction="Left" VerticalAlignment="Stretch">
<local:AngleBorder Direction="Left" VerticalAlignment="Stretch" Content="Map ID:" />
<local:AngleBorder Direction="Out" VerticalAlignment="Stretch" Margin="0,0,5,0">
<Border Width="50">
<TextBlock Text="{Binding PrimaryMap.Name}" HorizontalAlignment="Center" />
</Border>
</local:AngleBorder>
<local:AngleBorder Direction="Left" VerticalAlignment="Stretch" Content="Map Name:" />
<local:AngleComboBox Height="18" Direction="Out" ItemsSource="{Binding PrimaryMap.AvailableNames}" SelectedIndex="{Binding PrimaryMap.SelectedNameIndex}" />
</StackPanel>
<Border Grid.Column="1" IsHitTestVisible="False" BorderBrush="{DynamicResource Secondary}" Background="{DynamicResource Background}" BorderThickness="0,0,0,1" Height="5" VerticalAlignment="Bottom" />

View File

@@ -922,7 +922,7 @@
</DockPanel>
</Grid>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" Background="{DynamicResource Backlight}" Width="4" MouseDoubleClick="ResetLeftToolsPane"/>
<GridSplitter Grid.Column="1" Focusable="False" HorizontalAlignment="Stretch" Background="{DynamicResource Backlight}" Width="4" MouseDoubleClick="ResetLeftToolsPane"/>
<!-- Center Panel-->
<DockPanel Grid.Column="2">

View File

@@ -5,6 +5,7 @@ using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using HavenSoft.HexManiac.WPF.Implementations;
using HavenSoft.HexManiac.WPF.Windows;
using IronPython.Runtime;
using System;
using System.ComponentModel;
using System.Diagnostics;
@@ -18,6 +19,7 @@ using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Xml.Linq;
namespace HavenSoft.HexManiac.WPF.Controls {
public partial class TabView {

View File

@@ -72,6 +72,7 @@ namespace HavenSoft.HexManiac.WPF.Controls {
private void ShowCheck(object sender, EventArgs e) {
var tut = (MapTutorialViewModel)sender;
if (tut.Index >= 5) return; // don't show the checkmark for off-screen tutorials
var ui = (FrameworkElement)Tutorials.ItemContainerGenerator.ContainerFromItem(tut);
if (ui == null) return;
Canvas.SetTop(Check, tut.TopEdge + 15);

View File

@@ -95,7 +95,13 @@
<PathGeometry x:Name="RightArrow" Figures="M5,5 L15,5 L15,0 L25,10 L15,20 L15,15 L5,15 z"/>
<PathGeometry x:Name="LeftArrow" Figures="M-5,5 L-15,5 L-15,0 L-25,10 L-15,20 L-15,15 L-5,15 z"/>
<PathGeometry x:Name="ArrowsLeftRight" Figures="M6,6 L7,6 7,5 9,7 7,9 7,8 6,8 6,8 5,8 5,9 3,7 5,5 5,6 z"/>
<PathGeometry x:Name="ArrowsLeftRightDisconnect" Figures="M0,0 L1,-1 1,1 Z M3,0 L2,-1 2,1 Z"/>
<PathGeometry x:Name="ArrowsUpDown" Figures="M6,6 L6,7 5,7 7,9 9,7 8,7 8,6 8,6 8,5 9,5 7,3 5,5 6,5 z"/>
<PathGeometry x:Name="ArrowsUpDownDisconnect" Figures="M0,0 L-1,1 1,1 Z M0,3 L-1,2 1,2 Z"/>
<PathGeometry x:Name="LineArrowRight" Figures="M0,0 L1,0, 1,2, 4,2 4,1 6,3 4,5 4,4 1,4 1,6 0,6 Z" />
<PathGeometry x:Name="LineArrowDown" Figures="M0,0 L1,0, 1,2, 4,2 4,1 6,3 4,5 4,4 1,4 1,6 0,6 Z" Transform="{hsv:RotateTransform 90}" />
<PathGeometry x:Name="LineArrowLeft" Figures="M0,0 L1,0, 1,2, 4,2 4,1 6,3 4,5 4,4 1,4 1,6 0,6 Z" Transform="{hsv:RotateTransform 180}" />
<PathGeometry x:Name="LineArrowUp" Figures="M0,0 L1,0, 1,2, 4,2 4,1 6,3 4,5 4,4 1,4 1,6 0,6 Z" Transform="{hsv:RotateTransform 270}" />
<PathGeometry x:Name="UndoArrow" Figures="m-7,2 L-3,-5 -2,-2 7,-5 6,2 3,6 4,-1 -1,2 0,5 z"/>
<PathGeometry x:Name="RedoArrow" Figures="m7,2 L3,-5 2,-2 -7,-5 -6,2 -3,6 -4,-1 1,2 0,5 z"/>
<PathGeometry x:Name="Add" Figures="M -5,-5 L -5,-10 0,-10 0,-5 5,-5 5,0 0,0 0,5 -5,5 -5,0 -10,0 -10,-5 -5,-5"/>
@@ -210,7 +216,7 @@
</GeometryGroup>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry GeometryCombineMode="Xor" Geometry2="{hsv:TextGeometry 2,-7,-8,10}">
<CombinedGeometry.Geometry1>
<!-- Left Click 1 -->
<GeometryGroup FillRule="Nonzero">
@@ -231,26 +237,6 @@
<RectangleGeometry Rect="-8,0,8,4"/>
</GeometryGroup>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<!-- Left Click 2 -->
<GeometryGroup FillRule="Nonzero">
<GeometryGroup.Transform>
<TranslateTransform X="1.5" Y="1.5" />
</GeometryGroup.Transform>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<GeometryGroup FillRule="Nonzero">
<RectangleGeometry Rect="0,-10,10,20"/>
<RectangleGeometry Rect="-10,0,10,10"/>
</GeometryGroup>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
<RectangleGeometry Rect="-8,0,8,4"/>
</GeometryGroup>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</CombinedGeometry.Geometry2>
</CombinedGeometry>

View File

@@ -22,6 +22,8 @@ namespace HavenSoft.HexManiac.WPF.Resources {
CenterY = centerY;
}
public RotateTransformExtension(double angle) : this(angle, .5, .5) { }
public override object ProvideValue(IServiceProvider serviceProvider) {
return new RotateTransform(Angle, CenterX, CenterY);
}