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);