diff --git a/pkNX.Game/File/GameFile.cs b/pkNX.Game/File/GameFile.cs index 548fcb13..824e496a 100644 --- a/pkNX.Game/File/GameFile.cs +++ b/pkNX.Game/File/GameFile.cs @@ -190,5 +190,8 @@ public enum GameFile /// "PokeMisc" Details about a given Species-Form not stored in PokeMisc, + + /// Gives bonus rolls based on met thresholds. + ShinyRolls, } } diff --git a/pkNX.Game/File/GameFileMapping.cs b/pkNX.Game/File/GameFileMapping.cs index 911beaeb..67d5f918 100644 --- a/pkNX.Game/File/GameFileMapping.cs +++ b/pkNX.Game/File/GameFileMapping.cs @@ -460,6 +460,7 @@ public static IReadOnlyCollection GetMapping(GameVersion game new(GameFile.PokeMisc, ContainerType.SingleFile, "bin", "pokemon", "data", "poke_misc.bin"), new(GameFile.Outbreak, ContainerType.SingleFile, "bin", "field", "encount", "huge_outbreak.bin"), new(GameFile.MoveShop, ContainerType.SingleFile, "bin", "appli", "wazaremember", "bin", "wazashop_table.bin"), + new(GameFile.ShinyRolls, ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_rare.bin"), // Cutscenes bin\demo // Models bin\archive\pokemon diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/AreaInstance8a.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/AreaInstance8a.cs index 25d3a083..fb86923e 100644 --- a/pkNX.Structures.FlatBuffers/Arceus/Placement/AreaInstance8a.cs +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/AreaInstance8a.cs @@ -16,6 +16,7 @@ public sealed class AreaInstance8a public readonly LandmarkItem8a[] LandMarks; public readonly PlacementUnnnEntry[] Unown; public readonly PlacementMkrgEntry[] Mikaruge; + public readonly PlacementSearchItem[] SearchItem; public readonly EncounterDataArchive8a Encounters; @@ -36,6 +37,7 @@ private AreaInstance8a(GFPack resident, string areaName, AreaInstance8a? parentA var l_markf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/landmark_item/landmark_item.bin"); var unnf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/unnn/unnn.bin"); var mkrgf = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/mkrg/mkrg.bin"); + var psif = resident.GetDataFullPath($"bin/field/param/placement/{areaName}/search_item/search_item.bin"); Locations = FlatBufferConverter.DeserializeFrom(locationf).Table; Spawners = FlatBufferConverter.DeserializeFrom(spawnerf).Table; @@ -44,6 +46,7 @@ private AreaInstance8a(GFPack resident, string areaName, AreaInstance8a? parentA LandMarks = FlatBufferConverter.DeserializeFrom(l_markf).Table; Unown = FlatBufferConverter.DeserializeFrom(unnf).Table; Mikaruge = FlatBufferConverter.DeserializeFrom(mkrgf).Table; + SearchItem = FlatBufferConverter.DeserializeFrom(psif).Table; AreaName = areaName; diff --git a/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSearchItemTable.cs b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSearchItemTable.cs new file mode 100644 index 00000000..86a1f9cf --- /dev/null +++ b/pkNX.Structures.FlatBuffers/Arceus/Placement/PlacementSearchItemTable.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using FlatSharp.Attributes; + +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedType.Global +// ReSharper disable UnusedMember.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace pkNX.Structures.FlatBuffers; + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementSearchItemTable : IFlatBufferArchive +{ + public byte[] Write() => FlatBufferConverter.SerializeFrom(this); + + [FlatBufferItem(0)] public PlacementSearchItem[] Table { get; set; } = Array.Empty(); +} + +[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))] +public class PlacementSearchItem +{ + [FlatBufferItem(0)] public string Identifier { get; set; } = string.Empty; + [FlatBufferItem(1)] public ulong Hash_01 { get; set; } + [FlatBufferItem(2)] public PlacementParameters8a[] Field_02 { get; set; } = Array.Empty(); + [FlatBufferItem(03)] public string Field_03 { get; set; } = string.Empty; + [FlatBufferItem(04)] public string Field_04 { get; set; } = string.Empty; + [FlatBufferItem(05)] public int Rate { get; set; } + [FlatBufferItem(06)] public bool Field_06 { get; set; } // bool + [FlatBufferItem(07)] public bool Field_07 { get; set; } // bool + [FlatBufferItem(08)] public PlacementV3f8a Field_08 { get; set; } = new(); + [FlatBufferItem(09)] public byte Field_09 { get; set; } // bool + [FlatBufferItem(10)] public PlacementV3f8a Field_10 { get; set; } = new(); + [FlatBufferItem(11)] public bool Field_11 { get; set; } // bool + [FlatBufferItem(12)] public PlacementV3f8a Field_12 { get; set; } = new(); + [FlatBufferItem(13)] public bool Field_13 { get; set; } // bool + [FlatBufferItem(14)] public PlacementV3f8a Field_14 { get; set; } = new(); + [FlatBufferItem(15)] public bool Field_15 { get; set; } // bool + [FlatBufferItem(16)] public PlacementV3f8a Field_16 { get; set; } = new(); + [FlatBufferItem(17)] public bool Field_17 { get; set; } // bool + [FlatBufferItem(18)] public PlacementV3f8a Field_18 { get; set; } = new(); + [FlatBufferItem(19)] public bool Field_19 { get; set; } // bool + [FlatBufferItem(20)] public PlacementV3f8a Field_20 { get; set; } = new(); + [FlatBufferItem(21)] public bool Field_21 { get; set; } // bool + [FlatBufferItem(22)] public PlacementV3f8a Field_22 { get; set; } = new(); + [FlatBufferItem(23)] public bool Field_23 { get; set; } // bool + [FlatBufferItem(24)] public PlacementV3f8a Field_24 { get; set; } = new(); + [FlatBufferItem(25)] public bool Field_25 { get; set; } // bool + [FlatBufferItem(26)] public PlacementV3f8a Field_26 { get; set; } = new(); + + public PlacementParameters8a Parameters + { + get { if (Field_02.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_02)}"); return Field_02[0]; } + set { if (Field_02.Length != 1) throw new ArgumentException($"Invalid {nameof(Field_02)}"); Field_02[0] = value; } + } + + public override string ToString() => $"SearchItem(\"{Identifier}\", 0x{Hash_01:X16}, {Parameters}, {Rate}, {Field_03}, {Field_04})"; + + public IEnumerable GetContainingLocations(IReadOnlyList locations) + { + var result = new List(); + foreach (var loc in locations) + { + if (!loc.IsNamedPlace) + continue; + if (loc.Contains(Parameters.Coordinates)) + result.Add(loc); + } + if (result.Count == 0) + result.Add(locations.First(l => l.IsNamedPlace)); // base area name + return result; + } +} diff --git a/pkNX.Structures.FlatBuffers/Arceus/PokemonRare8aTable.cs b/pkNX.Structures.FlatBuffers/Arceus/PokemonRare8aTable.cs index b63f8d41..c66a65fe 100644 --- a/pkNX.Structures.FlatBuffers/Arceus/PokemonRare8aTable.cs +++ b/pkNX.Structures.FlatBuffers/Arceus/PokemonRare8aTable.cs @@ -26,7 +26,12 @@ public class PokemonRare8aEntry [FlatBufferItem(01)] public ulong Hash { get; set; } [FlatBufferItem(02)] public byte UnusedValue { get; set; } // none have this [FlatBufferItem(03)] public string Option { get; set; } = string.Empty; - [FlatBufferItem(04)] public string Value { get; set; } = string.Empty; - [FlatBufferItem(05)] public string[] Field_05 { get; set; } = Array.Empty(); + [FlatBufferItem(04)] public string Field_04 { get; set; } = string.Empty; + [FlatBufferItem(05)] public string[] Parameters { get; set; } = Array.Empty(); [FlatBufferItem(06)] public FlatDummyEntry[] UnusedArray { get; set; } = Array.Empty(); // none have this + public string ConfiguredValue + { + get => Parameters[0]; + set => Parameters[0] = value; + } } diff --git a/pkNX.WinForms/Dumping/GameDumperPLA.cs b/pkNX.WinForms/Dumping/GameDumperPLA.cs index 0834a508..79cbc6e0 100644 --- a/pkNX.WinForms/Dumping/GameDumperPLA.cs +++ b/pkNX.WinForms/Dumping/GameDumperPLA.cs @@ -460,6 +460,8 @@ public void DumpWilds() var allLandItems = new List(); var allLandMarks = new List(); var allUnown = new List(); + var allMkrg = new List(); + var allSearchItem = new List(); foreach (var areaNameList in PLAInfo.AreaNames) { var instance = AreaInstance8a.Create(resident, areaNameList); @@ -490,6 +492,8 @@ public void DumpWilds() allLandItems.AddRange(instance.LandItems); allLandMarks.AddRange(instance.LandMarks); allUnown.AddRange(instance.Unown); + allMkrg.AddRange(instance.Mikaruge); + allSearchItem.AddRange(instance.SearchItem); foreach (var subArea in instance.SubAreas) { @@ -499,6 +503,9 @@ public void DumpWilds() allLocations.AddRange(subArea.Locations); allLandItems.AddRange(subArea.LandItems); allLandMarks.AddRange(subArea.LandMarks); + allUnown.AddRange(subArea.Unown); + allMkrg.AddRange(subArea.Mikaruge); + allSearchItem.AddRange(subArea.SearchItem); } } @@ -518,6 +525,8 @@ public void DumpWilds() File.WriteAllText(GetPath(wild, "allLandMarks.csv"), TableUtil.GetTable(allLandMarks)); File.WriteAllText(GetPath(wild, "allLandMarkSpawns.csv"), TableUtil.GetTable(allLandMarks)); File.WriteAllText(GetPath(wild, "allUnown.csv"), TableUtil.GetTable(allUnown)); + File.WriteAllText(GetPath(wild, "allMkrg.csv"), TableUtil.GetTable(allMkrg)); + File.WriteAllText(GetPath(wild, "allSearchItem.csv"), TableUtil.GetTable(allSearchItem)); } public void DumpResident() @@ -610,6 +619,14 @@ public void DumpPlacement() mkrg_all.Add(string.Empty); File.WriteAllLines(GetPath(placement, $"Mikaruge_{areaName}.txt"), mkrg_lines); } + if (subArea.SearchItem.Length != 0) + { + var mkrg_lines = GetSearchItemLines(areaName, map, subArea.SearchItem, subArea.Locations); + + mkrg_all.AddRange(mkrg_lines); + mkrg_all.Add(string.Empty); + File.WriteAllLines(GetPath(placement, $"SearchItem_{areaName}.txt"), mkrg_lines); + } // Debug for Visualization if (new[] { "ha_area01", "ha_area02", "ha_area03", "ha_area04", "ha_area05" }.Contains(areaName)) @@ -684,6 +701,20 @@ public void DumpPlacement() return result; } + private static IReadOnlyList GetSearchItemLines(string areaName, + IReadOnlyDictionary map, + IEnumerable mkrgs, + IReadOnlyList locations) + { + var result = new List { $"Area: {areaName}" }; + foreach (var psi in mkrgs) + { + var contained = GetNearbyLocationNames(psi, locations, map); + result.Add($"\t{psi} // Containing Locations: {contained}"); + } + return result; + } + private static string GetNearbyLocationNames(PlacementSpawner8a spawner, IReadOnlyList locations, IReadOnlyDictionary map) @@ -694,6 +725,16 @@ public void DumpPlacement() return string.Join(", ", localized); } + private static string GetNearbyLocationNames(PlacementSearchItem mkrg, + IReadOnlyList locations, + IReadOnlyDictionary map) + { + var containedBy = mkrg.GetContainingLocations(locations); + var placeNames = containedBy.Select(z => z.PlaceName).Distinct(); + var localized = placeNames.Select(pn => map[pn].Name); + return string.Join(", ", localized); + } + private static string GetNearbyLocationNames(PlacementMkrgEntry mkrg, IReadOnlyList locations, IReadOnlyDictionary map) diff --git a/pkNX.WinForms/MainEditor/EditorPLA.cs b/pkNX.WinForms/MainEditor/EditorPLA.cs index c4c2a74e..d4a44ee5 100644 --- a/pkNX.WinForms/MainEditor/EditorPLA.cs +++ b/pkNX.WinForms/MainEditor/EditorPLA.cs @@ -1,6 +1,5 @@ using System.IO; using System.Linq; -using System.Windows.Forms; using pkNX.Containers; using pkNX.Game; using pkNX.Structures; @@ -107,6 +106,20 @@ public void EditItems() obj[0] = Item8a.SetArray(items, data); } + public void EditShinyRate() + { + var obj = ROM.GetFile(GameFile.ShinyRolls); // flatbuffer + var data = obj[0]; + var root = FlatBufferConverter.DeserializeFrom(data); + var cache = new DataCache(root.Table); + var names = root.Table.Select(z => z.Name).ToArray(); + using var form = new GenericEditor(cache, names, "Shiny Rate Editor"); + form.ShowDialog(); + if (!form.Modified) + return; + obj[0] = FlatBufferConverter.SerializeFrom(root); + } + public void EditSymbolBehave() { var obj = ROM.GetFile(GameFile.SymbolBehave);