add 'scripts' option to 'show uses' button

Unlike the other ones, this one doesn't pre-calculate, and may end up being empty when you click on it.
This commit is contained in:
haven1433 2022-11-23 08:26:46 -06:00
parent 9e42cedeac
commit e285eaaf7b
4 changed files with 67 additions and 12 deletions

View File

@ -83,6 +83,17 @@ namespace HavenSoft.HexManiac.Core.Models.Code {
public ScriptLine GetLine(IDataModel model, int address) => engine.GetMatchingLine(model, address);
public IEnumerable<ScriptLine> DependsOn(string basename) {
foreach (var line in engine) {
foreach (var arg in line.Args) {
if (arg.EnumTableName == basename) {
yield return line;
break;
}
}
}
}
// TODO refactor to rely on CollectScripts rather than duplicate code
public void FormatScript<TSERun>(ModelDelta token, IDataModel model, int address) where TSERun : IScriptStartRun {
Func<int, SortedSpan<int>, IScriptStartRun> constructor = (a, s) => new XSERun(a, s);

View File

@ -124,17 +124,17 @@
5A faceplayer # if the script was called by a person event, make that person face the player
5B spriteface npc: direction.
5C trainerbattle 00 trainer: arg: start<> playerwin<>
5C trainerbattle 01 trainer: arg: start<> playerwin<> winscript<> # doesn't play encounter music, continues with winscript
5C trainerbattle 02 trainer: arg: start<> playerwin<> winscript<> # does play encounter music, continues with winscript
5C trainerbattle 03 trainer: arg: playerwin<> # no intro text
5C trainerbattle 04 trainer: arg: start<> playerwin<> needmorepokemonText<> # double battles
5C trainerbattle 05 trainer: arg: start<> playerwin<> # clone of 0, but with rematch potential
5C trainerbattle 06 trainer: arg: start<> playerwin<> needmorepokemonText<> unknown<> # double battles
5C trainerbattle 07 trainer: arg: start<> playerwin<> needmorepokemonText<> # clone of 4, but with rematch potential
5C trainerbattle 08 trainer: arg: start<> playerwin<> needmorepokemonText<> unknown<> # clone of 6
5C trainerbattle 09 trainer: arg: start<> playerwin<> # tutorial battle (can't lose) (set arg=3 for oak's naration)
5C trainerbattle other. trainer: arg: start<> playerwin<> # same as 0
5C trainerbattle 00 trainer:data.trainers.stats arg: start<> playerwin<>
5C trainerbattle 01 trainer:data.trainers.stats arg: start<> playerwin<> winscript<> # doesn't play encounter music, continues with winscript
5C trainerbattle 02 trainer:data.trainers.stats arg: start<> playerwin<> winscript<> # does play encounter music, continues with winscript
5C trainerbattle 03 trainer:data.trainers.stats arg: playerwin<> # no intro text
5C trainerbattle 04 trainer:data.trainers.stats arg: start<> playerwin<> needmorepokemonText<> # double battles
5C trainerbattle 05 trainer:data.trainers.stats arg: start<> playerwin<> # clone of 0, but with rematch potential
5C trainerbattle 06 trainer:data.trainers.stats arg: start<> playerwin<> needmorepokemonText<> unknown<> # double battles
5C trainerbattle 07 trainer:data.trainers.stats arg: start<> playerwin<> needmorepokemonText<> # clone of 4, but with rematch potential
5C trainerbattle 08 trainer:data.trainers.stats arg: start<> playerwin<> needmorepokemonText<> unknown<> # clone of 6
5C trainerbattle 09 trainer:data.trainers.stats arg: start<> playerwin<> # tutorial battle (can't lose) (set arg=3 for oak's naration)
5C trainerbattle other. trainer:data.trainers.stats arg: start<> playerwin<> # same as 0
# trainer battle takes different parameters depending on the
# 'type', or the first parameter.
# 'trainer' is the ID of the trainer battle

View File

@ -1,6 +1,7 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.Map;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@ -141,6 +142,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
private set => TryUpdate(ref enabled, value);
}
private bool usageOptionsOpen;
public bool UsageOptionsOpen { get => usageOptionsOpen; set => Set(ref usageOptionsOpen, value); }
private string fieldFilter = string.Empty;
public string FieldFilter {
get => fieldFilter;
@ -468,18 +472,21 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
return s;
}).ToList() : null;
AddUsageChild(new ButtonArrayElementViewModel("trainer teams", () => {
UsageOptionsOpen = false;
viewPort.OpenSearchResultsTab($"{elementName} within {HardcodeTablesModel.TrainerTableName}", selections, trainerAddresses);
}));
}
if (plmResults.Count > 0) {
AddUsageChild(new ButtonArrayElementViewModel("level-up moves", () => {
UsageOptionsOpen = false;
viewPort.OpenSearchResultsTab($"{elementName} within {HardcodeTablesModel.LevelMovesTableName}", plmResults);
}));
}
if (eggResults.Count > 0) {
AddUsageChild(new ButtonArrayElementViewModel("egg moves", () => {
UsageOptionsOpen = false;
viewPort.OpenSearchResultsTab($"{elementName} within {HardcodeTablesModel.EggMovesTableName}", eggResults);
}));
}
@ -490,15 +497,52 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
if (results.Count == 0) continue;
var name = model.GetAnchorFromAddress(-1, table.Start);
AddUsageChild(new ButtonArrayElementViewModel(name, name, () => {
UsageOptionsOpen = false;
viewPort.OpenSearchResultsTab($"{elementName} within {name}", results);
}));
}
if (streamResults.Count > 0) {
AddUsageChild(new ButtonArrayElementViewModel("other streams", () => {
UsageOptionsOpen = false;
viewPort.OpenSearchResultsTab($"{elementName} within streams", streamResults);
}));
}
// only check XSE scripts for now
if (viewPort.Tools.CodeTool.ScriptParser.DependsOn(basename).Any()) {
AddUsageChild(new ButtonArrayElementViewModel("scripts", () => {
var results = new List<(int, int)>(FindXseScriptUses(basename, index));
if (results.Count == 0) {
OnMessage?.Invoke(this, "No matches were found.");
} else {
UsageOptionsOpen = false;
viewPort.Tools.SelectedTool = viewPort.Tools.CodeTool;
viewPort.Tools.CodeTool.Mode = CodeMode.Script;
viewPort.OpenSearchResultsTab($"{elementName} within scripts", results);
viewPort.Tools.SelectedTool = viewPort.Tools.CodeTool;
viewPort.Tools.CodeTool.Mode = CodeMode.Script;
}
}));
}
}
private IEnumerable<(int, int)> FindXseScriptUses(string basename, int index) {
var parser = viewPort.Tools.CodeTool.ScriptParser;
var lines = parser.DependsOn(basename).ToList();
var filter = lines.Select(line => line.LineCode[0]).ToArray();
foreach (var spot in Flags.GetAllScriptSpots(model, parser, Flags.GetAllTopLevelScripts(model), filter)) {
int check = spot.Address + spot.Line.LineCode.Count;
foreach (var arg in spot.Line.Args) {
var length = arg.Length(model, check);
if (arg.EnumTableName == basename) {
if (model.ReadMultiByteValue(check, length) == index) {
yield return (spot.Address, check + length - 1);
}
}
check += length;
}
}
}
private void ApplyFieldFilter() {

View File

@ -211,7 +211,7 @@
so we declare an extra ItemsControl just so we can access its HasItems property
-->
<ItemsControl Name="UsageItems" ItemsSource="{Binding TableTool.UsageChildren}" Visibility="Collapsed" />
<hsg3hv:AngleButton x:Name="ShowUsesButton" Content="Show Uses" HorizontalAlignment="Center" IsEnabled="{Binding HasItems, ElementName=UsageItems}" Direction="Out" />
<hsg3hv:AngleButton x:Name="ShowUsesButton" Content="Show Uses" HorizontalAlignment="Center" IsEnabled="{Binding HasItems, ElementName=UsageItems}" Direction="Out" IsChecked="{Binding TableTool.UsageOptionsOpen}" />
<Popup IsOpen="{Binding IsChecked, ElementName=ShowUsesButton}" StaysOpen="{Binding IsMouseOver, ElementName=ShowUsesButton}" PlacementTarget="{Binding ElementName=ShowUsesButton}">
<Border Background="{DynamicResource Background}" BorderThickness="1" BorderBrush="{DynamicResource Accent}">
<ItemsControl ItemsSource="{Binding TableTool.UsageChildren}" />