diff --git a/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs b/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs index 8854fefb..d31aab11 100644 --- a/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/BlockMapViewModel.cs @@ -526,7 +526,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { (LeftEdge, TopEdge) = (-PixelWidth / 2, -PixelHeight / 2); - mapScriptCollection = new(viewPort); + mapScriptCollection = new(viewPort, eventTemplate); mapScriptCollection.NewMapScriptsCreated += (sender, e) => GetMapModel().SetAddress("mapscripts", e.Address); mapRepointer = new MapRepointer(format, fileSystem, viewPort, viewPort.ChangeHistory, MapID, () => { diff --git a/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs b/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs index 835cf67e..1974c1c1 100644 --- a/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs @@ -1,5 +1,4 @@ -using HavenSoft.HexManiac.Core; -using HavenSoft.HexManiac.Core.Models; +using HavenSoft.HexManiac.Core.Models; using HavenSoft.HexManiac.Core.Models.Code; using HavenSoft.HexManiac.Core.Models.Map; using HavenSoft.HexManiac.Core.Models.Runs; @@ -980,7 +979,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public bool ShowTutorContent { get { var content = tutorContent.Value; - if (content != null && TutorOptions .AllOptions == null) { + if (content != null && TutorOptions.AllOptions == null) { TutorOptions.Update(ComboOption.Convert(element.Model.GetOptions(HardcodeTablesModel.MoveTutors)), TutorNumber); TutorOptions.Bind(nameof(TutorOptions.SelectedIndex), (sender, e) => TutorNumber = TutorOptions.SelectedIndex); } @@ -990,7 +989,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private TextEditorViewModel tutorInfoEditor, tutorWhichPokemonEditor, tutorFailedEditor, tutorSuccessEditor; public TextEditorViewModel TutorInfoText => CreateTextEditor(ref tutorInfoEditor, () => tutorContent.Value?.InfoPointer); - public TextEditorViewModel TutorWhichPokemonText => CreateTextEditor(ref tutorWhichPokemonEditor , () => tutorContent.Value?.WhichPokemonPointer); + public TextEditorViewModel TutorWhichPokemonText => CreateTextEditor(ref tutorWhichPokemonEditor, () => tutorContent.Value?.WhichPokemonPointer); public TextEditorViewModel TutorFailedText => CreateTextEditor(ref tutorFailedEditor, () => tutorContent.Value?.FailedPointer); public TextEditorViewModel TutorSucessText => CreateTextEditor(ref tutorSuccessEditor, () => tutorContent.Value?.SuccessPointer); diff --git a/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs b/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs index 1e885179..2a62410c 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapEditorViewModel.cs @@ -185,6 +185,24 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { } } + public bool IsSingleBlock => tilesToDraw is not null && tilesToDraw.GetLength(0) == 1 && tilesToDraw.GetLength(1) == 1; + public int BlockUsageCount { + get { + if (preferredCollisionsPrimary == null) CountCollisionForBlocks(); + var layout = new LayoutModel(primaryMap?.GetLayout()); + if (layout.PrimaryBlockset?.Start is not int primaryAddress) return -1; + if (layout.SecondaryBlockset?.Start is not int secondaryAddress) return -1; + if (!IsSingleBlock) return -1; + + var isPrimary = drawBlockIndex < PrimaryBlocks; + if (isPrimary) { + return blockUsageCount[primaryAddress][drawBlockIndex]; + } else { + return blockUsageCount[secondaryAddress][drawBlockIndex]; + } + } + } + public ObservableCollection CollisionOptions { get; } = new(); public bool BlockSelectionToggle { get; private set; } @@ -1075,7 +1093,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private bool drawMultipleTiles; public bool DrawMultipleTiles { get => drawMultipleTiles; - private set => Set(ref drawMultipleTiles, value, old => NotifyPropertyChanged(nameof(BlockBagVisible))); + private set { + Set(ref drawMultipleTiles, value, old => NotifyPropertyChanged(nameof(BlockBagVisible))); + NotifyPropertiesChanged(nameof(IsSingleBlock), nameof(BlockUsageCount)); + } } private bool blockEditorVisible; @@ -1708,9 +1729,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { }); } public bool IsValid9GridSelection { - get { - return tilesToDraw != null && tilesToDraw.GetLength(0) == 3 && tilesToDraw.GetLength(1) == 3; - } + get => tilesToDraw != null && tilesToDraw.GetLength(0) == 3 && tilesToDraw.GetLength(1) == 3; } public void SelectBlock(int x, int y) { @@ -2078,9 +2097,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { private Dictionary preferredCollisionsPrimary; private Dictionary preferredCollisionsSecondary; + private readonly AutoDictionary> blockUsageCount = new(_ => new(_ => 0)); // for a tileset, for a block, the total number of times that block is used private void CountCollisionForBlocks() { preferredCollisionsPrimary = new Dictionary(); preferredCollisionsSecondary = new Dictionary(); + blockUsageCount.Clear(); // step 1: count the usages of each collision for each block in each blockset var banksTable = model.GetTable(HardcodeTablesModel.MapBankTable); @@ -2121,9 +2142,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { var tile = pair & 0x3FF; var isPrimary = tile < PrimaryTiles; if (isPrimary) { + blockUsageCount[address1][tile] += 1; if (!blocks1[tile].TryGetValue(collision, out var value)) value = 0; blocks1[tile][collision] = value + 1; } else { + blockUsageCount[address2][tile] += 1; if (!blocks2[tile].TryGetValue(collision, out var value)) value = 0; blocks2[tile][collision] = value + 1; } diff --git a/src/HexManiac.Core/ViewModels/Map/MapScriptCollection.cs b/src/HexManiac.Core/ViewModels/Map/MapScriptCollection.cs index 50cf7d57..a9eeab0c 100644 --- a/src/HexManiac.Core/ViewModels/Map/MapScriptCollection.cs +++ b/src/HexManiac.Core/ViewModels/Map/MapScriptCollection.cs @@ -11,6 +11,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public class MapScriptCollection : ViewModelCore { private readonly IEditableViewPort viewPort; + private readonly EventTemplate eventTemplate; private ModelArrayElement owner; private int address; @@ -22,7 +23,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public event EventHandler NewMapScriptsCreated; - public MapScriptCollection(IEditableViewPort viewPort) => this.viewPort = viewPort; + public MapScriptCollection(IEditableViewPort viewPort, EventTemplate eventTemplate) => (this.viewPort, this.eventTemplate) = (viewPort, eventTemplate); public void Load(ModelArrayElement owner) { foreach (var script in Scripts) script.DeleteMe -= HandleDelete; @@ -36,11 +37,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { AllowAddMoreScripts = true; while (model[scriptStart] != 0) { if (Scripts.Count == 10) { - Scripts.Add(new(viewPort, Pointer.NULL)); // 'UI is full' sentinel + Scripts.Add(new(viewPort, eventTemplate, Pointer.NULL)); // 'UI is full' sentinel AllowAddMoreScripts = false; break; } - Scripts.Add(new(viewPort, scriptStart)); + Scripts.Add(new(viewPort, eventTemplate, scriptStart)); AddDeleteHandler(Scripts.Count - 1); scriptStart += 5; } @@ -115,6 +116,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public class MapScriptViewModel : ViewModelCore { private readonly IEditableViewPort viewPort; + private readonly EventTemplate eventTemplate; private readonly int start; private int scriptType, address; private string displayAddress; @@ -128,8 +130,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public ObservableCollection ScriptOptions { get; } = new(); public ObservableCollection SubScripts { get; } = new(); - public MapScriptViewModel(IEditableViewPort viewPort, int start) { + public MapScriptViewModel(IEditableViewPort viewPort, EventTemplate eventTemplate, int start) { this.viewPort = viewPort; + this.eventTemplate = eventTemplate; var model = viewPort.Model; this.start = start; if (start == Pointer.NULL) { @@ -193,7 +196,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { while (true) { var currentValue = model.ReadMultiByteValue(destination, 2); if (currentValue == 0 || currentValue == 0xFFFF) break; - var child = new MapSubScriptViewModel(viewPort, destination); + var child = new MapSubScriptViewModel(viewPort, eventTemplate, destination); child.DeleteMe += HandleDelete; SubScripts.Add(child); destination += 8; @@ -292,7 +295,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { if (run.Start != tableRun.Start) { Load(); } else { - SubScripts.Append(new MapSubScriptViewModel(viewPort, tableRun.Start + tableRun.ElementCount * tableRun.ElementLength - 4)); + SubScripts.Append(new MapSubScriptViewModel(viewPort, eventTemplate, tableRun.Start + tableRun.ElementCount * tableRun.ElementLength - 4)); } } @@ -333,6 +336,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { /// public class MapSubScriptViewModel : ViewModelCore { private readonly IEditableViewPort viewPort; + private readonly EventTemplate eventTemplate; private int start, variable, val, address; private string variableText, valueText, addressText; @@ -340,8 +344,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public event EventHandler DeleteMe; - public MapSubScriptViewModel(IEditableViewPort viewPort, int start) { - (this.viewPort, this.start) = (viewPort, start); + public MapSubScriptViewModel(IEditableViewPort viewPort, EventTemplate eventTemplate, int start) { + (this.viewPort, this.eventTemplate, this.start) = (viewPort, eventTemplate, start); this.variable = viewPort.Model.ReadMultiByteValue(start, 2); this.val = viewPort.Model.ReadMultiByteValue(start + 2, 2); this.address = viewPort.Model.ReadPointer(start + 4); @@ -356,6 +360,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { if (!variableText.TryParseHex(out int result)) return; variable = result; viewPort.Model.WriteMultiByteValue(start, 2, viewPort.ChangeHistory.CurrentChange, variable); + NotifyPropertyChanged(nameof(CanGenerateNewVar)); }); } @@ -377,6 +382,12 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { }); } + public bool CanGenerateNewVar => variable == 0; + public void GenerateNewVar() { + variable = eventTemplate.FindNextUnusedVariable(); + Variable = variable.ToString("X4"); + } + public void Delete() { var args = new MapScriptDeleteEventArgs(); DeleteMe.Raise(this, args); diff --git a/src/HexManiac.WPF/Controls/MapTab.xaml b/src/HexManiac.WPF/Controls/MapTab.xaml index 4b4377e4..415bcb28 100644 --- a/src/HexManiac.WPF/Controls/MapTab.xaml +++ b/src/HexManiac.WPF/Controls/MapTab.xaml @@ -134,6 +134,10 @@ + + + + @@ -1324,7 +1328,19 @@ - + + + + + + Automatically choose an unused var. + + + + +