added right-click menu and a draft of the creation logic. Doesn't quite work right yet.
This commit is contained in:
haven1433
2022-10-27 21:37:05 -05:00
parent 6d56881685
commit b5e15ead3f
10 changed files with 399 additions and 158 deletions

View File

@@ -0,0 +1,97 @@
using System.Collections.Generic;
using System.Linq;
namespace HavenSoft.HexManiac.Core.Models.Map {
public record MapModel(ModelArrayElement Element) {
public LayoutModel Layout => Element.TryGetSubTable(Format.Layout, out var table) ? new(table[0]) : null;
public EventGroupModel Events => Element.TryGetSubTable(Format.Events, out var table) ? new(table[0]) : null;
}
public record LayoutModel(ModelArrayElement Element) {
public int Width => Element.GetValue("width");
public int Height => Element.GetValue("height");
public ModelArrayElement PrimaryBlockset => Element.TryGetSubTable(Format.PrimaryBlockset, out var table) ? table[0] : null;
public ModelArrayElement SecondaryBlockset => Element.TryGetSubTable(Format.SecondaryBlockset, out var table) ? table[0] : null;
}
public record EventGroupModel(ModelArrayElement Element) {
public List<ObjectEventModel> Objects {
get {
if (!Element.TryGetSubTable(Format.Objects, out var objects)) return null;
return objects.Select(obj => new ObjectEventModel(obj)).ToList();
}
}
public List<WarpEventModel> Warps {
get {
if (!Element.TryGetSubTable(Format.Warps, out var warps)) return null;
return warps.Select(obj => new WarpEventModel(obj)).ToList();
}
}
}
public record BaseEventModel(ModelArrayElement Element) {
public int X => Element.GetValue("x");
public int Y => Element.GetValue("y");
public int Elevation => Element.GetValue("elevation");
}
public record ObjectEventModel(ModelArrayElement Element) : BaseEventModel(Element) {
}
public record WarpEventModel(ModelArrayElement Element) : BaseEventModel(Element) {
}
public class Format {
public static string RegionSection => "regionSectionID";
public static string Events => "events";
public static string Layout => "layout";
public static string Warps => "warps";
public static string Objects => "objects";
public static string Connections => "connections";
public static string Scripts => "scripts";
public static string Signposts => "signposts";
public static string ObjectCount => "objectCount";
public static string WarpCount => "warpCount";
public static string ScriptCount => "scriptCount";
public static string SignpostCount => "signpostCount";
public static string BorderBlock => "borderblock";
public static string BlockMap => "blockmap";
public static string PrimaryBlockset => "blockdata1";
public static string SecondaryBlockset => "blockdata2";
public static string Tileset => "tileset";
public static string BlockAttributes => "attributes";
public static string Blocks => "block";
public static string TileAnimationRoutine => "animation";
public static string Palette => "pal";
public static string BorderWidth => "borderwidth";
public static string BorderHeight => "borderheight";
public string BlockDataFormat { get; }
public string LayoutFormat { get; }
public string ObjectsFormat { get; }
public string WarpsFormat { get; }
public string ScriptsFormat { get; }
public string SignpostsFormat { get; }
public string EventsFormat { get; }
public string ConnectionsFormat { get; }
public string HeaderFormat { get; }
public string MapFormat { get; }
public Format(bool isRSE) {
BlockDataFormat = $"[isCompressed. isSecondary. padding: {Tileset}<> {Palette}<`ucp4:0123456789ABCDEF`> {Blocks}<> {TileAnimationRoutine}<> {BlockAttributes}<>]1";
if (isRSE) BlockDataFormat = $"[isCompressed. isSecondary. padding: {Tileset}<> {Palette}<`ucp4:0123456789ABCDEF`> {Blocks}<> {BlockAttributes}<> {TileAnimationRoutine}<>]1";
LayoutFormat = $"[width:: height:: {BorderBlock}<> {BlockMap}<`blm`> {PrimaryBlockset}<{BlockDataFormat}> {SecondaryBlockset}<{BlockDataFormat}> {BorderWidth}. {BorderHeight}. unused:]1";
if (isRSE) LayoutFormat = $"[width:: height:: {BorderBlock}<> {BlockMap}<`blm`> {PrimaryBlockset}<{BlockDataFormat}> {PrimaryBlockset}<{BlockDataFormat}>]1";
ObjectsFormat = $"[id. graphics. unused: x:500 y:500 elevation. moveType. range:|t|x::|y:: trainerType: trainerRangeOrBerryID: script<`xse`> flag: unused:]/{ObjectCount}";
WarpsFormat = $"[x:500 y:500 elevation. warpID. map. bank.]/{WarpCount}";
ScriptsFormat = $"[x:500 y:500 elevation: trigger: index:: script<`xse`>]/{ScriptCount}";
SignpostsFormat = $"[x:500 y:500 elevation. kind. unused: arg::|h]/{SignpostCount}";
EventsFormat = $"[{ObjectCount}. {WarpCount}. {ScriptCount}. {SignpostCount}. {Objects}<{ObjectsFormat}> {Warps}<{WarpsFormat}> {Scripts}<{ScriptsFormat}> {Signposts}<{SignpostsFormat}>]1";
ConnectionsFormat = "[count:: connections<[direction:: offset:: mapGroup. mapNum. unused:]/count>]1";
HeaderFormat = "music: layoutID: regionSectionID. cave. weather. mapType. allowBiking. flags.|t|allowEscaping.|allowRunning.|showMapName::: floorNum. battleType.";
MapFormat = $"[{Layout}<{LayoutFormat}> events<{EventsFormat}> mapscripts<[type. pointer<>]!00> {Connections}<{ConnectionsFormat}> {HeaderFormat}]";
}
}
}

View File

@@ -301,6 +301,11 @@ namespace HavenSoft.HexManiac.Core.Models {
}
}
public bool TryGetSubTable(string fieldName, out ModelTable table) {
table = GetSubTable(fieldName);
return table != null;
}
public ModelTable GetSubTable(string fieldName) {
var elementOffset = table.ElementContent.Until(segment => segment.Name == fieldName).Sum(segment => segment.Length);
var valueAddress = table.Start + table.ElementLength * arrayIndex + elementOffset;

View File

@@ -440,6 +440,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
#endregion
public void EnterTile(IPixelViewModel tile) {
FlipVVisible = true;
FlipHVisible = true;
var index = indexForTileImage[tile];

View File

@@ -1,4 +1,5 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Map;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.Images;
@@ -32,8 +33,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
#region SelectedEvent
private IEventModel selectedEvent;
public IEventModel SelectedEvent {
private IEventViewModel selectedEvent;
public IEventViewModel SelectedEvent {
get => selectedEvent;
set {
var oldValue = selectedEvent;
@@ -43,7 +44,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
}
private void HandleSelectedEventChanged(IEventModel old) {
private void HandleSelectedEventChanged(IEventViewModel old) {
if (old == selectedEvent) return;
if (old != null) {
old.EventVisualUpdated -= RefreshFromEventChange;
@@ -61,15 +62,15 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
private void CycleActiveEvent(object sender, EventCycleDirection direction) {
// organize events into categories
var events = GetEvents();
var categories = new List<List<IEventModel>> { new(), new(), new(), new(), new() };
var categories = new List<List<IEventViewModel>> { new(), new(), new(), new(), new() };
int selectionIndex = -1, selectedCategory = -1;
for (int i = 0; i < events.Count; i++) {
int currentCategory =
events[i] is ObjectEventModel ? 0 :
events[i] is WarpEventModel ? 1 :
events[i] is ScriptEventModel ? 2 :
events[i] is SignpostEventModel ? 3 :
events[i] is FlyEventModel ? 4 :
events[i] is ObjectEventViewModel ? 0 :
events[i] is WarpEventViewModel ? 1 :
events[i] is ScriptEventViewModel ? 2 :
events[i] is SignpostEventViewModel ? 3 :
events[i] is FlyEventViewModel ? 4 :
-1;
categories[currentCategory].Add(events[i]);
@@ -194,7 +195,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
private byte[][] blocks;
private byte[][] blockAttributes;
private readonly List<IPixelViewModel> blockRenders = new(); // one image per block
private IReadOnlyList<IEventModel> eventRenders;
private IReadOnlyList<IEventViewModel> eventRenders;
public IReadOnlyList<IPixelViewModel> BlockRenders {
get {
if (blockRenders.Count == 0) RefreshBlockRenderCache();
@@ -514,6 +515,118 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
NotifyPropertyChanged(nameof(SpriteScale));
}
// check for other warps on this same tile and see what primary/secondary blockset is expected for the new map.
// if no reasonable tileset is found, just use the current map's blocksets
public BlockMapViewModel CreateMapForWarp(WarpEventViewModel warp) {
// what bank should the new map go in?
var option = MapRepointer.GetMapBankForNewMap("Which map bank do you want to add the new map to?");
if (option == -1) return null;
var token = tokenFactory();
var myLayout = GetLayout();
// give me this block
var (width, height) = (myLayout.GetValue("width"), myLayout.GetValue("height"));
var start = myLayout.GetAddress("blockmap");
var modelAddress = start + (warp.Y * width + warp.X) * 2;
var data = model.ReadMultiByteValue(modelAddress, 2);
int blockIndex = data & 0x3FF;
// give me all maps that use this blockset
var borderBlockAddress = myLayout.GetAddress(Format.BorderBlock);
var primaryBlocksetAddress = myLayout.GetAddress(Format.PrimaryBlockset);
var secondaryBlocksetAddress = myLayout.GetAddress(Format.SecondaryBlockset);
var maps = new List<ModelArrayElement>();
foreach (var bank in model.GetTableModel(HardcodeTablesModel.MapBankTable)) {
if (bank == null) continue;
foreach (var mapList in bank.GetSubTable("maps")) {
if (mapList == null) continue;
var mapTable = mapList.GetSubTable("map");
if (mapTable == null) continue;
var map = mapTable[0];
var layoutTable = map.GetSubTable(Format.Layout);
if (layoutTable == null) continue;
var layout = layoutTable[0];
var primary = layout.GetAddress(Format.PrimaryBlockset);
var secondary = layout.GetAddress(Format.SecondaryBlockset);
if (primary != primaryBlocksetAddress && blockIndex < PrimaryBlocks) continue;
if (secondary != secondaryBlocksetAddress && blockIndex >= PrimaryBlocks) continue;
maps.Add(map);
}
}
// give me all warps in those maps (except for this warp itself)
// give me all warps that are on this tile
var warps = new List<ModelArrayElement>();
foreach (var map in maps) {
var layoutTable = map.GetSubTable("layout");
if (layoutTable == null) continue;
var blockmapStart = layoutTable[0].GetAddress(Format.BlockMap);
var candidateWidth = layoutTable[0].GetValue("width");
var eventTable = map.GetSubTable(Format.Events);
if (eventTable == null) continue;
var warpTable = eventTable[0].GetSubTable(Format.Warps);
if (warpTable == null) continue;
foreach (var w in warpTable) {
if (w.Start == warp.Element.Start) continue;
var p = new Point(w.GetValue("x"), w.GetValue("y"));
var warpBlockIndex = model.ReadMultiByteValue(blockmapStart + (p.Y * width + p.X) * 2, 2) & 0x3FF;
if (warpBlockIndex != blockIndex) continue;
warps.Add(w);
}
}
// give me maps that those warp to
// give me all the primary/secondary blocksets for those maps
// give me all the borders for those maps
var primaryBlocksets = new Dictionary<int, int> { { primaryBlocksetAddress, 0 } };
var secondaryBlocksets = new Dictionary<int, int> { { secondaryBlocksetAddress, 0 } };
var borders = new Dictionary<int, int> { { borderBlockAddress, 0 } };
foreach (var w in warps) {
var (targetBank, targetMap) = (w.GetValue("bank"), w.GetValue("map"));
var m = GetMapModel(model, targetBank, targetMap, tokenFactory);
var targetLayout = GetLayout(m);
var primary = targetLayout.GetAddress(Format.PrimaryBlockset);
var secondary = targetLayout.GetAddress(Format.SecondaryBlockset);
var border = targetLayout.GetAddress(Format.BorderBlock);
if (!primaryBlocksets.ContainsKey(primary)) primaryBlocksets[primary] = 0;
if (!secondaryBlocksets.ContainsKey(secondary)) secondaryBlocksets[secondary] = 0;
if (!borders.ContainsKey(border)) borders[border] = 0;
primaryBlocksets[primary]++;
secondaryBlocksets[secondary]++;
borders[border] = 0;
}
// use the most frequent primary/secondary blockset and border blocks
var bestPrimaryCount = primaryBlocksets.Values.Max();
primaryBlocksetAddress = primaryBlocksets.Keys.First(key => primaryBlocksets[key] == bestPrimaryCount);
var bestSecondaryCount = secondaryBlocksets.Values.Max();
secondaryBlocksetAddress = secondaryBlocksets.Keys.First(key => secondaryBlocksets[key] == bestSecondaryCount);
var bestBorderCount = borders.Values.Max();
borderBlockAddress = borders.Keys.First(key => borders[key] == bestBorderCount);
// create a new 9x9 map
var newMap = CreateNewMap(token, option, 9, 9);
var newLayout = newMap.GetLayout();
newLayout.SetAddress(Format.BorderBlock, borderBlockAddress);
newLayout.SetAddress(Format.PrimaryBlockset, primaryBlocksetAddress);
newLayout.SetAddress(Format.SecondaryBlockset, secondaryBlocksetAddress);
// TODO fill with 'background' tile?
// TODO fill the border
// place reverse warp at bottom heading back
(warp.Bank, warp.Map, warp.WarpID) = (newMap.group, newMap.map, 1);
var returnWarp = newMap.CreateWarpEvent(group, map);
returnWarp.WarpID = GetEvents().Where(e => e is WarpEventViewModel).Until(e => e.Equals(warp)).Count();
(returnWarp.X, returnWarp.Y) = (4, 8);
// TODO figure out which tiles should be in the 3x2 section around the warp
return newMap;
}
#region Draw / Paint
/// <summary>
@@ -635,7 +748,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
#region Events
public void UpdateEventLocation(IEventModel ev, double x, double y) {
public void UpdateEventLocation(IEventViewModel ev, double x, double y) {
(lastDrawX, lastDrawY) = (-1, -1);
var layout = GetLayout();
var border = GetBorderThickness(layout);
@@ -652,7 +765,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
NotifyPropertyChanged(nameof(PixelData));
}
public IEventModel EventUnderCursor(double x, double y) {
public IEventViewModel EventUnderCursor(double x, double y) {
var layout = GetLayout();
var border = GetBorderThickness(layout);
var tileX = (int)((x - LeftEdge) / SpriteScale / 16) - border.West;
@@ -767,6 +880,19 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
#region Work Methods
private IEnumerable<MapModel> AllMaps() {
foreach (var bank in model.GetTableModel(HardcodeTablesModel.MapBankTable)) {
if (bank == null) continue;
foreach (var mapList in bank.GetSubTable("maps")) {
if (mapList == null) continue;
var mapTable = mapList.GetSubTable("map");
if (mapTable == null) continue;
var map = mapTable[0];
yield return new(map);
}
}
}
private void ResizeMapData(MapDirection direction, int amount) {
if (amount == 0) return;
var token = tokenFactory();
@@ -807,7 +933,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, tokenFactory);
var option = MapRepointer.GetMapBankForNewMap("Which map group do you want to add the new map to?");
var option = MapRepointer.GetMapBankForNewMap("Which map bank do you want to add the new map to?");
if (option == -1) return;
var info = (ConnectionInfo)obj;
@@ -823,20 +949,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
newConnection.Offset = info.Offset;
newConnection.Direction = info.Direction;
newConnection.MapGroup = option;
var otherMap = CreateNewMap(token, option, info.Size, info.Size);
var mapTable = MapRepointer.AddNewMapToBank(option);
newConnection.MapNum = mapTable.ElementCount - 1;
var address = MapRepointer.CreateNewMap(token);
var layoutStart = MapRepointer.CreateNewLayout(token);
WritePointerAndSource(token, layoutStart + 12, MapRepointer.CreateNewBlockMap(token, info.Size, info.Size));
WritePointerAndSource(token, address + 0, layoutStart);
model.UpdateArrayPointer(token, null, null, -1, mapTable.Start + mapTable.Length - 4, address);
var otherMap = new BlockMapViewModel(fileSystem, viewPort, format, newConnection.MapGroup, newConnection.MapNum) { allOverworldSprites = allOverworldSprites };
otherMap.UpdateLayoutID();
newConnection.MapGroup = otherMap.group;
newConnection.MapNum = otherMap.map;
info = new ConnectionInfo(info.Size, -info.Offset, info.OppositeDirection);
newConnection = otherMap.AddConnection(info);
newConnection.Offset = info.Offset;
@@ -847,6 +963,30 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
NeighborsChanged.Raise(this);
}
private BlockMapViewModel CreateNewMap(ModelDelta token, int bank, int width, int height) {
var mapTable = MapRepointer.AddNewMapToBank(bank);
var address = MapRepointer.CreateNewMap(token);
var layoutStart = MapRepointer.CreateNewLayout(token);
// update width / height
model.WriteValue(token, layoutStart + 0, width);
model.WriteValue(token, layoutStart + 4, height);
// TODO update border block (layoutStart + 8)
// update blockmap (layoutStart + 12)
WritePointerAndSource(token, layoutStart + 12, MapRepointer.CreateNewBlockMap(token, width, height));
// TODO update primary blockset (layoutStart + 16)
// TODO update primary blockset (layoutStart + 20)
WritePointerAndSource(token, address + 0, layoutStart);
model.UpdateArrayPointer(token, null, null, -1, mapTable.Start + mapTable.Length - 4, address);
var otherMap = new BlockMapViewModel(fileSystem, viewPort, format, bank, mapTable.ElementCount - 1) { allOverworldSprites = allOverworldSprites };
otherMap.UpdateLayoutID();
return otherMap;
}
private void ConnectExistingMap(object obj) {
var info = (ConnectionInfo)obj;
var token = tokenFactory();
@@ -957,13 +1097,13 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
return newConnection;
}
public ObjectEventModel CreateObjectEvent(int graphics, int scriptAddress) {
public ObjectEventViewModel CreateObjectEvent(int graphics, int scriptAddress) {
var token = tokenFactory();
var map = GetMapModel();
var events = map.GetSubTable("events")[0];
var element = AddEvent(events, tokenFactory, "objectCount", "objects");
if (allOverworldSprites == null) allOverworldSprites = RenderOWs(model);
var newEvent = new ObjectEventModel(GotoAddress, element, allOverworldSprites) {
var newEvent = new ObjectEventViewModel(GotoAddress, element, allOverworldSprites) {
X = 0, Y = 0,
Elevation = 0,
ObjectID = element.Table.ElementCount,
@@ -981,29 +1121,29 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
return newEvent;
}
public WarpEventModel CreateWarpEvent(int bank, int map) {
public WarpEventViewModel CreateWarpEvent(int bank, int map) {
var mapModel = GetMapModel();
var events = mapModel.GetSubTable("events")[0];
var element = AddEvent(events, tokenFactory, "warpCount", "warps");
var newEvent = new WarpEventModel(element) { X = 0, Y = 0, Elevation = 0, Bank = bank, Map = map, WarpID = 0 };
var newEvent = new WarpEventViewModel(element) { X = 0, Y = 0, Elevation = 0, Bank = bank, Map = map, WarpID = 0 };
SelectedEvent = newEvent;
return newEvent;
}
public ScriptEventModel CreateScriptEvent() {
public ScriptEventViewModel CreateScriptEvent() {
var map = GetMapModel();
var events = map.GetSubTable("events")[0];
var element = AddEvent(events, tokenFactory, "scriptCount", "scripts");
var newEvent = new ScriptEventModel(GotoAddress, element) { X = 0, Y = 0, Elevation = 0, Index = 0, Trigger = 0, ScriptAddress = Pointer.NULL };
var newEvent = new ScriptEventViewModel(GotoAddress, element) { X = 0, Y = 0, Elevation = 0, Index = 0, Trigger = 0, ScriptAddress = Pointer.NULL };
SelectedEvent = newEvent;
return newEvent;
}
public SignpostEventModel CreateSignpostEvent() {
public SignpostEventViewModel CreateSignpostEvent() {
var map = GetMapModel();
var events = map.GetSubTable("events")[0];
var element = AddEvent(events, tokenFactory, "signpostCount", "signposts");
var newEvent = new SignpostEventModel(element, GotoAddress) { X = 0, Y = 0, Elevation = 0, Kind = 0, Pointer = Pointer.NULL };
var newEvent = new SignpostEventViewModel(element, GotoAddress) { X = 0, Y = 0, Elevation = 0, Kind = 0, Pointer = Pointer.NULL };
SelectedEvent = newEvent;
return newEvent;
}
@@ -1019,7 +1159,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
}
public FlyEventModel CreateFlyEvent() {
public FlyEventViewModel CreateFlyEvent() {
var map = GetMapModel();
var region = map.GetValue(Format.RegionSection);
if (model.IsFRLG()) region -= 88;
@@ -1054,7 +1194,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
spawns[emptySpawn].SetValue("map", this.map);
NotifyPropertyChanged(nameof(CanCreateFlyEvent));
return new FlyEventModel(model, group, this.map, tokenFactory);
return new FlyEventViewModel(model, group, this.map, tokenFactory);
}
// TODO use this for connections as well, since the structure is the same
@@ -1202,7 +1342,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
private void RefreshMapEvents() {
if (eventRenders != null) return;
var list = new List<IEventModel>();
var list = new List<IEventViewModel>();
var events = GetEvents();
foreach (var obj in events) {
obj.Render(model);
@@ -1353,15 +1493,15 @@ 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, 0));
list.Add(ObjectEventViewModel.Render(model, ows, i, 0));
}
return list;
}
private IReadOnlyList<IEventModel> GetEvents() {
private IReadOnlyList<IEventViewModel> GetEvents() {
if (allOverworldSprites == null) allOverworldSprites = RenderOWs(model);
var map = GetMapModel();
var results = new List<IEventModel>();
var results = new List<IEventViewModel>();
var events = new EventGroupModel(GotoAddress, map.GetSubTable("events")[0], allOverworldSprites, group, this.map);
events.DataMoved += HandleEventDataMoved;
results.AddRange(events.Objects);
@@ -1556,57 +1696,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public record Border(int North, int East, int South, int West);
public class Format {
public static string RegionSection => "regionSectionID";
public static string Layout => "layout";
public static string Warps => "warps";
public static string Objects => "objects";
public static string Connections => "connections";
public static string Scripts => "scripts";
public static string Signposts => "signposts";
public static string ObjectCount => "objectCount";
public static string WarpCount => "warpCount";
public static string ScriptCount => "scriptCount";
public static string SignpostCount => "signpostCount";
public static string BorderBlock => "borderblock";
public static string BlockMap => "blockmap";
public static string PrimaryBlockset => "blockdata1";
public static string SecondaryBlockset => "blockdata2";
public static string Tileset => "tileset";
public static string BlockAttributes => "attributes";
public static string Blocks => "block";
public static string TileAnimationRoutine => "animation";
public static string Palette => "pal";
public static string BorderWidth => "borderwidth";
public static string BorderHeight => "borderheight";
public string BlockDataFormat { get; }
public string LayoutFormat { get; }
public string ObjectsFormat { get; }
public string WarpsFormat { get; }
public string ScriptsFormat { get; }
public string SignpostsFormat { get; }
public string EventsFormat { get; }
public string ConnectionsFormat { get; }
public string HeaderFormat { get; }
public string MapFormat { get; }
public Format(bool isRSE) {
BlockDataFormat = $"[isCompressed. isSecondary. padding: {Tileset}<> {Palette}<`ucp4:0123456789ABCDEF`> {Blocks}<> {TileAnimationRoutine}<> {BlockAttributes}<>]1";
if (isRSE) BlockDataFormat = $"[isCompressed. isSecondary. padding: {Tileset}<> {Palette}<`ucp4:0123456789ABCDEF`> {Blocks}<> {BlockAttributes}<> {TileAnimationRoutine}<>]1";
LayoutFormat = $"[width:: height:: {BorderBlock}<> {BlockMap}<`blm`> {PrimaryBlockset}<{BlockDataFormat}> {SecondaryBlockset}<{BlockDataFormat}> {BorderWidth}. {BorderHeight}. unused:]1";
if (isRSE) LayoutFormat = $"[width:: height:: {BorderBlock}<> {BlockMap}<`blm`> {PrimaryBlockset}<{BlockDataFormat}> {PrimaryBlockset}<{BlockDataFormat}>]1";
ObjectsFormat = $"[id. graphics. unused: x:500 y:500 elevation. moveType. range:|t|x::|y:: trainerType: trainerRangeOrBerryID: script<`xse`> flag: unused:]/{ObjectCount}";
WarpsFormat = $"[x:500 y:500 elevation. warpID. map. bank.]/{WarpCount}";
ScriptsFormat = $"[x:500 y:500 elevation: trigger: index:: script<`xse`>]/{ScriptCount}";
SignpostsFormat = $"[x:500 y:500 elevation. kind. unused: arg::|h]/{SignpostCount}";
EventsFormat = $"[{ObjectCount}. {WarpCount}. {ScriptCount}. {SignpostCount}. {Objects}<{ObjectsFormat}> {Warps}<{WarpsFormat}> {Scripts}<{ScriptsFormat}> {Signposts}<{SignpostsFormat}>]1";
ConnectionsFormat = "[count:: connections<[direction:: offset:: mapGroup. mapNum. unused:]/count>]1";
HeaderFormat = "music: layoutID: regionSectionID. cave. weather. mapType. allowBiking. flags.|t|allowEscaping.|allowRunning.|showMapName::: floorNum. battleType.";
MapFormat = $"[{Layout}<{LayoutFormat}> events<{EventsFormat}> mapscripts<[type. pointer<>]!00> {Connections}<{ConnectionsFormat}> {HeaderFormat}]";
}
}
public record ConnectionInfo(int Size, int Offset, MapDirection Direction) {
public const string SingleConnectionContent = "direction:: offset:: mapGroup. mapNum. unused:";
public const string SingleConnectionLength = "/count";
@@ -1744,10 +1833,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
var objectCount = events.GetValue("objectCount");
var objects = events.GetSubTable("objects");
var objectList = new List<ObjectEventModel>();
var objectList = new List<ObjectEventViewModel>();
if (objects != null) {
for (int i = 0; i < objectCount; i++) {
var newEvent = new ObjectEventModel(gotoAddress, objects[i], ows);
var newEvent = new ObjectEventViewModel(gotoAddress, objects[i], ows);
newEvent.DataMoved += (sender, e) => DataMoved.Raise(this, e);
objectList.Add(newEvent);
}
@@ -1756,43 +1845,43 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
var warpCount = events.GetValue("warpCount");
var warps = events.GetSubTable("warps");
var warpList = new List<WarpEventModel>();
var warpList = new List<WarpEventViewModel>();
if (warps != null) {
for (int i = 0; i < warpCount; i++) warpList.Add(new WarpEventModel(warps[i]));
for (int i = 0; i < warpCount; i++) warpList.Add(new WarpEventViewModel(warps[i]));
}
Warps = warpList;
var scriptCount = events.GetValue("scriptCount");
var scripts = events.GetSubTable("scripts");
var scriptList = new List<ScriptEventModel>();
var scriptList = new List<ScriptEventViewModel>();
if (scripts != null) {
for (int i = 0; i < scriptCount; i++) scriptList.Add(new ScriptEventModel(gotoAddress, scripts[i]));
for (int i = 0; i < scriptCount; i++) scriptList.Add(new ScriptEventViewModel(gotoAddress, scripts[i]));
}
Scripts = scriptList;
var signpostCount = events.GetValue("signpostCount");
var signposts = events.GetSubTable("signposts");
var signpostList = new List<SignpostEventModel>();
var signpostList = new List<SignpostEventViewModel>();
if (signposts != null) {
for (int i = 0; i < signpostCount; i++) {
var newEvent = new SignpostEventModel(signposts[i], gotoAddress);
var newEvent = new SignpostEventViewModel(signposts[i], gotoAddress);
newEvent.DataMoved += (sender, e) => DataMoved.Raise(this, e);
signpostList.Add(newEvent);
}
}
Signposts = signpostList;
var flyEvent = new FlyEventModel(events.Model, bank, map, () => events.Token);
var flyEvent = new FlyEventViewModel(events.Model, bank, map, () => events.Token);
if (flyEvent.Valid) {
FlyEvent = flyEvent;
}
}
public IReadOnlyList<ObjectEventModel> Objects { get; }
public IReadOnlyList<WarpEventModel> Warps { get; }
public IReadOnlyList<ScriptEventModel> Scripts { get; }
public IReadOnlyList<SignpostEventModel> Signposts { get; }
public FlyEventModel FlyEvent { get; }
public IReadOnlyList<ObjectEventViewModel> Objects { get; }
public IReadOnlyList<WarpEventViewModel> Warps { get; }
public IReadOnlyList<ScriptEventViewModel> Scripts { get; }
public IReadOnlyList<SignpostEventViewModel> Signposts { get; }
public FlyEventViewModel FlyEvent { get; }
/*
* events<[objectCount. warpCount. scriptCount. signpostCount.

View File

@@ -89,7 +89,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public ObservableCollection<TemplateType> AvailableTemplateTypes { get; } = new();
public void ApplyTemplate(ObjectEventModel objectEventModel, ModelDelta token) {
public void ApplyTemplate(ObjectEventViewModel objectEventModel, ModelDelta token) {
if (selectedTemplate == TemplateType.Trainer) CreateTrainer(objectEventModel, token);
if (selectedTemplate == TemplateType.Npc) CreateNPC(objectEventModel, token);
if (selectedTemplate == TemplateType.Item) CreateItem(objectEventModel, token);
@@ -125,7 +125,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
// TODO use all-caps name or mixed-caps name depending on other trainers in the table
// TODO use reference file to get names and before/win/after text
public void CreateTrainer(ObjectEventModel objectEventModel, ModelDelta token) {
public void CreateTrainer(ObjectEventViewModel objectEventModel, ModelDelta token) {
// part 1: the team
var availablePokemon = new List<int>();
var dexName = HardcodeTablesModel.RegionalDexTableName;
@@ -216,9 +216,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
model.ObserveRunWritten(token, new XSERun(scriptStart, SortedSpan.One(objectEventModel.Start + 16)));
}
public TrainerEventContent GetTrainerContent(IEventModel eventModel) => GetTrainerContent(model, eventModel);
public static TrainerEventContent GetTrainerContent(IDataModel model, IEventModel eventModel) {
if (eventModel is not ObjectEventModel objectModel) return null;
public TrainerEventContent GetTrainerContent(IEventViewModel eventModel) => GetTrainerContent(model, eventModel);
public static TrainerEventContent GetTrainerContent(IDataModel model, IEventViewModel eventModel) {
if (eventModel is not ObjectEventViewModel objectModel) return null;
var address = objectModel.ScriptAddress;
if (address < 0) return null;
// 5C 00 trainerFlag: 00 00 <before> <win> 0F 00 <after> 09 06 02
@@ -255,7 +255,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
#region NPC
public void CreateNPC(ObjectEventModel eventModel, ModelDelta token) {
public void CreateNPC(ObjectEventViewModel eventModel, ModelDelta token) {
// loadpointer 0 <text>; callstd 2; end; text="I'm an NPC!"
// 0F 00 <text> 09 02 02 "I'm an NPC!"
var scriptStart = model.FindFreeSpace(model.FreeSpaceStart, 24);
@@ -277,9 +277,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
model.ObserveRunWritten(token, new XSERun(scriptStart, SortedSpan.One(eventModel.Start + 16)));
}
public int GetNPCTextPointer(IEventModel eventModel) => GetNPCTextPointer(model, eventModel);
public static int GetNPCTextPointer(IDataModel model, IEventModel eventModel) {
if (eventModel is not ObjectEventModel objectModel) return Pointer.NULL;
public int GetNPCTextPointer(IEventViewModel eventModel) => GetNPCTextPointer(model, eventModel);
public static int GetNPCTextPointer(IDataModel model, IEventViewModel eventModel) {
if (eventModel is not ObjectEventViewModel objectModel) return Pointer.NULL;
var address = objectModel.ScriptAddress;
if (address < 0) return Pointer.NULL;
var expectedValues = new Dictionary<int, byte> {
@@ -304,7 +304,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
private int itemID = 20;
public int ItemID { get => itemID; set => Set(ref itemID, value); }
public void CreateItem(ObjectEventModel objectEventModel, ModelDelta token) {
public void CreateItem(ObjectEventViewModel objectEventModel, ModelDelta token) {
// copyvarifnotzero 0x8000 item:
// copyvarifnotzero 0x8001 1
// callstd 1
@@ -330,9 +330,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
model.ObserveRunWritten(token, new XSERun(scriptStart, SortedSpan.One(objectEventModel.Start + 16)));
}
public int GetItemAddress(IEventModel eventModel) => GetItemAddress(model, eventModel);
public static int GetItemAddress(IDataModel model, IEventModel eventModel) {
if (eventModel is not ObjectEventModel objectModel) return Pointer.NULL;
public int GetItemAddress(IEventViewModel eventModel) => GetItemAddress(model, eventModel);
public static int GetItemAddress(IDataModel model, IEventViewModel eventModel) {
if (eventModel is not ObjectEventViewModel objectModel) return Pointer.NULL;
var address = objectModel.ScriptAddress;
if (address < 0) return Pointer.NULL;
// 1A 00 80 item: 1A 01 80 01 00 09 01 02
@@ -361,7 +361,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
#region Signpost
public void ApplyTemplate(SignpostEventModel signpost, ModelDelta token) {
public void ApplyTemplate(SignpostEventViewModel signpost, ModelDelta token) {
signpost.Elevation = 0;
signpost.Kind = 0;
@@ -380,9 +380,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
signpost.Pointer = scriptStart;
}
public int GetSignpostTextPointer(IEventModel eventModel) => GetSignpostTextPointer(model, eventModel);
public static int GetSignpostTextPointer(IDataModel model, IEventModel eventModel) {
if (eventModel is not SignpostEventModel signpost) return Pointer.NULL;
public int GetSignpostTextPointer(IEventViewModel eventModel) => GetSignpostTextPointer(model, eventModel);
public static int GetSignpostTextPointer(IDataModel model, IEventViewModel eventModel) {
if (eventModel is not SignpostEventViewModel signpost) return Pointer.NULL;
if (!signpost.ShowPointer) return Pointer.NULL;
int address = signpost.Pointer;
var expectedValues = new Dictionary<int, byte> {

View File

@@ -1,4 +1,5 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Map;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.Models.Runs.Sprites;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
@@ -14,7 +15,7 @@ using System.Windows.Input;
namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public interface IEventModel : IEquatable<IEventModel>, INotifyPropertyChanged {
public interface IEventViewModel : IEquatable<IEventViewModel>, INotifyPropertyChanged {
event EventHandler EventVisualUpdated;
public event EventHandler<EventCycleDirection> CycleEvent;
public ICommand CycleEventCommand { get; }
@@ -31,7 +32,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public enum EventCycleDirection { PreviousCategory, PreviousEvent, NextEvent, NextCategory }
public class FlyEventModel : ViewModelCore, IEventModel {
public class FlyEventViewModel : ViewModelCore, IEventViewModel {
private readonly ModelArrayElement flySpot;
private readonly ModelArrayElement connectionEntry;
@@ -71,7 +72,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public event EventHandler EventVisualUpdated;
public event EventHandler<EventCycleDirection> CycleEvent;
public FlyEventModel(IDataModel model, int bank, int map, Func<ModelDelta> tokenFactory) {
public FlyEventViewModel(IDataModel model, int bank, int map, Func<ModelDelta> tokenFactory) {
// get the region from the map
var banks = model.GetTableModel(HardcodeTablesModel.MapBankTable, tokenFactory);
if (banks == null) return;
@@ -109,17 +110,17 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
flySpot.SetValue("y", 0);
}
public bool Equals(IEventModel? other) {
if (other is not FlyEventModel fly) return false;
public bool Equals(IEventViewModel? other) {
if (other is not FlyEventViewModel fly) return false;
return X == fly.X && Y == fly.Y && flySpot.Start == fly.flySpot.Start;
}
public void Render(IDataModel model) {
EventRender = BaseEventModel.BuildEventRender(UncompressedPaletteColor.Pack(31, 31, 0));
EventRender = BaseEventViewModel.BuildEventRender(UncompressedPaletteColor.Pack(31, 31, 0));
}
}
public abstract class BaseEventModel : ViewModelCore, IEventModel, IEquatable<IEventModel> {
public abstract class BaseEventViewModel : ViewModelCore, IEventViewModel, IEquatable<IEventViewModel> {
public event EventHandler EventVisualUpdated;
public event EventHandler<EventCycleDirection> CycleEvent;
@@ -131,6 +132,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
protected readonly ModelArrayElement element;
private readonly string parentLengthField;
public ModelArrayElement Element => element;
public ModelDelta Token => element.Token;
public string EventType => GetType().Name.Replace("EventModel", string.Empty);
@@ -199,12 +201,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public IPixelViewModel EventRender { get; protected set; }
public BaseEventModel(ModelArrayElement element, string parentLengthField) => (this.element, this.parentLengthField) = (element, parentLengthField);
public BaseEventViewModel(ModelArrayElement element, string parentLengthField) => (this.element, this.parentLengthField) = (element, parentLengthField);
public void Delete() => DeleteElement(parentLengthField);
public bool Equals(IEventModel other) {
if (other is not BaseEventModel bem) return false;
public virtual bool Equals(IEventViewModel other) {
if (other is not BaseEventViewModel bem) return false;
return bem.element.Start == element.Start;
}
@@ -292,7 +294,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
}
public class ObjectEventModel : BaseEventModel {
public class ObjectEventViewModel : BaseEventViewModel {
private readonly Action<int> gotoAddress;
public event EventHandler<DataMovedEventArgs> DataMoved;
@@ -577,7 +579,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
#endregion
public ObjectEventModel(Action<int> gotoAddress, ModelArrayElement objectEvent, IReadOnlyList<IPixelViewModel> sprites) : base(objectEvent, "objectCount") {
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));
objectEvent.Model.TryGetList("FacingOptions", out var list);
@@ -630,8 +632,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
}
public class WarpEventModel : BaseEventModel {
public WarpEventModel(ModelArrayElement warpEvent) : base(warpEvent, "warpCount") { }
public class WarpEventViewModel : BaseEventViewModel {
public WarpEventViewModel(ModelArrayElement warpEvent) : base(warpEvent, "warpCount") { }
public int WarpID {
get => element.GetValue("warpID") + 1;
@@ -689,10 +691,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
}
public class ScriptEventModel : BaseEventModel {
public class ScriptEventViewModel : BaseEventViewModel {
private readonly Action<int> gotoAddress;
public ScriptEventModel(Action<int> gotoAddress, ModelArrayElement scriptEvent) : base(scriptEvent, "scriptCount") { this.gotoAddress = gotoAddress; }
public ScriptEventViewModel(Action<int> gotoAddress, ModelArrayElement scriptEvent) : base(scriptEvent, "scriptCount") { this.gotoAddress = gotoAddress; }
public int Trigger {
get => element.GetValue("trigger");
@@ -742,7 +744,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
}
public class SignpostEventModel : BaseEventModel {
public class SignpostEventViewModel : BaseEventViewModel {
// kind. arg::|h
// kind = 0/1/2/3/4 => arg is a pointer to an XSE script
// kind = 5/6/7 => arg is itemID: hiddenItemID. attr|t|quantity:::.|isUnderFoot.
@@ -753,7 +755,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
public event EventHandler<DataMovedEventArgs> DataMoved;
public SignpostEventModel(ModelArrayElement signpostEvent, Action<int> gotoAddress) : base(signpostEvent, "signpostCount") {
public SignpostEventViewModel(ModelArrayElement signpostEvent, Action<int> gotoAddress) : base(signpostEvent, "signpostCount") {
new List<string> {
"Facing Any",
"Facing North",

View File

@@ -1,4 +1,5 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Map;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.Images;
@@ -43,8 +44,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
set => Set(ref showHeaderPanel, value);
}
private IEventModel selectedEvent;
public IEventModel SelectedEvent {
private IEventViewModel selectedEvent;
public IEventViewModel SelectedEvent {
get => selectedEvent;
set {
if (selectedEvent == value) return;
@@ -505,17 +506,17 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
primaryMap.RedrawEvents(); // editing blocks in a map can draw over events, we need to redraw events now that we have new blocks
}
private void EventDown(double x, double y, IEventModel ev, int clickCount) {
private void EventDown(double x, double y, IEventViewModel ev, int clickCount) {
SelectedEvent = ev;
if (SelectedEvent is WarpEventModel warp && clickCount == 2) {
if (SelectedEvent is WarpEventViewModel warp && clickCount == 2) {
NavigateTo(warp.Bank, warp.Map);
} else if (clickCount == 2 && SelectedEvent is ObjectEventModel obj) {
} else if (clickCount == 2 && SelectedEvent is ObjectEventViewModel obj) {
viewPort.Goto.Execute(obj.ScriptAddress);
} else if (clickCount == 2 && SelectedEvent is ScriptEventModel script) {
} else if (clickCount == 2 && SelectedEvent is ScriptEventViewModel script) {
viewPort.Goto.Execute(script.ScriptAddress);
} else if (
clickCount == 2 &&
SelectedEvent is SignpostEventModel signpost &&
SelectedEvent is SignpostEventViewModel signpost &&
signpost.ShowPointer &&
signpost.Pointer >= 0 &&
signpost.Pointer < model.Count
@@ -615,22 +616,29 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
}
public void SelectDown(double x, double y) {
public SelectionInteractionResult SelectDown(double x, double y) {
PrimaryMap.BlockEditor.ShowTiles = false;
PrimaryMap.BorderEditor.ShowBorderPanel = false;
var map = MapUnderCursor(x, y);
if (map == null) return;
if (map == null) return SelectionInteractionResult.None;
PrimaryMap = map;
ShowHeaderPanel = false;
var (blockIndex, collisionIndex) = map.GetBlock(x, y);
SelectedEvent = null;
selectDownPosition = ToTilePosition(x, y);
UpdateHover(selectDownPosition.X, selectDownPosition.Y, 1, 1);
var ev = map.EventUnderCursor(x, y);
if (ev is WarpEventViewModel warp) {
interactionType = PrimaryInteractionType.None;
warpContext = warp;
return SelectionInteractionResult.ShowWarpMenu;
}
if (blockIndex >= 0) {
DrawBlockIndex = blockIndex;
AutoscrollBlocks.Raise(this);
}
if (collisionIndex >= 0) CollisionIndex = collisionIndex;
selectDownPosition = ToTilePosition(x, y);
UpdateHover(selectDownPosition.X, selectDownPosition.Y, 1, 1);
return SelectionInteractionResult.None;
}
public void SelectMove(double x, double y) {
@@ -699,6 +707,17 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
#endregion
#region Creating a new map for a warp
private WarpEventViewModel warpContext;
public void CreateMapForWarp() {
BlockMapViewModel newMap = primaryMap.CreateMapForWarp(warpContext);
if (newMap == null) return;
NavigateTo(newMap.MapID);
}
#endregion
public void Zoom(double x, double y, bool enlarge) {
var map = MapUnderCursor(x, y);
if (map == null) map = primaryMap;
@@ -1014,6 +1033,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
}
public enum SelectionInteractionResult { None, ShowWarpMenu }
public enum EventCreationType { None, Object, Warp, Script, Signpost, Fly }
public enum PrimaryInteractionType { None, Draw, Event }

View File

@@ -1,4 +1,5 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Map;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.Models.Runs.Sprites;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
@@ -197,7 +198,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
}
private void ExecuteDuplicateMap() {
var option = GetMapBankForNewMap("Duplicate map into which group?");
var option = GetMapBankForNewMap("Duplicate map into which bank?");
if (option == -1) return;
var table = AddNewMapToBank(option);
var newMapStart = CreateNewMap(history.CurrentChange);

View File

@@ -228,7 +228,7 @@
</Grid>
<ContentControl DataContext="{Binding SelectedEvent}" Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type map:ObjectEventModel}">
<DataTemplate DataType="{x:Type map:ObjectEventViewModel}">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
@@ -385,7 +385,7 @@
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type map:WarpEventModel}">
<DataTemplate DataType="{x:Type map:WarpEventViewModel}">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
@@ -423,7 +423,7 @@
<local:AngleTextBox Grid.Row="4" Grid.Column="1" VerticalAlignment="Top" Margin="0,2,2,2" Direction="Out" TextBinding="{Binding WarpID, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type map:ScriptEventModel}">
<DataTemplate DataType="{x:Type map:ScriptEventViewModel}">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
@@ -468,7 +468,7 @@
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type map:SignpostEventModel}">
<DataTemplate DataType="{x:Type map:SignpostEventViewModel}">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
@@ -537,7 +537,7 @@
Visibility="{Binding ShowHiddenItemProperties, Converter={StaticResource BoolToVisibility}}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type map:FlyEventModel}">
<DataTemplate DataType="{x:Type map:FlyEventViewModel}">
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
@@ -712,6 +712,11 @@
<local:PixelImage />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ContextMenu>
<ContextMenu IsEnabled="False" ContextMenuClosing="DisableMenuOnClose">
<MenuItem Header="Create New Map" Click="CreateMapForWarpClicked" />
</ContextMenu>
</ItemsControl.ContextMenu>
</ItemsControl>
<ItemsControl Grid.Column="1" Grid.Row="1" Name="MapButtons" ItemsSource="{Binding MapButtons}" Focusable="False">
<ItemsControl.ItemsPanel>

View File

@@ -3,8 +3,10 @@ using HavenSoft.HexManiac.Core.ViewModels.Map;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Linq;
namespace HavenSoft.HexManiac.WPF.Controls {
public partial class MapTab {
@@ -88,7 +90,11 @@ namespace HavenSoft.HexManiac.WPF.Controls {
vm.DragDown(p.X, p.Y);
} else if (e.RightButton == MouseButtonState.Pressed) {
withinMapInteraction = MouseButton.Right;
vm.SelectDown(p.X, p.Y);
var result = vm.SelectDown(p.X, p.Y);
if (result == SelectionInteractionResult.ShowWarpMenu) {
withinMapInteraction = MouseButton.XButton1;
ShowWarpCreation(element, e);
}
}
}
@@ -183,6 +189,20 @@ namespace HavenSoft.HexManiac.WPF.Controls {
return new(p.X - element.ActualWidth / 2, p.Y - element.ActualHeight / 2);
}
private void ShowWarpCreation(FrameworkElement element, MouseEventArgs e) {
element.ContextMenu.IsEnabled = true;
element.ContextMenu.IsOpen = true;
}
private void DisableMenuOnClose(object sender, ContextMenuEventArgs e) {
var menu = (ContextMenu)sender;
menu.IsEnabled = false;
}
private void CreateMapForWarpClicked(object sender, RoutedEventArgs e) {
ViewModel.CreateMapForWarp();
}
#endregion
#region Border Interaction