diff --git a/src/HexManiac.Core/Models/Map/MapModel.cs b/src/HexManiac.Core/Models/Map/MapModel.cs index 9fa95e98..3cafd5d1 100644 --- a/src/HexManiac.Core/Models/Map/MapModel.cs +++ b/src/HexManiac.Core/Models/Map/MapModel.cs @@ -175,7 +175,8 @@ namespace HavenSoft.HexManiac.Core.Models.Map { if (bank == null) return null; var map = bank[Map]; if (map == null) return null; - return map.Events.Warps[WarpID - 1]; + if (map.Events.Warps.Count <= WarpID) return null; + return map.Events.Warps[WarpID]; } } } diff --git a/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs b/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs index 966ff2e6..14a71c00 100644 --- a/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs +++ b/src/HexManiac.Core/ViewModels/Map/EventTemplate.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Text; // example for making a bug trainer: templates.CreateTrainer(objectEvent, history.CurrentChange, 20 /* bug catcher */, 30, 9, 6 /*bug*/, true); @@ -83,8 +84,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { AvailableTemplateTypes.Add(TemplateType.Trainer); AvailableTemplateTypes.Add(TemplateType.Mart); AvailableTemplateTypes.Add(TemplateType.Trade); - AvailableTemplateTypes.Add(TemplateType.HMObject); if (model.IsFRLG() || model.IsEmerald()) AvailableTemplateTypes.Add(TemplateType.Tutor); // Ruby/Sapphire don't have tutors + // AvailableTemplateTypes.Add(TemplateType.Legendary); + AvailableTemplateTypes.Add(TemplateType.HMObject); GraphicsOptions.Clear(); for (int i = 0; i < owGraphics.Count; i++) GraphicsOptions.Add(VisualComboOption.CreateFromSprite(i.ToString(), owGraphics[i].PixelData, owGraphics[i].PixelWidth, i, 2, true)); @@ -837,34 +839,125 @@ wrongspecies: #region Legendary Encounter - public void CreateLegendary(ObjectEventModel objectEventModel, ModelDelta token) { - // TODO + public void CreateLegendary(ObjectEventViewModel objectEventModel, ModelDelta token) { + var legendFlag = 0x21; + while (UsedFlags.Contains(legendFlag)) legendFlag++; + UsedFlags.Add(legendFlag); + int cryText = model.IsFRLG() ? WriteText(token, "Roar!") : Pointer.NULL; + + #region script + var script = new StringBuilder(@" +lock +faceplayer +waitsound +cry 1 2 +setwildbattle 1 50 0 +"); + if (model.IsFRLG()) { + script.AppendLine($"preparemsg <{cryText:X6}>"); + script.AppendLine("waitmsg"); + } + script.AppendLine("waitcry"); + script.AppendLine("pause 10"); + if (model.IsFRLG()) { + script.AppendLine(@" + playsong mus_encounter_gym_leader playOnce + waitkeypress + setflag 0x0807 + special 0x138 + waitstate + clearflag 0x0807 +"); + } else if (model.IsEmerald()) { + script.AppendLine(@" + setflag 0x08C1 + special 0x13A + waitstate + clearflag 0x08C1 +"); + } else { + script.AppendLine(@" + setflag 0x861 + special 0x137 + waitstate + clearflag 0x861 +"); + } + script.AppendLine(@$" +special2 0x800D GetBattleOutcome +if.compare.goto 0x800D = 1 +if.compare.goto 0x800D = 4 +if.compare.goto 0x800D = 5 +setflag 0x{legendFlag:X4} +release +end + +section1: +setflag 0x{legendFlag:X4} +fadescreen 1 +hidesprite 0x800F +fadescreen 0 +release +end + +section2: +fadescreen 1 +hidesprite 0x800F +fadescreen 0 +bufferPokemon 0 1 +msgbox.default +{{ +The [buffer1] disappeared! +}} +release +end +"); + #endregion + + var scriptStart = model.FindFreeSpace(model.FreeSpaceStart, 160); + var scriptText = script.ToString(); + var content = parser.Compile(token, model, scriptStart, ref scriptText, out var _, out var _); + token.ChangeData(model, scriptStart, content); + + objectEventModel.Graphics = trainerGraphics; + objectEventModel.Elevation = FindPreferredTrainerElevation(model, trainerGraphics); + objectEventModel.MoveType = 8; + objectEventModel.RangeX = objectEventModel.RangeY = 0; + objectEventModel.TrainerType = objectEventModel.TrainerRangeOrBerryID = 0; + objectEventModel.ScriptAddress = scriptStart; + objectEventModel.Flag = legendFlag; + + model.ObserveRunWritten(token, new XSERun(scriptStart, SortedSpan.One(objectEventModel.Start + 16))); + parser.FormatScript(token, model, scriptStart); } public LegendaryEventContent GetLegendaryEventContent(ScriptParser parser, ObjectEventViewModel eventModel) => GetLegendaryEventContent(model, parser, eventModel); public static LegendaryEventContent GetLegendaryEventContent(IDataModel model, ScriptParser parser, ObjectEventViewModel ev) { - var content = new LegendaryEventContent(Pointer.NULL, Pointer.NULL, Pointer.NULL, Pointer.NULL); + var content = new LegendaryEventContent(Pointer.NULL, Pointer.NULL, null, null, Pointer.NULL); /* 67 preparemsg text<""> A1 cry species:data.pokemon.names effect: B6 setwildbattle species: level. item: 29 setflag flag: 2A clearflag flag: + 7D bufferPokemon buffer.3 species:data.pokemon.names */ - var spots = Flags.GetAllScriptSpots(model, parser, new[] { ev.ScriptAddress }, 0x67, 0xA1, 0xB6, 0x29, 0x2A); + var spots = Flags.GetAllScriptSpots(model, parser, new[] { ev.ScriptAddress }, 0x67, 0xA1, 0xB6, 0x29, 0x2A, 0x7D); var flagsSet = new Dictionary(); // address of flag -> flag value var flagsCleared = new Dictionary(); // address of flag -> flag value + var bufferSpots = new Dictionary(); // address of buffer -> pokemon to buffer foreach (var spot in spots) { if (spot.Line.LineCode[0] == 0x29) { flagsSet[spot.Address + 1] = model.ReadMultiByteValue(spot.Address + 1, 2); } else if (spot.Line.LineCode[0] == 0x2A) { flagsCleared[spot.Address + 1] = model.ReadMultiByteValue(spot.Address + 1, 2); + } else if (spot.Line.LineCode[0] == 0x7D) { + bufferSpots[spot.Address + 2] = model.ReadMultiByteValue(spot.Address + 2, 2); } else { content = spot.Line.LineCode[0] switch { 0x67 => content with { CryTextPointer = spot.Address + 1 }, 0xA1 => content with { Cry = spot.Address }, 0xB6 => content with { SetWildBattle = spot.Address }, - 0x29 => content with { SetFlag = spot.Address }, _ => throw new NotImplementedException(), }; } @@ -873,10 +966,15 @@ wrongspecies: if (content.SetWildBattle == Pointer.NULL) return null; var setOnlyFlags = flagsSet.Values.Except(flagsCleared.Values).ToHashSet(); if (setOnlyFlags.Count != 1) return null; + var bufferPokemon = new List(); + foreach (var kvp in bufferSpots) { + if (kvp.Value == model.ReadMultiByteValue(content.SetWildBattle + 1, 2)) bufferPokemon.Add(kvp.Value - 2); + } var legendFlag = setOnlyFlags.Single(); - var legendFlagAddress = flagsSet.Keys.First(key => flagsSet[key] == legendFlag); - content = content with { SetFlag = legendFlagAddress - 1 }; + var legendFlagAddress = flagsSet.Keys.Where(key => flagsSet[key] == legendFlag); + content = content with { SetFlag = legendFlagAddress.Select(flag => flag - 1).ToList() }; + content = content with { BufferPokemon = bufferPokemon }; return content; } @@ -942,7 +1040,7 @@ wrongspecies: private void UpdateObjectTemplateImage(TemplateType old = default) { if (selectedTemplate == TemplateType.None) { ObjectTemplateImage = GraphicsOptions[0]; - } else if (selectedTemplate.IsAny(TemplateType.Trainer, TemplateType.Npc, TemplateType.Tutor, TemplateType.Trade, TemplateType.HMObject)) { + } else if (selectedTemplate.IsAny(TemplateType.Trainer, TemplateType.Npc, TemplateType.Tutor, TemplateType.Trade, TemplateType.Legendary, TemplateType.HMObject)) { ObjectTemplateImage = GraphicsOptions[TrainerGraphics]; } else if (selectedTemplate == TemplateType.Item) { ObjectTemplateImage = GraphicsOptions[ItemGraphics]; @@ -965,7 +1063,7 @@ wrongspecies: public record TradeEventContent(int InfoPointer, int ThanksPointer, int SuccessPointer, int FailedPointer, int WrongSpeciesPointer, int TradeAddress); - public record LegendaryEventContent(int Cry, int SetWildBattle, int SetFlag, int CryTextPointer); + public record LegendaryEventContent(int Cry, int SetWildBattle, List BufferPokemon, List SetFlag, int CryTextPointer); public enum TemplateType { None, Npc, Item, Trainer, Mart, Tutor, Trade, Legendary, HMObject } } diff --git a/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs b/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs index 12e4f75b..be930165 100644 --- a/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs +++ b/src/HexManiac.Core/ViewModels/Map/IEventViewModel.cs @@ -952,6 +952,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { PokemonOptions.Bind(nameof(PokemonOptions.SelectedIndex), (sender, e) => { element.Model.WriteMultiByteValue(content.Cry + 1, 2, element.Token, PokemonOptions.SelectedIndex); element.Model.WriteMultiByteValue(content.SetWildBattle + 1, 2, element.Token, PokemonOptions.SelectedIndex); + foreach (var buffer in content.BufferPokemon) element.Model.WriteMultiByteValue(buffer + 2, 2, element.Token, PokemonOptions.SelectedIndex); }); } if (content != null && HoldItemOptions.AllOptions == null) { @@ -978,13 +979,15 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map { public string LegendaryFlagText { get { if (legendaryContent.Value == null) return null; - if (legendaryFlagText == null) legendaryFlagText = element.Model.ReadMultiByteValue(legendaryContent.Value.SetFlag + 1, 2).ToString("X4"); + if (legendaryFlagText == null) legendaryFlagText = element.Model.ReadMultiByteValue(legendaryContent.Value.SetFlag[0] + 1, 2).ToString("X4"); return legendaryFlagText; } set { if (legendaryContent.Value == null) return; legendaryFlagText = value; - element.Model.WriteMultiByteValue(legendaryContent.Value.SetFlag + 1, 2, element.Token, value.TryParseHex(out int result) ? result : 0); + foreach (var flag in legendaryContent.Value.SetFlag) { + element.Model.WriteMultiByteValue(flag + 1, 2, element.Token, value.TryParseHex(out int result) ? result : 0); + } NotifyPropertyChanged(); } } diff --git a/src/HexManiac.WPF/Controls/MapTab.xaml b/src/HexManiac.WPF/Controls/MapTab.xaml index 46468959..1f2e92ab 100644 --- a/src/HexManiac.WPF/Controls/MapTab.xaml +++ b/src/HexManiac.WPF/Controls/MapTab.xaml @@ -1615,6 +1615,10 @@ + + + +