face correct way

- fix undo/redo bug for model element
- update event UI
- fix hover selection bug
This commit is contained in:
haven1433
2022-09-19 18:24:57 -05:00
parent 75d8235b9e
commit 47d3642bdf
11 changed files with 247 additions and 135 deletions

View File

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

View File

@@ -11,7 +11,7 @@ namespace HavenSoft.HexManiac.Core.Models {
public class ModelTable : IReadOnlyList<ModelArrayElement> {
private readonly IDataModel model;
private readonly int arrayAddress;
private readonly ModelDelta token;
private readonly Func<ModelDelta> 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<ModelDelta> 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<ModelDelta> 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<ModelArrayElement> 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<ModelDelta> 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<ModelDelta> 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();
}

View File

@@ -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<ModelDelta> tokenFactory = null) {
var table = model.GetTable(name);
if (table == null) return null;
return new ModelTable(model, table, token);
return new ModelTable(model, table, tokenFactory);
}
/// <param name="byteOffset">Ranges from 0 to Model.Count</param>

View File

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

View File

@@ -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<List<IEventModel>> { 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<int, ConnectionInfo>();
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<int>)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<ModelDelta> 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);
}
/// <summary>
@@ -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<ConnectionModel> GetConnections(ModelArrayElement map = null, ModelDelta token = null) {
if (map == null) map = GetMapModel(token);
private IReadOnlyList<ConnectionModel> GetConnections(ModelArrayElement map = null) {
if (map == null) map = GetMapModel();
if (map == null) return null;
var connectionsAndCountTable = map.GetSubTable("connections");
var list = new List<ConnectionModel>();
@@ -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<IEventModel> GetEvents(ModelDelta token = null) {
private IReadOnlyList<IEventModel> GetEvents() {
if (allOverworldSprites == null) allOverworldSprites = RenderOWs(model);
var table = model.GetTable(HardcodeTablesModel.MapBankTable);
var map = GetMapModel(token);
var map = GetMapModel();
var results = new List<IEventModel>();
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);

View File

@@ -129,8 +129,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
var availablePokemon = new List<int>();
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);

View File

@@ -172,7 +172,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
/// <returns></returns>
public static IList<ModelArrayElement> GetAllEvents(IDataModel model, string type) {
var elements = new List<ModelArrayElement>();
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) {

View File

@@ -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<VisualComboOption> Options { get; } = new();
public ObservableCollection<string> FacingOptions { get; } = new();
public ObjectEventModel(ModelArrayElement objectEvent, IReadOnlyList<IPixelViewModel> 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) {
/// <param name="facing">(0, 1, 2, 3) = (down, up, left, right)</param>
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;
}
}

View File

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

View File

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

View File

@@ -8,6 +8,9 @@
Focusable="True">
<UserControl.Resources>
<local:EqualityToBooleanConverter x:Key="Equality" />
<DataTemplate DataType="{x:Type tools:VisualComboOption}">
<local:PixelImage />
</DataTemplate>
</UserControl.Resources>
<UserControl.InputBindings>
<KeyBinding Key="Left" Command="{Binding PanCommand}" CommandParameter="{x:Static map:MapDirection.Left}" />
@@ -77,7 +80,7 @@
<ContentControl DataContext="{Binding SelectedEvent}" Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type map:ObjectEventModel}">
<Grid>
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
@@ -90,64 +93,64 @@
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel>
<StackPanel Margin="2,2,0,2">
<local:AngleBorder Direction="Out">
<TextBlock Text="ID:"/>
</local:AngleBorder>
<local:AngleTextBox Direction="Out" TextBinding="{Binding ObjectID}" />
</StackPanel>
<ComboBox Grid.Row="0" Grid.Column="1" SelectedIndex="{Binding Graphics}" ItemsSource="{Binding Options}">
<ComboBox.Resources>
<DataTemplate DataType="{x:Type tools:VisualComboOption}">
<local:PixelImage />
</DataTemplate>
</ComboBox.Resources>
</ComboBox>
<local:AngleComboBox Grid.Row="0" Grid.Column="1" Direction="Right" SelectedIndex="{Binding Graphics}" ItemsSource="{Binding Options}" Margin="0,2,2,2">
<local:AngleComboBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="130" />
</ItemsPanelTemplate>
</local:AngleComboBox.ItemsPanel>
</local:AngleComboBox>
<local:AngleBorder Grid.Row="1" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="1" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="(x, y):" />
</local:AngleBorder>
<local:AngleTextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding XY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleTextBox Grid.Row="1" Grid.Column="1" Margin="0,2,2,2" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding XY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleBorder Grid.Row="2" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="2" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Elevation:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Elevation}" />
<local:AngleTextBox Grid.Row="2" Grid.Column="1" Margin="0,2,2,2" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Elevation}" />
<local:AngleBorder Grid.Row="3" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="3" VerticalAlignment="Top" Direction="Out" Margin="2,2,0,2">
<TextBlock Text="Movement Type:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding MoveType, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleComboBox Grid.Row="3" Grid.Column="1" Margin="0,2,2,2" VerticalAlignment="Top" Direction="Right" SelectedIndex="{Binding MoveType}" ItemsSource="{Binding FacingOptions}" />
<local:AngleBorder Grid.Row="4" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="4" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Range:" />
</local:AngleBorder>
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding RangeXY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding RangeXY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleBorder Grid.Row="5" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="5" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Trainer Type:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="5" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding TrainerType}" />
<local:AngleTextBox Grid.Row="5" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding TrainerType}" />
<local:AngleBorder Grid.Row="6" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="6" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="TrainerRange / BerryID:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="6" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding TrainerRangeOrBerryID}" />
<local:AngleTextBox Grid.Row="6" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding TrainerRangeOrBerryID}" />
<local:AngleBorder Grid.Row="7" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="7" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Script Address:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="7" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding ScriptAddressText, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleTextBox Grid.Row="7" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding ScriptAddressText, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleBorder Grid.Row="8" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="8" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Flag:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="8" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding FlagText}" />
<local:AngleTextBox Grid.Row="8" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding FlagText}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type map:WarpEventModel}">
<Grid>
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
@@ -160,32 +163,32 @@
<RowDefinition />
</Grid.RowDefinitions>
<local:AngleBorder Grid.Row="1" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="1" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="(x, y):" />
</local:AngleBorder>
<local:AngleTextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding XY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleTextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding XY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleBorder Grid.Row="2" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="2" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Elevation:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Elevation}" />
<local:AngleTextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding Elevation}" />
<local:AngleBorder Grid.Row="3" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="3" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="(bank, map):"/>
</local:AngleBorder>
<StackPanel Grid.Row="3" Grid.Column="1" VerticalAlignment="Top">
<StackPanel Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2">
<local:AngleTextBox Direction="Out" TextBinding="{Binding BankMap}" />
<TextBlock Text="{Binding TargetMapName}" />
</StackPanel>
<local:AngleBorder Grid.Row="4" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="4" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Destination ID:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding WarpID}" />
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding WarpID}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type map:ScriptEventModel}">
<Grid>
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
@@ -198,34 +201,34 @@
<RowDefinition />
</Grid.RowDefinitions>
<local:AngleBorder Grid.Row="1" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="1" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="(x, y):" />
</local:AngleBorder>
<local:AngleTextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding XY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleTextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding XY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleBorder Grid.Row="2" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="2" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Elevation:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Elevation}" />
<local:AngleTextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding Elevation}" />
<local:AngleBorder Grid.Row="3" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="3" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Trigger:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Trigger}" />
<local:AngleTextBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding Trigger}" />
<local:AngleBorder Grid.Row="4" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="4" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Index:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Index}" />
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding Index}" />
<local:AngleBorder Grid.Row="7" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="7" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Script Address:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="7" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding ScriptAddressText, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleTextBox Grid.Row="7" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding ScriptAddressText, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type map:SignpostEventModel}">
<Grid>
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
@@ -238,25 +241,25 @@
<RowDefinition />
</Grid.RowDefinitions>
<local:AngleBorder Grid.Row="1" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="1" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="(x, y):" />
</local:AngleBorder>
<local:AngleTextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding XY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleTextBox Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding XY, UpdateSourceTrigger=PropertyChanged}" />
<local:AngleBorder Grid.Row="2" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="2" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Elevation:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Elevation}" />
<local:AngleTextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding Elevation}" />
<local:AngleBorder Grid.Row="3" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="3" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Kind:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Kind}" />
<local:AngleTextBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding Kind}" />
<local:AngleBorder Grid.Row="4" VerticalAlignment="Top" Direction="Left">
<local:AngleBorder Grid.Row="4" VerticalAlignment="Top" Direction="Left" Margin="2,2,0,2">
<TextBlock Text="Arg:"/>
</local:AngleBorder>
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Direction="Out" TextBinding="{Binding Arg}" />
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding Arg}" />
<!-- TODO break Arg into multiple pieces and show individual UI based on the value of Kind -->
<!--<local:AngleBorder Grid.Row="7" VerticalAlignment="Top" Direction="Left">
@@ -529,20 +532,13 @@
</local:AngleButton>
<TextBlock Margin="2,0" Text="pokemon." />
</StackPanel>
<Decorator Margin="5" HorizontalAlignment="Center">
<Decorator.Resources>
<DataTemplate DataType="{x:Type tools:VisualComboOption}">
<local:PixelImage />
</DataTemplate>
</Decorator.Resources>
<local:AngleComboBox Direction="Out" MinWidth="50" ItemsSource="{Binding GraphicsOptions}" SelectedIndex="{Binding TrainerGraphics}">
<local:AngleComboBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel MaxWidth="256" />
</ItemsPanelTemplate>
</local:AngleComboBox.ItemsPanel>
</local:AngleComboBox>
</Decorator>
<local:AngleComboBox Margin="5" HorizontalAlignment="Center" Direction="Out" MinWidth="50" ItemsSource="{Binding GraphicsOptions}" SelectedIndex="{Binding TrainerGraphics}">
<local:AngleComboBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel MaxWidth="256" />
</ItemsPanelTemplate>
</local:AngleComboBox.ItemsPanel>
</local:AngleComboBox>
<local:PixelImage DataContext="{Binding TrainerSprite}" TransparentBrush="{DynamicResource Background}" />
</StackPanel>
</Grid>