From 47d3642bdfb735f85a6fe4be3f03479f57fe6b42 Mon Sep 17 00:00:00 2001 From: haven1433 Date: Mon, 19 Sep 2022 18:24:57 -0500 Subject: [PATCH] face correct way - fix undo/redo bug for model element - update event UI - fix hover selection bug --- src/HexManiac.Core/Models/Code/default.toml | 86 ++++++++++++ src/HexManiac.Core/Models/ModelTable.cs | 27 ++-- src/HexManiac.Core/Models/Runs/ArrayRun.cs | 4 +- .../ViewModels/Images/IPixelViewModel.cs | 11 ++ .../ViewModels/Map/BlockMapViewModel.cs | 73 +++++----- .../ViewModels/Map/EventTemplate.cs | 6 +- src/HexManiac.Core/ViewModels/Map/Flags.cs | 2 +- .../ViewModels/Map/IEventViewModel.cs | 34 ++++- .../ViewModels/Map/MapEditorViewModel.cs | 5 +- .../ViewModels/Tools/PythonTool.cs | 2 +- src/HexManiac.WPF/Controls/MapTab.xaml | 132 +++++++++--------- 11 files changed, 247 insertions(+), 135 deletions(-) diff --git a/src/HexManiac.Core/Models/Code/default.toml b/src/HexManiac.Core/Models/Code/default.toml index aac431f5..75419bae 100644 --- a/src/HexManiac.Core/Models/Code/default.toml +++ b/src/HexManiac.Core/Models/Code/default.toml @@ -131,6 +131,92 @@ Name = '''egggroups''' '''Undiscovered''', ] +[[List]] +Name = '''FacingOptions''' +0 = [ + '''DoNotUse''', + '''LookAround''', + '''WanderAround''', + '''WanderUpAndDown''', + '''WanderDownAndUp''', + '''WanderLeftAndRight''', + '''WanderRightAndLeft''', + '''FaceUp''', + '''FaceDown''', + '''FaceLeft''', + '''FaceRight''', + '''Player''', + '''BerryTreeGrowth''', + '''FaceDownAndUp''', + '''FaceLeftAndRight''', + '''FaceUpAndLeft''', + '''FaceUpAndRight''', + '''FaceDownAndLeft''', + '''FaceDownAndRight''', + '''FaceDownUpAndLeft''', + '''FaceDownUpAndRight''', + '''FaceUpLeftAndRight''', + '''FaceDownLeftAndRight''', + '''RotateCounterclockwise''', + '''RotateClockwise''', + '''WalkUpAndDown''', + '''WalkDownAndUp''', + '''WalkLeftAndRight''', + '''WalkRightAndLeft''', + '''WalkSequenceUpRightLeftDown''', + '''WalkSequenceRightLeftDownUp''', + '''WalkSequenceDownUpRightLeft''', + '''WalkSequenceLeftDownUpRight''', + '''WalkSequenceUpLeftRightDown''', + '''WalkSequenceLeftRightDownUp''', + '''WalkSequenceDownUpLeftRight''', + '''WalkSequenceRightDownUpLeft''', + '''WalkSequenceLeftUpDownRight''', + '''WalkSequenceUpDownRightLeft''', + '''WalkSequenceRightLeftUpDown''', + '''WalkSequenceDownRightLeftUp''', + '''WalkSequenceRightUpDownLeft''', + '''WalkSequenceUpDownLeftRight''', + '''WalkSequenceLeftRightUpDown''', + '''WalkSequenceDownLeftRightUp''', + '''WalkSequenceUpLeftDownRight''', + '''WalkSequenceDownRightUpLeft''', + '''WalkSequenceLeftDownRightUp''', + '''WalkSequenceRightUpLeftDown''', + '''WalkSequenceUpRightDownLeft''', + '''WalkSequenceDownLeftUpRight''', + '''WalkSequenceLeftUpRightDown''', + '''WalkSequenceRightDownLeftUp''', + '''CopyPlayer''', + '''CopyPlayerOpposite''', + '''CopyPlayerCounterclockwise''', + '''CopyPlayerClockwise''', + '''TreeDisguise''', + '''MountainDisguise''', + '''CopyPlayerInGrass''', + '''CopyPlayerOppositeInGrass''', + '''CopyPlayerCounterclockwiseInGrass''', + '''CopyPlayerClockwiseInGrass''', + '''Buried''', + '''WalkInPlaceDown''', + '''WalkInPlaceUp''', + '''WalkInPlaceLeft''', + '''WalkInPlaceRight''', + '''WalkSlowlyInPlaceDown''', + '''WalkSlowlyInPlaceUp''', + '''WalkSlowlyInPlaceLeft''', + '''WalkSlowlyInPlaceRight''', + '''JogInPlaceDown''', + '''JogInPlaceUp''', + '''JogInPlaceLeft''', + '''JogInPlaceRight''', + '''Invisible''', + '''VsSeeker4d''', + '''VsSeeker4e''', + '''VsSeeker4f''', + '''WanderAroundSlower''', +] + [[List]] Name = '''terrains''' 0 = [ diff --git a/src/HexManiac.Core/Models/ModelTable.cs b/src/HexManiac.Core/Models/ModelTable.cs index 3714f31d..4663aa43 100644 --- a/src/HexManiac.Core/Models/ModelTable.cs +++ b/src/HexManiac.Core/Models/ModelTable.cs @@ -11,7 +11,7 @@ namespace HavenSoft.HexManiac.Core.Models { public class ModelTable : IReadOnlyList { private readonly IDataModel model; private readonly int arrayAddress; - private readonly ModelDelta token; + private readonly Func tokenFactory; private readonly ITableRun run; public int Count => run?.ElementCount ?? 0; @@ -21,7 +21,7 @@ namespace HavenSoft.HexManiac.Core.Models { public ModelArrayElement this[int value] { get { - return new ModelArrayElement(model, arrayAddress, value, token, run); + return new ModelArrayElement(model, arrayAddress, value, tokenFactory, run); } } @@ -36,12 +36,12 @@ namespace HavenSoft.HexManiac.Core.Models { } } - public ModelTable(IDataModel model, ITableRun table, ModelDelta token = null) : this(model, table.Start, token, table) { } + public ModelTable(IDataModel model, ITableRun table, Func tokenFactory = null) : this(model, table.Start, tokenFactory, table) { } - public ModelTable(IDataModel model, int address, ModelDelta token = null, ITableRun tableRun = null) { + public ModelTable(IDataModel model, int address, Func tokenFactory = null, ITableRun tableRun = null) { (this.model, arrayAddress) = (model, address); run = tableRun ?? model.GetNextRun(address) as ITableRun; - this.token = token ?? new(); + this.tokenFactory = tokenFactory ?? (() => new NoDataChangeDeltaModel()); } public IEnumerator GetEnumerator() { @@ -57,19 +57,19 @@ namespace HavenSoft.HexManiac.Core.Models { private readonly int arrayAddress; private readonly int arrayIndex; private readonly ITableRun table; - private readonly ModelDelta token; + private readonly Func tokenFactory; public int Start => table.Start + table.ElementLength * arrayIndex; public int Length => table.ElementLength; public string Address => (table.Start + table.ElementLength * arrayIndex).ToAddress(); public ITableRun Table => table; public IDataModel Model => model; - public ModelDelta Token => token; + public ModelDelta Token => tokenFactory(); - public ModelArrayElement(IDataModel model, int address, int index, ModelDelta token, ITableRun table) { + public ModelArrayElement(IDataModel model, int address, int index, Func tokenFactory, ITableRun table) { (this.model, arrayAddress, arrayIndex) = (model, address, index); this.table = table ?? (ITableRun)model.GetNextRun(arrayAddress); - this.token = token; + this.tokenFactory = tokenFactory; } public bool HasField(string name) => table.ElementContent.Any(field => field.Name == name); @@ -203,7 +203,7 @@ namespace HavenSoft.HexManiac.Core.Models { if (seg.Type == ElementContentType.Pointer) { var address = GetAddress(fieldName); if (model.GetNextRun(address) is ITableRun table1) { - return new ModelTable(model, table1.Start, token); + return new ModelTable(model, table1.Start, tokenFactory); } return address; } @@ -224,7 +224,7 @@ namespace HavenSoft.HexManiac.Core.Models { var valueAddress = table.Start + table.ElementLength * arrayIndex + elementOffset; var seg = table.ElementContent.Single(segment => segment.Name == fieldName); if (seg is ArrayRunEnumSegment enumSeg) { - enumSeg.Write(null, model, token, valueAddress, ref value); + enumSeg.Write(null, model, tokenFactory(), valueAddress, ref value); } else { throw new NotImplementedException(); } @@ -245,6 +245,7 @@ namespace HavenSoft.HexManiac.Core.Models { var elementOffset = table.ElementContent.Until(segment => segment.Name == fieldName).Sum(segment => segment.Length); var valueAddress = table.Start + table.ElementLength * arrayIndex + elementOffset; var seg = table.ElementContent.Single(segment => segment.Name == fieldName); + var token = tokenFactory(); if (seg.Type == ElementContentType.PCS) { var bytes = model.TextConverter.Convert(value, out var _); while (bytes.Count > seg.Length) bytes.RemoveAt(bytes.Count - 1); @@ -269,7 +270,7 @@ namespace HavenSoft.HexManiac.Core.Models { var valueAddress = table.Start + table.ElementLength * arrayIndex + elementOffset; var seg = table.ElementContent.Single(segment => segment.Name == fieldName); if (seg.Type == ElementContentType.Integer || seg.Type == ElementContentType.BitArray) { - model.WriteMultiByteValue(valueAddress, seg.Length, token, value); + model.WriteMultiByteValue(valueAddress, seg.Length, tokenFactory(), value); } else { throw new NotImplementedException(); } @@ -287,7 +288,7 @@ namespace HavenSoft.HexManiac.Core.Models { childTable = model.GetNextRun(destination) as ITableRun; } if (childTable == null) return null; - return new ModelTable(model, destination, token, childTable); + return new ModelTable(model, destination, tokenFactory, childTable); } else { throw new NotImplementedException(); } diff --git a/src/HexManiac.Core/Models/Runs/ArrayRun.cs b/src/HexManiac.Core/Models/Runs/ArrayRun.cs index e3a47d56..092a132b 100644 --- a/src/HexManiac.Core/Models/Runs/ArrayRun.cs +++ b/src/HexManiac.Core/Models/Runs/ArrayRun.cs @@ -28,10 +28,10 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { return model.GetNextRun(address) as ITableRun; } - public static ModelTable GetTableModel(this IDataModel model, string name, ModelDelta token = null) { + public static ModelTable GetTableModel(this IDataModel model, string name, Func tokenFactory = null) { var table = model.GetTable(name); if (table == null) return null; - return new ModelTable(model, table, token); + return new ModelTable(model, table, tokenFactory); } /// Ranges from 0 to Model.Count diff --git a/src/HexManiac.Core/ViewModels/Images/IPixelViewModel.cs b/src/HexManiac.Core/ViewModels/Images/IPixelViewModel.cs index 8d4bf58c..d1c1a4f9 100644 --- a/src/HexManiac.Core/ViewModels/Images/IPixelViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Images/IPixelViewModel.cs @@ -29,5 +29,16 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Images { } return new ReadonlyPixelViewModel(image.PixelWidth, height, data, image.Transparent); } + + public static IPixelViewModel ReflectX(this IPixelViewModel image) { + var data = new short[image.PixelWidth * image.PixelHeight]; + for (int y = 0; y < image.PixelHeight; y++) { + for (int x = 0; x < image.PixelWidth; x++) { + var xx = image.PixelWidth - x - 1; + data[y * image.PixelWidth + x] = image.PixelData[y * image.PixelWidth + xx]; + } + } + return new ReadonlyPixelViewModel(image.PixelWidth, image.PixelHeight, data, image.Transparent); + } } } diff --git a/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs b/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs index 08f1c2f5..3a56343e 100644 --- a/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs @@ -58,7 +58,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private void CycleActiveEvent(object sender, EventCycleDirection direction) { // organize events into categories - var events = GetEvents(tokenFactory()); + var events = GetEvents(); var categories = new List> { new(), new(), new(), new() }; int selectionIndex = -1, selectedCategory = -1; for (int i = 0; i < events.Count; i++) { @@ -200,7 +200,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { #region Borders - private bool includeBorders = false; + private bool includeBorders = true; public bool IncludeBorders { get => includeBorders; set => Set(ref includeBorders, value, IncludeBordersChanged); @@ -258,7 +258,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { wildTable = model.RelocateForExpansion(token, wildTable, wildTable.Length + wildTable.ElementLength); wildTable = wildTable.Append(token, 1); model.ObserveRunWritten(token, wildTable); - var element = new ModelArrayElement(model, wildTable.Start, wildTable.ElementCount - 1, token, wildTable); + var element = new ModelArrayElement(model, wildTable.Start, wildTable.ElementCount - 1, tokenFactory, wildTable); element.SetValue("bank", MapID / 1000); element.SetValue("map", MapID % 1000); element.SetAddress("grass", Pointer.NULL); @@ -490,7 +490,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var border = GetBorderThickness(layout); var tileX = (int)((x - LeftEdge) / SpriteScale / 16) - border.West; var tileY = (int)((y - TopEdge) / SpriteScale / 16) - border.North; - foreach (var e in GetEvents(tokenFactory())) { + foreach (var e in GetEvents()) { if (e.X == tileX && e.Y == tileY) { if (selectedEvent == null || selectedEvent.X != e.X || selectedEvent.Y != e.Y) { SelectedEvent = e; @@ -603,7 +603,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private void ResizeMapData(MapDirection direction, int amount) { if (amount == 0) return; var token = tokenFactory(); - var map = GetMapModel(token); + var map = GetMapModel(); var layout = GetLayout(map); var run = model.GetNextRun(layout.GetAddress("blockmap")) as BlockmapRun; if (run == null) return; @@ -623,7 +623,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { } } } - foreach (var e in GetEvents(token)) { + foreach (var e in GetEvents()) { if (direction == MapDirection.Left) { e.X += amount; } else if (direction == MapDirection.Up) { @@ -637,7 +637,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private void ConnectNewMap(object obj) { var token = tokenFactory(); - var mapBanks = new ModelTable(model, model.GetTable(HardcodeTablesModel.MapBankTable).Start, token); + var mapBanks = new ModelTable(model, model.GetTable(HardcodeTablesModel.MapBankTable).Start, tokenFactory); var enumViewModel = new EnumViewModel(mapBanks.Count.Range(i => i.ToString()).ToArray()); var option = fileSystem.ShowOptions( "Pick a group", @@ -647,12 +647,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { if (option == -1) return; var info = (ConnectionInfo)obj; - var map = GetMapModel(token); + var map = GetMapModel(); var connectionsAndCount = map.GetSubTable("connections")[0]; var connections = connectionsAndCount.GetSubTable("connections").Run; connections = model.RelocateForExpansion(token, connections, connections.Length + connections.ElementLength); connectionsAndCount.SetValue("count", connections.ElementCount + 1); - var table = new ModelTable(model, connections.Start, token, connections); + var table = new ModelTable(model, connections.Start, tokenFactory, connections); var newConnection = new ConnectionModel(table[connections.ElementCount]); newConnection.Offset = info.Offset; newConnection.Direction = info.Direction; @@ -664,7 +664,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var newTable = model.RelocateForExpansion(token, mapBanks.Run, mapBanks.Run.Length + mapBanks.Run.ElementLength); newTable = newTable.Append(token, 1); model.ObserveRunWritten(token, newTable); - mapBanks = new ModelTable(model, newTable.Start, token, newTable); + mapBanks = new ModelTable(model, newTable.Start, tokenFactory, newTable); var tableStart = model.FindFreeSpace(model.FreeSpaceStart, 8); mapTable = new TableStreamRun(model, tableStart, SortedSpan.One(mapBanks[newConnection.MapGroup].Start), $"[map<{_map}1>]", null, new DynamicStreamStrategy(model, null), 0); model.UpdateArrayPointer(token, null, null, -1, mapBanks[newConnection.MapGroup].Start, tableStart); @@ -696,7 +696,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { // find available maps var options = new Dictionary(); var table = model.GetTable(HardcodeTablesModel.MapBankTable); - var mapBanks = new ModelTable(model, table.Start, token); + var mapBanks = new ModelTable(model, table.Start, tokenFactory); for (int group = 0; group < mapBanks.Count; group++) { var bank = mapBanks[group]; var maps = bank.GetSubTable("maps"); @@ -738,7 +738,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private void RemoveConnections(object obj) { var toRemove = (IReadOnlyList)obj; var token = tokenFactory(); - var map = GetMapModel(token); + var map = GetMapModel(); var connections = GetConnections(map); for (int i = 0; i < toRemove.Count; i++) { for (int j = toRemove[i] - i + 1; j < connections.Count - i; j++) { @@ -764,7 +764,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private ConnectionModel AddConnection(ConnectionInfo info) { var token = tokenFactory(); - var map = GetMapModel(token); + var map = GetMapModel(); var connectionsAndCountTable = map.GetSubTable("connections"); if (connectionsAndCountTable == null) { var newConnectionsAndCountTable = CreateNewConnections(token); @@ -790,7 +790,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { connections = connections.Append(token, 1); model.ObserveRunWritten(token, connections); - var table = new ModelTable(model, connections.Start, token, connections); + var table = new ModelTable(model, connections.Start, tokenFactory, connections); var newConnection = new ConnectionModel(table[count]); token.ChangeData(model, table[count].Start, new byte[12]); newConnection.Direction = info.Direction; @@ -799,9 +799,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public ObjectEventModel CreateObjectEvent(int graphics, int scriptAddress) { var token = tokenFactory(); - var map = GetMapModel(token); + var map = GetMapModel(); var events = map.GetSubTable("events")[0]; - var element = AddEvent(events, token, "objectCount", "objects"); + var element = AddEvent(events, tokenFactory, "objectCount", "objects"); if (allOverworldSprites == null) allOverworldSprites = RenderOWs(model); var newEvent = new ObjectEventModel(element, allOverworldSprites) { X = 0, Y = 0, @@ -821,37 +821,34 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { } public WarpEventModel CreateWarpEvent(int bank, int map) { - var token = tokenFactory(); - var mapModel = GetMapModel(token); + var mapModel = GetMapModel(); var events = mapModel.GetSubTable("events")[0]; - var element = AddEvent(events, token, "warpCount", "warps"); + var element = AddEvent(events, tokenFactory, "warpCount", "warps"); var newEvent = new WarpEventModel(element) { X = 0, Y = 0, Elevation = 0, Bank = bank, Map = map, WarpID = 0 }; SelectedEvent = newEvent; return newEvent; } public ScriptEventModel CreateScriptEvent() { - var token = tokenFactory(); - var map = GetMapModel(token); + var map = GetMapModel(); var events = map.GetSubTable("events")[0]; - var element = AddEvent(events, token, "scriptCount", "scripts"); + var element = AddEvent(events, tokenFactory, "scriptCount", "scripts"); var newEvent = new ScriptEventModel(element) { X = 0, Y = 0, Elevation = 0, Index = 0, Trigger = 0, ScriptAddress = Pointer.NULL }; SelectedEvent = newEvent; return newEvent; } public SignpostEventModel CreateSignpostEvent() { - var token = tokenFactory(); - var map = GetMapModel(token); + var map = GetMapModel(); var events = map.GetSubTable("events")[0]; - var element = AddEvent(events, token, "signpostCount", "signposts"); + var element = AddEvent(events, tokenFactory, "signpostCount", "signposts"); var newEvent = new SignpostEventModel(element) { X = 0, Y = 0, Elevation = 0, Kind = 0, Arg = "0" }; SelectedEvent = newEvent; return newEvent; } // TODO use this for connections as well, since the structure is the same - public static ModelArrayElement AddEvent(ModelArrayElement events, ModelDelta token, string countName, string fieldName) { + public static ModelArrayElement AddEvent(ModelArrayElement events, Func tokenFactory, string countName, string fieldName) { var model = events.Model; var count = events.GetValue(countName); var elementTable = events.GetSubTable(fieldName)?.Run; @@ -867,9 +864,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { elementTable = new TableStreamRun(model, newTableStart, SortedSpan.One(events.Table.ElementContent.Until(seg => seg.Name == fieldName).Sum(seg => seg.Length) + events.Table.Start), segment.InnerFormat, childSegments, parentStrategy, 0); events.SetAddress(fieldName, newTableStart); } + var token = tokenFactory(); var newRun = elementTable.Append(token, 1); model.ObserveRunWritten(token, newRun); - return new ModelArrayElement(model, newRun.Start, newRun.ElementCount - 1, token, newRun); + return new ModelArrayElement(model, newRun.Start, newRun.ElementCount - 1, tokenFactory, newRun); } /// @@ -1105,23 +1103,23 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { BorderBlock = canvas; } - private ModelArrayElement GetMapModel(ModelDelta token = null) { + private ModelArrayElement GetMapModel() { var table = model.GetTable(HardcodeTablesModel.MapBankTable); if (table == null) return null; - var mapBanks = new ModelTable(model, table.Start, token); + var mapBanks = new ModelTable(model, table.Start, tokenFactory); var bank = mapBanks[group].GetSubTable("maps"); var mapTable = bank[map].GetSubTable("map"); return mapTable[0]; } - private ModelArrayElement GetLayout(ModelArrayElement map = null, ModelDelta token = null) { - if (map == null) map = GetMapModel(token); + private ModelArrayElement GetLayout(ModelArrayElement map = null) { + if (map == null) map = GetMapModel(); if (map == null) return null; return map.GetSubTable("layout")[0]; } - private IReadOnlyList GetConnections(ModelArrayElement map = null, ModelDelta token = null) { - if (map == null) map = GetMapModel(token); + private IReadOnlyList GetConnections(ModelArrayElement map = null) { + if (map == null) map = GetMapModel(); if (map == null) return null; var connectionsAndCountTable = map.GetSubTable("connections"); var list = new List(); @@ -1147,15 +1145,14 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var run = model.GetTable(HardcodeTablesModel.OverworldSprites); var ows = new ModelTable(model, run.Start, null, run); for (int i = 0; i < ows.Count; i++) { - list.Add(ObjectEventModel.Render(model, ows, i)); + list.Add(ObjectEventModel.Render(model, ows, i,0)); } return list; } - private IReadOnlyList GetEvents(ModelDelta token = null) { + private IReadOnlyList GetEvents() { if (allOverworldSprites == null) allOverworldSprites = RenderOWs(model); - var table = model.GetTable(HardcodeTablesModel.MapBankTable); - var map = GetMapModel(token); + var map = GetMapModel(); var results = new List(); var events = new EventGroupModel(map.GetSubTable("events")[0], allOverworldSprites); results.AddRange(events.Objects); @@ -1424,7 +1421,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { // when we call SetValue, get the latest token private void SetValue(int value, [CallerMemberName]string name = null) { if (value == GetValue(name)) return; - map = new(map.Model, map.Table.Start, (map.Start - map.Table.Start) / map.Table.ElementCount, tokenFactory(), map.Table); + map = new(map.Model, map.Table.Start, (map.Start - map.Table.Start) / map.Table.ElementCount, tokenFactory, map.Table); var originalName = name; name = char.ToLower(name[0]) + name.Substring(1); map.SetValue(name, value); diff --git a/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs b/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs index 4f05aa4b..3ee13095 100644 --- a/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs +++ b/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs @@ -129,8 +129,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var availablePokemon = new List(); var dexName = HardcodeTablesModel.RegionalDexTableName; if (useNationalDex) dexName = HardcodeTablesModel.NationalDexTableName; - var pokedex = model.GetTableModel(dexName, token); - var pokestats = model.GetTableModel(HardcodeTablesModel.PokemonStatsTable, token); + var pokedex = model.GetTableModel(dexName, () => token); + var pokestats = model.GetTableModel(HardcodeTablesModel.PokemonStatsTable, () => token); for (int i = 1; i < pokedex.Count; i++) { if (pokedex[i - 1].GetValue(0) > maxPokedex) continue; if (MinLevel.TryGetValue(i, out var level) && level > maxLevel) continue; @@ -158,7 +158,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { while (UsedTrainerFlags.Contains(trainerFlag)) trainerFlag++; usedTrainerFlags.Add(trainerFlag); - var trainers = model.GetTableModel(HardcodeTablesModel.TrainerTableName, token); + var trainers = model.GetTableModel(HardcodeTablesModel.TrainerTableName, () => token); var trainer = trainers[trainerFlag]; // structType. class. introMusicAndGender. sprite. name""12 item1: item2: item3: item4: doubleBattle:: ai:: pokemonCount:: pokemon<> trainer.SetValue("structType", 0); diff --git a/src/HexManiac.Core/ViewModels/Map/Flags.cs b/src/HexManiac.Core/ViewModels/Map/Flags.cs index e15867e3..1692d6bc 100644 --- a/src/HexManiac.Core/ViewModels/Map/Flags.cs +++ b/src/HexManiac.Core/ViewModels/Map/Flags.cs @@ -172,7 +172,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { /// public static IList GetAllEvents(IDataModel model, string type) { var elements = new List(); - var banks = model.GetTableModel(HardcodeTablesModel.MapBankTable, new()); + var banks = model.GetTableModel(HardcodeTablesModel.MapBankTable); foreach (var bank in banks) { var maps = bank.GetSubTable("maps"); foreach (var map in maps) { diff --git a/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs b/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs index 1d6acd31..c52363fa 100644 --- a/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs @@ -148,7 +148,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { foreach (var source in table.PointerSources) { model.UpdateArrayPointer(token, null, null, 0, source, Pointer.NULL); if (model.GetNextRun(source) is ITableRun parentTable) { - var parent = new ModelArrayElement(model, parentTable.Start, 0, token, parentTable); + var parent = new ModelArrayElement(model, parentTable.Start, 0, () => token, parentTable); parent.SetValue(parentCountField, 0); } } @@ -187,7 +187,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public int MoveType { get => element.GetValue("moveType"); - set => element.SetValue("moveType", value); + set { + element.SetValue("moveType", value); + RaiseEventVisualUpdated(); + } } #region Range @@ -266,9 +269,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { } public ObservableCollection Options { get; } = new(); + public ObservableCollection FacingOptions { get; } = new(); public ObjectEventModel(ModelArrayElement objectEvent, IReadOnlyList sprites) : base(objectEvent, "objectCount") { for (int i = 0; i < sprites.Count; i++) Options.Add(VisualComboOption.CreateFromSprite(i.ToString(), sprites[i].PixelData, sprites[i].PixelWidth, i)); + objectEvent.Model.TryGetList("FacingOptions", out var list); + foreach (var item in list) FacingOptions.Add(item); } public override int TopOffset => 16 - EventRender.PixelHeight; @@ -276,22 +282,36 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public override void Render(IDataModel model) { var owTable = new ModelTable(model, model.GetTable(HardcodeTablesModel.OverworldSprites).Start); - EventRender = Render(model, owTable, Graphics); + var facing = MoveType switch { + 7 => 1, + 9 => 2, + 10 => 3, + _ => 0, + }; + EventRender = Render(model, owTable, Graphics, facing); + NotifyPropertyChanged(nameof(EventRender)); } - public static IPixelViewModel Render(IDataModel model, ModelTable owTable, int index) { + /// (0, 1, 2, 3) = (down, up, left, right) + public static IPixelViewModel Render(IDataModel model, ModelTable owTable, int index, int facing) { if (index >= owTable.Count) { return new ReadonlyPixelViewModel(new SpriteFormat(4, 2, 2, null), new short[256], 0); } var element = owTable[index]; var data = element.GetSubTable("data")[0]; - var sprites = data.GetSubTable("sprites")[0]; - var graphicsAddress = sprites.GetAddress("sprite"); + var sprites = data.GetSubTable("sprites"); + bool flip = facing == 3; + if (facing == 3) facing = 2; + if (facing >= sprites.Count) facing = 0; + var sprite = sprites[facing]; + var graphicsAddress = sprite.GetAddress("sprite"); var graphicsRun = model.GetNextRun(graphicsAddress) as ISpriteRun; if (graphicsRun == null) { return new ReadonlyPixelViewModel(new SpriteFormat(4, 16, 16, null), new short[256], 0); } - return ReadonlyPixelViewModel.Create(model, graphicsRun, true); + var ow = ReadonlyPixelViewModel.Create(model, graphicsRun, true); + if (flip) ow = ow.ReflectX(); + return ow; } } diff --git a/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs b/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs index e64fff52..fa446ac7 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs @@ -204,7 +204,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { history.Redo.CanExecuteChanged += (sender, e) => redo.RaiseCanExecuteChanged(); history.Bind(nameof(history.HasDataChange), (sender, e) => NotifyPropertyChanged(nameof(Name))); - var map = new BlockMapViewModel(fileSystem, viewPort, 3, 19) { IncludeBorders = true }; + var map = new BlockMapViewModel(fileSystem, viewPort, 3, 19); templates = new(model, viewPort.Tools.CodeTool.ScriptParser, map.AllOverworldSprites); UpdatePrimaryMap(map); for (int i = 0; i < 0x40; i++) CollisionOptions.Add(i.ToString("X2")); @@ -555,8 +555,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private Point ToTilePosition(double x, double y) { (x, y) = ((x - primaryMap.LeftEdge) / primaryMap.SpriteScale / 16, (y - primaryMap.TopEdge) / primaryMap.SpriteScale / 16); + var borders = primaryMap.GetBorderThickness(); - return new Point((int)x - borders.West, (int)y - borders.North); + return new Point((int)Math.Floor(x) - borders.West, (int)Math.Floor(y) - borders.North); } private (double,double) ToMapPosition(int x, int y) { diff --git a/src/HexManiac.Core/ViewModels/Tools/PythonTool.cs b/src/HexManiac.Core/ViewModels/Tools/PythonTool.cs index a59bd8ae..c8060d43 100644 --- a/src/HexManiac.Core/ViewModels/Tools/PythonTool.cs +++ b/src/HexManiac.Core/ViewModels/Tools/PythonTool.cs @@ -61,7 +61,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools { public ModelTable this[string name] { get { if (Editor.SelectedTab is IViewPort viewPort && viewPort.Model is IDataModel model) { - return new ModelTable(model, model.GetAddressFromAnchor(new(), -1, name), viewPort.ChangeHistory.CurrentChange); + return new ModelTable(model, model.GetAddressFromAnchor(new(), -1, name), () => viewPort.ChangeHistory.CurrentChange); } return null; } diff --git a/src/HexManiac.WPF/Controls/MapTab.xaml b/src/HexManiac.WPF/Controls/MapTab.xaml index 513309b8..79391dc3 100644 --- a/src/HexManiac.WPF/Controls/MapTab.xaml +++ b/src/HexManiac.WPF/Controls/MapTab.xaml @@ -8,6 +8,9 @@ Focusable="True"> + + + @@ -77,7 +80,7 @@ - + @@ -90,64 +93,64 @@ - + - - - - - - - + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -160,32 +163,32 @@ - + - + - + - + - + - + - + - + - + @@ -198,34 +201,34 @@ - + - + - + - + - + - + - + - + - + - + - + @@ -238,25 +241,25 @@ - + - + - + - + - + - + - + - +