diff --git a/FlatBuffers/ZA/Battle/Schemas/ItemTableArray.fbs b/FlatBuffers/ZA/Battle/Schemas/ItemTableArray.fbs
new file mode 100644
index 00000000..4c1d689c
--- /dev/null
+++ b/FlatBuffers/ZA/Battle/Schemas/ItemTableArray.fbs
@@ -0,0 +1,13 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+attribute "fs_serializer";
+
+table ItemTable {
+ ItemID:int;
+ PlibID:int;
+}
+
+table ItemTableArray (fs_serializer) {
+ Table:[ItemTable] (required);
+}
+
+root_type ItemTableArray;
diff --git a/FlatBuffers/ZA/Misc/Schemas/ConditionRareDBArray.fbs b/FlatBuffers/ZA/Misc/Schemas/ConditionRareDBArray.fbs
new file mode 100644
index 00000000..84282ce2
--- /dev/null
+++ b/FlatBuffers/ZA/Misc/Schemas/ConditionRareDBArray.fbs
@@ -0,0 +1,18 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table ConditionRareDB {
+ Table:[ConditionRare] (required);
+}
+
+table ConditionRareDBArray (fs_serializer) {
+ Table:[ConditionRareDB] (required);
+}
+
+table ConditionRare {
+ Version:float;
+ TriggerID:int;
+ ComparisonOperatorType:int;
+}
+root_type ConditionRareDBArray;
diff --git a/FlatBuffers/ZA/Misc/Schemas/Donut/BerryDetailArray.fbs b/FlatBuffers/ZA/Misc/Schemas/Donut/BerryDetailArray.fbs
new file mode 100644
index 00000000..47f0cee9
--- /dev/null
+++ b/FlatBuffers/ZA/Misc/Schemas/Donut/BerryDetailArray.fbs
@@ -0,0 +1,22 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table BerryDetail {
+ ItemID:uint;
+ DonutIndex:uint;
+ IsHyperspace:bool;
+ Spicy:uint;
+ Fresh:uint;
+ Sweet:uint;
+ Bitter:uint;
+ Sour:uint;
+ LevelBoost:uint;
+ Calories:uint;
+}
+
+table BerryDetailArray (fs_serializer) {
+ Table:[BerryDetail] (required);
+}
+
+root_type BerryDetailArray;
diff --git a/FlatBuffers/ZA/Misc/Schemas/Donut/DonutDetailArray.fbs b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutDetailArray.fbs
new file mode 100644
index 00000000..44524e12
--- /dev/null
+++ b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutDetailArray.fbs
@@ -0,0 +1,17 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DonutDetail {
+ Index:uint;
+ FlavorDominant:uint;
+ BerryBase:uint;
+ Name:string (required);
+ Info:string (required);
+}
+
+table DonutDetailArray (fs_serializer) {
+ Table:[DonutDetail] (required);
+}
+
+root_type DonutDetailArray;
diff --git a/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorArray.fbs b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorArray.fbs
new file mode 100644
index 00000000..99d28a63
--- /dev/null
+++ b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorArray.fbs
@@ -0,0 +1,26 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DonutFlavor {
+ Flavor:string (required); // power granted
+ FlavorName:int;
+ UniqueEffect:int;
+ BoostArg:int; // type to boost, or which variant of the flavor
+ Field04:int;
+ Category2:int; // does this prevent multiple in same category?
+ IsBattleStatBoost:bool;
+ Level:int;
+ LevelVariance:int; // mostly matches level, besides shiny power (3) and legends (1)
+ IsSpecial:bool;
+ Name:string (required);
+ Info:string (required);
+ PrefixAdj:string (required); // based on the primary power
+ Detail:string (required);
+}
+
+table DonutFlavorArray (fs_serializer) {
+ Table:[DonutFlavor] (required);
+}
+
+root_type DonutFlavorArray;
diff --git a/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorLotteryArray.fbs b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorLotteryArray.fbs
new file mode 100644
index 00000000..c3cf6500
--- /dev/null
+++ b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorLotteryArray.fbs
@@ -0,0 +1,16 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DonutFlavorLottery {
+ Count:uint;
+ Lv1:uint;
+ Lv2:uint;
+ Lv3:uint;
+}
+
+table DonutFlavorLotteryArray (fs_serializer) {
+ Table:[DonutFlavorLottery] (required);
+}
+
+root_type DonutFlavorLotteryArray;
diff --git a/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorParameterArray.fbs b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorParameterArray.fbs
new file mode 100644
index 00000000..73941161
--- /dev/null
+++ b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutFlavorParameterArray.fbs
@@ -0,0 +1,16 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DonutFlavorParameter {
+ Index:uint;
+ Lv1:float;
+ Lv2:float;
+ Lv3:float;
+}
+
+table DonutFlavorParameterArray (fs_serializer) {
+ Table:[DonutFlavorParameter] (required);
+}
+
+root_type DonutFlavorParameterArray;
diff --git a/FlatBuffers/ZA/Misc/Schemas/Donut/DonutOneWordArray.fbs b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutOneWordArray.fbs
new file mode 100644
index 00000000..591bbd92
--- /dev/null
+++ b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutOneWordArray.fbs
@@ -0,0 +1,20 @@
+include "Shared/ActivationCondition.fbs";
+include "Shared/ActivationConditionElement.fbs";
+include "Shared/ActivationConditionParam.fbs";
+
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DonutOneWord {
+ Text:string (required);
+ State:uint; // start=0, working=1, max=2, leave=3
+ Other:uint; // only used by oneword_max_*
+ Condition:[ActivationCondition] (required);
+}
+
+table DonutOneWordArray (fs_serializer) {
+ Table:[DonutOneWord] (required);
+}
+
+root_type DonutOneWordArray;
diff --git a/FlatBuffers/ZA/Misc/Schemas/Donut/DonutRecipeArray.fbs b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutRecipeArray.fbs
new file mode 100644
index 00000000..59fc7853
--- /dev/null
+++ b/FlatBuffers/ZA/Misc/Schemas/Donut/DonutRecipeArray.fbs
@@ -0,0 +1,26 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+// special donut recipe
+table DonutRecipe {
+ Index:uint;
+ SweetMin:uint;
+ SweetMax:uint;
+ SpicyMin:uint;
+ SpicyMax:uint;
+ SourMin:uint;
+ SourMax:uint;
+ BitterMin:uint;
+ BitterMax:uint;
+ FreshMin:uint;
+ FreshMax:uint;
+ DonutName:uint;
+ RecipeName:string (required);
+}
+
+table DonutRecipeArray (fs_serializer) {
+ Table:[DonutRecipe] (required);
+}
+
+root_type DonutRecipeArray;
diff --git a/FlatBuffers/ZA/Misc/pkNX.Structures.FlatBuffers.ZA.Misc.csproj b/FlatBuffers/ZA/Misc/pkNX.Structures.FlatBuffers.ZA.Misc.csproj
index 46d17945..6b512ec9 100644
--- a/FlatBuffers/ZA/Misc/pkNX.Structures.FlatBuffers.ZA.Misc.csproj
+++ b/FlatBuffers/ZA/Misc/pkNX.Structures.FlatBuffers.ZA.Misc.csproj
@@ -1,5 +1 @@
-
-
-
-
-
+
diff --git a/FlatBuffers/ZA/Personal/Dumpers/PersonalDumper9a.cs b/FlatBuffers/ZA/Personal/Dumpers/PersonalDumper9a.cs
index dca092aa..c23c511f 100644
--- a/FlatBuffers/ZA/Personal/Dumpers/PersonalDumper9a.cs
+++ b/FlatBuffers/ZA/Personal/Dumpers/PersonalDumper9a.cs
@@ -54,6 +54,8 @@ public void AddDump(List lines, PersonalTable9ZA table, ushort speciesIn
if (form != 0)
name += $"-{form}";
name += $" #{entry.DexIndex:000}";
+ // if (entry.DexIndex != 0) // every group is 0, don't bother emitting. The game does -232 to check >=1 for Hyperspace Dex.
+ // name += $" [{entry.DexGroup}]";
AddDump(lines, entry, index, name, speciesInternal, form);
}
@@ -366,6 +368,20 @@ public static class Plib9
{ 0094, 2403 }, // Unremarkable Teacup
{ 0095, 2404 }, // Masterpiece Teacup
{ 0096, 2402 }, // Syrupy Apple
+ { 0097, 0000 },
+ { 0098, 0000 },
+ { 0099, 0000 },
+ { 0100, 0000 },
+ { 0101, 0000 },
+ { 0102, 0765 }, // Prison Bottle
+ { 0103, 0000 },
+ { 0104, 0000 },
+ { 0105, 0000 },
+ { 0106, 0000 },
+ { 0107, 0000 },
+ { 0108, 0000 },
+ { 0109, 0000 },
+ { 0110, 0000 },
{ 0111, 0537 }, // Prism Scale
{ 0112, 0325 }, // Reaper Cloth
{ 0113, 0252 }, // Upgrade
@@ -375,5 +391,8 @@ public static class Plib9
{ 0117, 0321 }, // Protector
{ 0118, 0235 }, // Dragon Scale
{ 0119, 2482 }, // Metal Alloy
+ { 0121, 0847 }, // Zygarde Cube
+ { 0120, 0000 },
+ { 1691, 1691 }, // Black Augurite -- yeah, they gave up having unique indexes
};
}
diff --git a/FlatBuffers/ZA/Personal/Personal/Wrapper/PersonalInfo9ZA.cs b/FlatBuffers/ZA/Personal/Personal/Wrapper/PersonalInfo9ZA.cs
index 687eeb03..01fdfbec 100644
--- a/FlatBuffers/ZA/Personal/Personal/Wrapper/PersonalInfo9ZA.cs
+++ b/FlatBuffers/ZA/Personal/Personal/Wrapper/PersonalInfo9ZA.cs
@@ -53,10 +53,27 @@ public int Gender
public ushort Form { get => FB.Info.Form; set => FB.Info.Form = value; }
- public ushort DexIndex
+ public int DexGroup
{
- get => FB.Dex;
- set => FB.Dex = value;
+ get => FB.Dex?.Group ?? 0;
+ set
+ {
+ if (value == 0)
+ FB.Dex = null;
+ else
+ (FB.Dex ??= new PersonalInfoDex()).Group = (byte)value;
+ }
+ }
+ public int DexIndex
+ {
+ get => FB.Dex?.Index ?? 0;
+ set
+ {
+ if (value == 0)
+ FB.Dex = null;
+ else
+ (FB.Dex ??= new PersonalInfoDex()).Index = (byte)value;
+ }
}
public int Item1 { get => 0; set { } }
@@ -98,7 +115,7 @@ public void Write(BinaryWriter bw)
bw.Write(FB.Info.Color);
bw.Write(FB.IsPresentInGame);
- bw.Write((byte)0);
+ bw.Write((byte)DexGroup);
bw.Write((ushort)DexIndex);
bw.Write(FB.Info.Height);
bw.Write(FB.Info.Weight);
@@ -144,5 +161,12 @@ private static ushort GetEXP(int bst, byte evoStage, short add)
404, 369, 417, 430, 164, 528, 231, 191, 390, 399,
174, 605, 200, 018, 269, 056, 377, 127, 118, 441,
527, 411, 526, 394, 059, 087, 370,
+ // ZA DLC
+ 004, 263, 886,
+ 047, 491, 490, 488, 885, 006, 318, 325, 466, 246,
+ 259, 206, 305, 706, 102, 443, 138, 402, 509, 451,
+ 409, 458, 299, 814, 530, 815, 480, 524, 207, 330,
+ 252, 660, 799, 813, 013, 130, 161, 503, 333, 410,
+ 080, 669, 143, 090, 329, 800, 796, 307, 308, 338,
];
}
diff --git a/FlatBuffers/ZA/Personal/Schemas/PersonalTable.fbs b/FlatBuffers/ZA/Personal/Schemas/PersonalTable.fbs
index 48d43bb9..f915e087 100644
--- a/FlatBuffers/ZA/Personal/Schemas/PersonalTable.fbs
+++ b/FlatBuffers/ZA/Personal/Schemas/PersonalTable.fbs
@@ -11,10 +11,8 @@ attribute "fs_serializer";
table PersonalInfo {
Info:PersonalInfoDetail (required);
IsPresentInGame:bool;
- Dex:ushort; // No need to group. Changed from SV.
- // Dex:PersonalInfoDex; // not required
- // KitakamiDex:ushort; // NOT USED IN DATA
- // BlueberryDex:ushort; // NOT USED IN DATA
+ // Dex:ushort; // base game was just a ushort; extended to 2+1 struct in Mega Dimension DLC
+ Dex:PersonalInfoDex; // not required
Type1:ubyte;
Type2:ubyte;
Ability1:ushort;
@@ -40,6 +38,11 @@ table PersonalInfo {
Learnset:[PersonalInfoMove] (required);
}
+struct PersonalInfoDex { // give me a class, not struct.
+ Index:ushort;
+ Group:ubyte;
+}
+
table PersonalTable (fs_serializer) {
Table:[PersonalInfo] (required);
}
diff --git a/FlatBuffers/ZA/Shared/Schemas/PokeData/PokeDataBattle.fbs b/FlatBuffers/ZA/Shared/Schemas/PokeData/PokeDataBattle.fbs
index 03c22de0..4cadd484 100644
--- a/FlatBuffers/ZA/Shared/Schemas/PokeData/PokeDataBattle.fbs
+++ b/FlatBuffers/ZA/Shared/Schemas/PokeData/PokeDataBattle.fbs
@@ -31,4 +31,5 @@ table PokeDataBattle {
EffortValue:ParamSet (required);
RareType:RareType;
ScaleValue:short;
+ IsOybn:bool;
}
diff --git a/FlatBuffers/ZA/Shared/Schemas/Shared/ItemID.fbs b/FlatBuffers/ZA/Shared/Schemas/Shared/ItemID.fbs
index 7a1affed..9e02f2d1 100644
--- a/FlatBuffers/ZA/Shared/Schemas/Shared/ItemID.fbs
+++ b/FlatBuffers/ZA/Shared/Schemas/Shared/ItemID.fbs
@@ -68,6 +68,10 @@ enum ItemID : int
ITEMID_HIKARINOISI = 107,
ITEMID_YAMINOISI = 108,
ITEMID_MEZAMEISI = 109,
+ ITEMID_AKUAKASETTO = 116,
+ ITEMID_INAZUMAKASETTO = 117,
+ ITEMID_BUREIZUKASETTO = 118,
+ ITEMID_HURIIZUKASETTO = 119,
ITEMID_KURABONOMI = 149,
ITEMID_KAGONOMI = 150,
ITEMID_MOMONNOMI = 151,
@@ -145,7 +149,10 @@ enum ItemID : int
ITEMID_MOKUTAN = 249,
ITEMID_RYUUNOKIBA = 250,
ITEMID_SIRUKUNOSUKAAHU = 251,
+ ITEMID_APPUGUREEDO = 252,
ITEMID_KAIGARANOSUZU = 253,
+ ITEMID_HUTOIHONE = 258,
+ ITEMID_NAGANEGI = 259,
ITEMID_KOUKAKURENZU = 265,
ITEMID_TIKARANOHATIMAKI = 266,
ITEMID_MONOSIRIMEGANE = 267,
@@ -174,6 +181,7 @@ enum ItemID : int
ITEMID_KIREINANUKEGARA = 295,
ITEMID_OOKINANEKKO = 296,
ITEMID_KODAWARIMEGANE = 297,
+ ITEMID_AYASIIPATTI = 324,
ITEMID_WAZAMASIN01 = 328,
ITEMID_WAZAMASIN02 = 329,
ITEMID_WAZAMASIN03 = 330,
@@ -274,6 +282,9 @@ enum ItemID : int
ITEMID_HURENDOBOORU = 497,
ITEMID_MUUNBOORU = 498,
ITEMID_KONPEBOORU = 499,
+ ITEMID_BENIIRONOTAMA = 534,
+ ITEMID_AIIRONOTAMA = 535,
+ ITEMID_KIREINAUROKO = 537,
ITEMID_SINKANOKISEKI = 538,
ITEMID_KARUISI = 539,
ITEMID_GOTUGOTUMETTO = 540,
@@ -408,16 +419,20 @@ enum ItemID : int
ITEMID_YOUKIMINTO = 1249,
ITEMID_MUJYAKIMINTO = 1250,
ITEMID_MAZIMEMINTO = 1251,
+ ITEMID_ROTOMUNOKATAROGU = 1278,
ITEMID_GARANATUBURESU = 1582,
ITEMID_AKASINOOMAMORI = 1589,
ITEMID_GARANATURIISU = 1592,
ITEMID_TOKUSEIPATTI = 1606,
+ ITEMID_KURONOKISEKI = 1691,
+ ITEMID_NOROINOYOROI = 1861,
ITEMID_SENTOUBAFFA1 = 1881,
ITEMID_SENTOUBAFFA2 = 1882,
ITEMID_SENTOUBAFFA3 = 1883,
ITEMID_SENTOUBAFFA4 = 1884,
ITEMID_SENTOUBAFFA5 = 1885,
ITEMID_SENTOUBAFFA6 = 1886,
+ ITEMID_SOZAI182 = 2137,
ITEMID_WAZAMASIN100 = 2160,
ITEMID_WAZAMASIN101 = 2161,
ITEMID_WAZAMASIN102 = 2162,
@@ -490,6 +505,7 @@ enum ItemID : int
ITEMID_WAZAMASIN169 = 2229,
ITEMID_WAZAMASIN170 = 2230,
ITEMID_WAZAMASIN171 = 2231,
+ ITEMID_IWAINOYOROI = 2344,
ITEMID_YOUSEINOHANE = 2401,
ITEMID_KAIDENNOTANE = 2558,
ITEMID_PIKUSINAITO = 2559,
@@ -534,19 +550,19 @@ enum ItemID : int
ITEMID_OISHIIGOMI = 2598,
ITEMID_GENKINOKOEDA = 2599,
ITEMID_DEURONOWASUREMONO = 2600,
- ITEMID_DAIZINAMONO14 = 2601,
- ITEMID_DAIZINAMONO15 = 2602,
- ITEMID_DAIZINAMONO16 = 2603,
- ITEMID_DAIZINAMONO17 = 2604,
- ITEMID_DAIZINAMONO18 = 2605,
- ITEMID_DAIZINAMONO19 = 2606,
- ITEMID_DAIZINAMONO20 = 2607,
- ITEMID_DAIZINAMONO21 = 2608,
- ITEMID_DAIZINAMONO22 = 2609,
- ITEMID_DAIZINAMONO23 = 2610,
- ITEMID_DAIZINAMONO24 = 2611,
- ITEMID_DAIZINAMONO25 = 2612,
- ITEMID_DAIZINAMONO26 = 2613,
+ ITEMID_BEIKINGUPAUDAA = 2601,
+ ITEMID_IJIGENPAUDAA1 = 2602,
+ ITEMID_IJIGENPAUDAA2 = 2603,
+ ITEMID_IJIGENPAUDAA3 = 2604,
+ ITEMID_IJIGENPAUDAA4 = 2605,
+ ITEMID_IJIGENPAUDAA5 = 2606,
+ ITEMID_HOUENNOARAJIO = 2607,
+ ITEMID_SUPAISUBURAKKU = 2608,
+ ITEMID_ORIIBUAOIRU = 2609,
+ ITEMID_PACHIPACHIKYANDII = 2610,
+ ITEMID_TAISETSUNATEGAMI = 2611,
+ ITEMID_OMOIDENOYUBIWA2 = 2612,
+ ITEMID_YOGORETASUKAAHU = 2613,
ITEMID_DAIZINAMONO27 = 2614,
ITEMID_DAIZINAMONO28 = 2615,
ITEMID_DAIZINAMONO29 = 2616,
@@ -568,4 +584,54 @@ enum ItemID : int
ITEMID_AONOKANARYINUI1 = 2632,
ITEMID_AONOKANARYINUI2 = 2633,
ITEMID_AONOKANARYINUI3 = 2634,
+ ITEMID_RAITYUUNAITOx = 2635,
+ ITEMID_RAITYUUNAITOy = 2636,
+ ITEMID_TIRIINNAITO = 2637,
+ ITEMID_ABUSORUNAITOz = 2638,
+ ITEMID_MUKUHOOKUNAITO = 2639,
+ ITEMID_GABURIASUNAITOz = 2640,
+ ITEMID_RUKARIONAITOz = 2641,
+ ITEMID_GORUUGUNAITO = 2642,
+ ITEMID_NYAONIKUSUNAITO = 2643,
+ ITEMID_KEKENKANINAITO = 2644,
+ ITEMID_GUSOKUMUSYANAITO = 2645,
+ ITEMID_MAGIANAITO = 2646,
+ ITEMID_SUKOBUIRANAITO = 2647,
+ ITEMID_SEGUREIBUNAITO = 2648,
+ ITEMID_SHARITATSUNAITO = 2649,
+ ITEMID_KIRAHURORUNAITO = 2650,
+ ITEMID_IJIGENKURABONOMI = 2651,
+ ITEMID_IJIGENKAGONOMI = 2652,
+ ITEMID_IJIGENMOMONNOMI = 2653,
+ ITEMID_IJIGENTIIGONOMI = 2654,
+ ITEMID_IJIGENNANASINOMI = 2655,
+ ITEMID_IJIGENORENNOMI = 2656,
+ ITEMID_IJIGENKIINOMI = 2657,
+ ITEMID_IJIGENRAMUNOMI = 2658,
+ ITEMID_IJIGENOBONNOMI = 2659,
+ ITEMID_IJIGENZAROKUNOMI = 2660,
+ ITEMID_IJIGENNEKOBUNOMI = 2661,
+ ITEMID_IJIGENTAPORUNOMI = 2662,
+ ITEMID_IJIGENROMENOMI = 2663,
+ ITEMID_IJIGENUBUNOMI = 2664,
+ ITEMID_IJIGENMATOMANOMI = 2665,
+ ITEMID_IJIGENOKKANOMI = 2666,
+ ITEMID_IJIGENITOKENOMI = 2667,
+ ITEMID_IJIGENSOKUNONOMI = 2668,
+ ITEMID_IJIGENRINDONOMI = 2669,
+ ITEMID_IJIGENYATHENOMI = 2670,
+ ITEMID_IJIGENYOPUNOMI = 2671,
+ ITEMID_IJIGENBIAANOMI = 2672,
+ ITEMID_IJIGENSYUKANOMI = 2673,
+ ITEMID_IJIGENBAKOUNOMI = 2674,
+ ITEMID_IJIGENUTANNOMI = 2675,
+ ITEMID_IJIGENTANGANOMI = 2676,
+ ITEMID_IJIGENYOROGINOMI = 2677,
+ ITEMID_IJIGENKASIBUNOMI = 2678,
+ ITEMID_IJIGENHABANNOMI = 2679,
+ ITEMID_IJIGENNAMONOMI = 2680,
+ ITEMID_IJIGENRIRIBANOMI = 2681,
+ ITEMID_IJIGENHOZUNOMI = 2682,
+ ITEMID_IJIGENROZERUNOMI = 2683,
+ ITEMID_KANARIIPAN = 2684,
}
diff --git a/FlatBuffers/ZA/Trainers/Schemas/TrainerBattleDataGlobal.fbs b/FlatBuffers/ZA/Trainers/Schemas/TrainerBattleDataGlobal.fbs
index 657cf3c9..01e970b7 100644
--- a/FlatBuffers/ZA/Trainers/Schemas/TrainerBattleDataGlobal.fbs
+++ b/FlatBuffers/ZA/Trainers/Schemas/TrainerBattleDataGlobal.fbs
@@ -10,10 +10,10 @@ table TrainerBattleDetail {
table TrainerBattleData {
Type:string (required);
- TrainerBattleID:string;
- NPCAssetID:string;
- Field03:string;
- Field04:string;
+ TrainerBattleID1:string;
+ NPCAssetID1:string;
+ TrainerBattleID2:string;
+ NPCAssetID2:string;
Field05:string;
Field06:string;
Field07:string;
@@ -28,7 +28,8 @@ table TrainerBattleData {
Field16:string;
Field17:string;
Field18:string;
- Field19:[TrainerBattleDetail] (required);
+ Field19:string; // one field was inserted in 2.0.0, not sure which but it was after Field13
+ Field20:[TrainerBattleDetail] (required);
}
table TrainerBattleGlobalDBArray (fs_serializer) {
diff --git a/FlatBuffers/ZA/World/Schemas/Area/FieldAreaInfo.fbs b/FlatBuffers/ZA/World/Schemas/Area/FieldAreaInfo.fbs
index b7c4db47..6631e234 100644
--- a/FlatBuffers/ZA/World/Schemas/Area/FieldAreaInfo.fbs
+++ b/FlatBuffers/ZA/World/Schemas/Area/FieldAreaInfo.fbs
@@ -17,6 +17,7 @@ table FieldAreaInfo {
ProhibitReadyBallThrow:bool;
ProhibitPartnerPutout:bool;
IsOptimizeWaterLight:bool;
+ CanEvolveToPoke0867:bool; // Runerigus, inserted in 2.0.0
DefaultStartPosition:PackedVec3f;
DefaultStartYaw:float;
Music:string;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionBossLotteryDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionBossLotteryDBArray.fbs
new file mode 100644
index 00000000..50baa461
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionBossLotteryDBArray.fbs
@@ -0,0 +1,33 @@
+include "Shared/ActivationCondition.fbs";
+include "Shared/ActivationConditionElement.fbs";
+include "Shared/ActivationConditionParam.fbs";
+
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionBossLottery {
+ BossLotteryId:string;
+ BattleId:string;
+ Rank:int;
+ EstimateLevel:int;
+ ExtensionLevel:int;
+ IsRematch:bool;
+ DevNo:int;
+ FormNo:int;
+ GateObjectType:int;
+ GateSpawnerId:string;
+ StartPointObjectName:string;
+ Condition:[ActivationCondition];
+ ReturnGateCondition:[ActivationCondition];
+}
+
+table DimensionBossLotteryDB {
+ Table:[DimensionBossLottery] (required);
+}
+
+table DimensionBossLotteryDBArray (fs_serializer) {
+ Table:[DimensionBossLotteryDB] (required);
+}
+
+root_type DimensionBossLotteryDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionConfigDB.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionConfigDB.fbs
new file mode 100644
index 00000000..c272280a
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionConfigDB.fbs
@@ -0,0 +1,18 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionConfig {
+ SpecialLotteryForceWinChainCount:int;
+ GateObjectStreamingActivateDistance:float;
+ GateObjectStreamingDeactivateDistance:float;
+ DummyZoneAreaId:string;
+ DummyZoneExtensionLevel:int;
+ BonusLotteryRandomThresholdDistance:float;
+}
+
+table DimensionConfigDB (fs_serializer) {
+ Table:DimensionConfig (required);
+}
+
+root_type DimensionConfigDB;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGateObjectDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGateObjectDBArray.fbs
new file mode 100644
index 00000000..39e5f839
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGateObjectDBArray.fbs
@@ -0,0 +1,18 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionGateObject {
+ GateObjectType:int;
+ ObjectTemplatePath:string;
+}
+
+table DimensionGateObjectDB {
+ Table:[DimensionGateObject] (required);
+}
+
+table DimensionGateObjectDBArray (fs_serializer) {
+ Table:[DimensionGateObjectDB] (required);
+}
+
+root_type DimensionGateObjectDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGateSpawnerDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGateSpawnerDBArray.fbs
new file mode 100644
index 00000000..55470974
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGateSpawnerDBArray.fbs
@@ -0,0 +1,23 @@
+include "Shared/ActivationCondition.fbs";
+include "Shared/ActivationConditionElement.fbs";
+include "Shared/ActivationConditionParam.fbs";
+
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionGateSpawner {
+ GateSpawnerId:string;
+ IsDynamicCondition:bool;
+ Condition:[ActivationCondition];
+}
+
+table DimensionGateSpawnerDB {
+ Table:[DimensionGateSpawner] (required);
+}
+
+table DimensionGateSpawnerDBArray (fs_serializer) {
+ Table:[DimensionGateSpawnerDB] (required);
+}
+
+root_type DimensionGateSpawnerDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGimmickItemDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGimmickItemDBArray.fbs
new file mode 100644
index 00000000..c5c66c62
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionGimmickItemDBArray.fbs
@@ -0,0 +1,23 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table RankItem {
+ Rank:int;
+ ItemTableId:string (required);
+}
+
+table DimensionGimmickItem {
+ GimmickId:string;
+ RankItems:[RankItem] (required);
+}
+
+table DimensionGimmickItemDB {
+ Table:[DimensionGimmickItem] (required);
+}
+
+table DimensionGimmickItemDBArray (fs_serializer) {
+ Table:[DimensionGimmickItemDB] (required);
+}
+
+root_type DimensionGimmickItemDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionMapDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionMapDBArray.fbs
new file mode 100644
index 00000000..709d3912
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionMapDBArray.fbs
@@ -0,0 +1,26 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table StartPointItem {
+ ObjectName:string (required);
+}
+
+table DimensionMap {
+ MapId:string;
+ MapSize:int;
+ TaskRandomPopItemLimit:int;
+ WildTypeCount:int;
+ BattleTypeCount:int;
+ StartPointItems:[StartPointItem] (required);
+}
+
+table DimensionMapDB {
+ Table:[DimensionMap] (required);
+}
+
+table DimensionMapDBArray (fs_serializer) {
+ Table:[DimensionMapDB] (required);
+}
+
+root_type DimensionMapDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionProgressDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionProgressDBArray.fbs
new file mode 100644
index 00000000..376c886e
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionProgressDBArray.fbs
@@ -0,0 +1,40 @@
+include "Shared/ActivationCondition.fbs";
+include "Shared/ActivationConditionElement.fbs";
+include "Shared/ActivationConditionParam.fbs";
+
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionProgressRankItem {
+ Rank:int;
+ LotteryWeight:int;
+ MinCount:int;
+ MaxCount:int;
+}
+
+table DimensionProgress {
+ ProgressId:string;
+ WildMinCount:int;
+ WildMaxCount:int;
+ BattleMinCount:int;
+ BattleMaxCount:int;
+ EventMinCount:int;
+ EventMaxCount:int;
+ AreaGateMinCount:int;
+ AreaGateMaxCount:int;
+ TotalGateMinCount:int;
+ TotalGateMaxCount:int;
+ RankItems:[DimensionProgressRankItem] (required);
+ Condition:[ActivationCondition];
+}
+
+table DimensionProgressDB {
+ Table:[DimensionProgress] (required);
+}
+
+table DimensionProgressDBArray (fs_serializer) {
+ Table:[DimensionProgressDB] (required);
+}
+
+root_type DimensionProgressDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionRankDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionRankDBArray.fbs
new file mode 100644
index 00000000..b97f1839
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionRankDBArray.fbs
@@ -0,0 +1,32 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table PokeTypeItem {
+ PokeType:int;
+ LotteryWeight:int;
+}
+
+table MapItem {
+ MapId:string;
+ LotteryWeight:int;
+}
+
+table DimensionRank {
+ Rank:int;
+ ExtensionLevel:int;
+ EstimateLevel:int;
+ LimitReductionSpeed:float;
+ MapItems:[MapItem] (required);
+ BattlePokeTypeItems:[PokeTypeItem] (required);
+}
+
+table DimensionRankDB {
+ Table:[DimensionRank] (required);
+}
+
+table DimensionRankDBArray (fs_serializer) {
+ Table:[DimensionRankDB] (required);
+}
+
+root_type DimensionRankDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionSpecialLotteryDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionSpecialLotteryDBArray.fbs
new file mode 100644
index 00000000..5ae54790
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionSpecialLotteryDBArray.fbs
@@ -0,0 +1,23 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionSpecialLottery {
+ SpecialLotteryId:string (required);
+ Rank:int;
+ ZoneType:int;
+ MapId:string (required);
+ PatternSceneName:string; // not required
+ LotteryTableType:int;
+ LotteryWeight:int;
+}
+
+table DimensionSpecialLotteryDB {
+ Table:[DimensionSpecialLottery] (required);
+}
+
+table DimensionSpecialLotteryDBArray (fs_serializer) {
+ Table:[DimensionSpecialLotteryDB] (required);
+}
+
+root_type DimensionSpecialLotteryDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionUniqueLotteryDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionUniqueLotteryDBArray.fbs
new file mode 100644
index 00000000..a03dde7c
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionUniqueLotteryDBArray.fbs
@@ -0,0 +1,36 @@
+include "Shared/ActivationCondition.fbs";
+include "Shared/ActivationConditionElement.fbs";
+include "Shared/ActivationConditionParam.fbs";
+
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionUniqueLottery {
+ UniqueLotteryId:string;
+ UseLimit:bool;
+ Rank:int;
+ EstimateLevel:int;
+ MapId:string;
+ StartPointObjectName:string;
+ PatternSceneName:string;
+ ZoneType:int;
+ WildPokeType_0:int;
+ WildPokePatternNo_0:int;
+ WildPokeType_1:int;
+ WildPokePatternNo_1:int;
+ WildPokeType_2:int;
+ WildPokePatternNo_2:int;
+ GateSpawnerId:string;
+ Condition:[ActivationCondition];
+}
+
+table DimensionUniqueLotteryDB {
+ Table:[DimensionUniqueLottery] (required);
+}
+
+table DimensionUniqueLotteryDBArray (fs_serializer) {
+ Table:[DimensionUniqueLotteryDB] (required);
+}
+
+root_type DimensionUniqueLotteryDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionWildPokemonDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionWildPokemonDBArray.fbs
new file mode 100644
index 00000000..ac6208ce
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionWildPokemonDBArray.fbs
@@ -0,0 +1,34 @@
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionWildSpawnerItem {
+ SpawnerDataId:string (required);
+ LotteryWeight:int;
+}
+
+table DimensionWildPatternItem {
+ PatternNo:int;
+ LotteryWeight:int;
+ SpawnerItems:[DimensionWildSpawnerItem];
+}
+
+table DimensionWildRankItem {
+ Rank:int;
+ PatternItems:[DimensionWildPatternItem];
+}
+
+table DimensionWildPokemon {
+ PokeType:int;
+ RankItems:[DimensionWildRankItem] (required);
+}
+
+table DimensionWildPokemonDB {
+ Table:[DimensionWildPokemon] (required);
+}
+
+table DimensionWildPokemonDBArray (fs_serializer) {
+ Table:[DimensionWildPokemonDB] (required);
+}
+
+root_type DimensionWildPokemonDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Dimension/DimensionZoneLotteryDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionZoneLotteryDBArray.fbs
new file mode 100644
index 00000000..6e26e29b
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Dimension/DimensionZoneLotteryDBArray.fbs
@@ -0,0 +1,40 @@
+include "Shared/ActivationCondition.fbs";
+include "Shared/ActivationConditionElement.fbs";
+include "Shared/ActivationConditionParam.fbs";
+
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table DimensionZoneEventItem {
+ EventId:string;
+ EventKind:int;
+ LotteryWeight:int;
+ EstimateLevel:int;
+ Condition:[ActivationCondition];
+}
+
+table DimensionZoneRankItem {
+ Rank:int;
+ WildLotteryWeight:int;
+ BattleLotteryWeight:int;
+ EventLotteryWeight:int;
+ WildSceneNames:[string];
+ BattleSceneNames:[string];
+ EventItems:[DimensionZoneEventItem];
+}
+
+table DimensionZoneLottery {
+ MapId:string;
+ RankItems:[DimensionZoneRankItem] (required);
+}
+
+table DimensionZoneLotteryDB {
+ Table:[DimensionZoneLottery] (required);
+}
+
+table DimensionZoneLotteryDBArray (fs_serializer) {
+ Table:[DimensionZoneLotteryDB] (required);
+}
+
+root_type DimensionZoneLotteryDBArray;
diff --git a/FlatBuffers/ZA/World/Schemas/Encount/WazagimmickItemSpawnerDB.fbs b/FlatBuffers/ZA/World/Schemas/Encount/WazagimmickItemSpawnerDB.fbs
new file mode 100644
index 00000000..ea10637f
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Encount/WazagimmickItemSpawnerDB.fbs
@@ -0,0 +1,26 @@
+include "Shared/ActivationCondition.fbs";
+include "Shared/ActivationConditionElement.fbs";
+include "Shared/ActivationConditionParam.fbs";
+
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table ItemTableInfo {
+ TableId:string;
+ Condition:[ActivationCondition];
+}
+
+table WazagimmickItemSpawner {
+ Id:string;
+ ObjectNameList:[string] (required);
+ MinCount:int;
+ MaxCount:int;
+ ItemTableInfoList:[ItemTableInfo] (required);
+}
+
+table WazagimmickItemSpawnerDB (fs_serializer) {
+ Table:[WazagimmickItemSpawner] (required);
+}
+
+root_type WazagimmickItemSpawnerDB;
diff --git a/FlatBuffers/ZA/World/Schemas/Encount/WazagimmickItemTableDBArray.fbs b/FlatBuffers/ZA/World/Schemas/Encount/WazagimmickItemTableDBArray.fbs
new file mode 100644
index 00000000..90322665
--- /dev/null
+++ b/FlatBuffers/ZA/World/Schemas/Encount/WazagimmickItemTableDBArray.fbs
@@ -0,0 +1,30 @@
+include "Shared/ActivationCondition.fbs";
+include "Shared/ActivationConditionElement.fbs";
+include "Shared/ActivationConditionParam.fbs";
+
+namespace pkNX.Structures.FlatBuffers.ZA;
+
+attribute "fs_serializer";
+
+table ItemTableItemLotteryData {
+ ItemId:string (required);
+ Weight:int;
+ MaxCount:int;
+ Type:int;
+ Condition:[ActivationCondition];
+}
+
+table WazagimmickItemTable {
+ Id:string (required);
+ ItemLotteryDataList:[ItemTableItemLotteryData] (required);
+}
+
+table WazagimmickItemTableDB {
+ Table:[WazagimmickItemTable] (required);
+}
+
+table WazagimmickItemTableDBArray (fs_serializer) {
+ Table:[WazagimmickItemTableDB] (required);
+}
+
+root_type WazagimmickItemTableDBArray;
diff --git a/README.md b/README.md
index 45e3b712..b27bd75b 100644
--- a/README.md
+++ b/README.md
@@ -23,10 +23,12 @@ The following games are supported:
* Sword / Shield
* Legends: Arceus
* Scarlet / Violet **(Only for dumping data, not to edit files!)**
+* Legends: Z-A **(Only for dumping data, not to edit files!)**
pkNX operates under the assumption that your dumped ROM includes the latest available update data for the following games:
* Sword / Shield (Ver. 1.3.2)
-* Scarlet / Violet (Ver. 3.0.1)
+* Scarlet / Violet (Ver. 4.0.0)
+* Legends: Z-A (Ver. 2.0.0)
## Features
diff --git a/pkNX.Game/GameLocation.cs b/pkNX.Game/GameLocation.cs
index ff6ec339..ae47ca9d 100644
--- a/pkNX.Game/GameLocation.cs
+++ b/pkNX.Game/GameLocation.cs
@@ -90,6 +90,7 @@ private static GameVersion GetGameFromPath(string romfs, string? exefs)
private const int FILECOUNT_SV_300 = 30; // Ver. 3.0.0 (Indigo Disk)
private const int FILECOUNT_ZA_100 = 22; // Ver. 1.0.0
private const int FILECOUNT_ZA_102 = 21; // Ver. 1.0.2
+ private const int FILECOUNT_ZA_200 = 22; // Ver. 2.0.0 (Mega Dimension)
private static ulong GetTitleID(string? exefs)
{
@@ -114,7 +115,7 @@ private static ulong GetTitleID(string? exefs)
FILECOUNT_SV or FILECOUNT_SV_101 or FILECOUNT_SV_120 or FILECOUNT_SV_130 or FILECOUNT_SV_201 or FILECOUNT_SV_300
=> DetectGen9(exefs),
- FILECOUNT_ZA_100 or FILECOUNT_ZA_102
+ FILECOUNT_ZA_100 or FILECOUNT_ZA_102 or FILECOUNT_ZA_200
=> GameVersion.ZA,
_ => GameVersion.Invalid
diff --git a/pkNX.Structures/Evolution/EvolutionType.cs b/pkNX.Structures/Evolution/EvolutionType.cs
index 6979ef84..8fae1479 100644
--- a/pkNX.Structures/Evolution/EvolutionType.cs
+++ b/pkNX.Structures/Evolution/EvolutionType.cs
@@ -73,6 +73,8 @@ public enum EvolutionType : byte
Hisui = 61,
+ UseMoveBarbBarrage = 62, // ZA
+
UseItemFullMoon = 90, // Ursaluna
UseMoveAgileStyle = 91, // Wyrdeer
UseMoveStrongStyle = 92, // Overqwil
@@ -158,6 +160,7 @@ public static class EvolutionTypeExtensions
LevelUpRecoilDamageMale => true,
LevelUpRecoilDamageFemale => true,
Hisui => false,
+ UseMoveBarbBarrage => false,
UseItemFullMoon => false,
UseMoveAgileStyle => false,
@@ -237,6 +240,7 @@ public static class EvolutionTypeExtensions
[LevelUpKnowMoveECElse] = Moves, // Dudunsparce 0
[LevelUpKnowMoveEC25] = Moves, // Dudunsparce 1
[Hisui] = NoArg,
+ [UseMoveBarbBarrage] = NoArg,
};
public static EvolutionTypeArgumentType GetArgType(this EvolutionType t) => ArgType[t];
diff --git a/pkNX.WinForms/Dumping/Gen9a/Dumper9a.Designer.cs b/pkNX.WinForms/Dumping/Gen9a/Dumper9a.Designer.cs
index f35cb7e8..132ab9d3 100644
--- a/pkNX.WinForms/Dumping/Gen9a/Dumper9a.Designer.cs
+++ b/pkNX.WinForms/Dumping/Gen9a/Dumper9a.Designer.cs
@@ -51,13 +51,15 @@ private void InitializeComponent()
B_Misc = new System.Windows.Forms.Button();
B_Scrubbed = new System.Windows.Forms.Button();
B_DumpTriggers = new System.Windows.Forms.Button();
+ B_DumpConfig = new System.Windows.Forms.Button();
Tab_PKHeX = new System.Windows.Forms.TabPage();
flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel();
B_Locations = new System.Windows.Forms.Button();
+ B_DonutText = new System.Windows.Forms.Button();
Tab_Future = new System.Windows.Forms.TabPage();
flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel();
B_OpenFolder = new System.Windows.Forms.Button();
- B_DumpConfig = new System.Windows.Forms.Button();
+ B_Dimension = new System.Windows.Forms.Button();
TC_Options.SuspendLayout();
Tab_General.SuspendLayout();
flowLayoutPanel1.SuspendLayout();
@@ -235,6 +237,7 @@ private void InitializeComponent()
flowLayoutPanel3.Controls.Add(B_Scrubbed);
flowLayoutPanel3.Controls.Add(B_DumpTriggers);
flowLayoutPanel3.Controls.Add(B_DumpConfig);
+ flowLayoutPanel3.Controls.Add(B_Dimension);
flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill;
flowLayoutPanel3.Location = new System.Drawing.Point(4, 3);
flowLayoutPanel3.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
@@ -341,6 +344,17 @@ private void InitializeComponent()
B_DumpTriggers.UseVisualStyleBackColor = true;
B_DumpTriggers.Click += B_Triggers_Click;
//
+ // B_DumpConfig
+ //
+ B_DumpConfig.Location = new System.Drawing.Point(4, 234);
+ B_DumpConfig.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ B_DumpConfig.Name = "B_DumpConfig";
+ B_DumpConfig.Size = new System.Drawing.Size(144, 71);
+ B_DumpConfig.TabIndex = 28;
+ B_DumpConfig.Text = "Config";
+ B_DumpConfig.UseVisualStyleBackColor = true;
+ B_DumpConfig.Click += B_DumpConfig_Click;
+ //
// Tab_PKHeX
//
Tab_PKHeX.Controls.Add(flowLayoutPanel2);
@@ -355,6 +369,7 @@ private void InitializeComponent()
// flowLayoutPanel2
//
flowLayoutPanel2.Controls.Add(B_Locations);
+ flowLayoutPanel2.Controls.Add(B_DonutText);
flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
flowLayoutPanel2.Location = new System.Drawing.Point(3, 3);
flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
@@ -373,6 +388,17 @@ private void InitializeComponent()
B_Locations.UseVisualStyleBackColor = true;
B_Locations.Click += B_Locations_Click;
//
+ // B_DonutText
+ //
+ B_DonutText.Location = new System.Drawing.Point(156, 3);
+ B_DonutText.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ B_DonutText.Name = "B_DonutText";
+ B_DonutText.Size = new System.Drawing.Size(144, 71);
+ B_DonutText.TabIndex = 22;
+ B_DonutText.Text = "Donut Text";
+ B_DonutText.UseVisualStyleBackColor = true;
+ B_DonutText.Click += B_DonutLocalization_Click;
+ //
// Tab_Future
//
Tab_Future.Controls.Add(flowLayoutPanel4);
@@ -405,16 +431,16 @@ private void InitializeComponent()
B_OpenFolder.UseVisualStyleBackColor = true;
B_OpenFolder.Click += B_OpenFolder_Click;
//
- // B_DumpConfig
+ // B_Dimension
//
- B_DumpConfig.Location = new System.Drawing.Point(4, 234);
- B_DumpConfig.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
- B_DumpConfig.Name = "B_DumpConfig";
- B_DumpConfig.Size = new System.Drawing.Size(144, 71);
- B_DumpConfig.TabIndex = 28;
- B_DumpConfig.Text = "Config";
- B_DumpConfig.UseVisualStyleBackColor = true;
- B_DumpConfig.Click += B_DumpConfig_Click;
+ B_Dimension.Location = new System.Drawing.Point(156, 234);
+ B_Dimension.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ B_Dimension.Name = "B_Dimension";
+ B_Dimension.Size = new System.Drawing.Size(144, 71);
+ B_Dimension.TabIndex = 29;
+ B_Dimension.Text = "Dimension";
+ B_Dimension.UseVisualStyleBackColor = true;
+ B_Dimension.Click += B_DumpDimension_Click;
//
// Dumper9a
//
@@ -471,5 +497,7 @@ private void InitializeComponent()
private System.Windows.Forms.Button B_Scrubbed;
private System.Windows.Forms.Button B_DumpTriggers;
private System.Windows.Forms.Button B_DumpConfig;
+ private System.Windows.Forms.Button B_DonutText;
+ private System.Windows.Forms.Button B_Dimension;
}
}
diff --git a/pkNX.WinForms/Dumping/Gen9a/Dumper9a.cs b/pkNX.WinForms/Dumping/Gen9a/Dumper9a.cs
index 46556b15..50e2dec9 100644
--- a/pkNX.WinForms/Dumping/Gen9a/Dumper9a.cs
+++ b/pkNX.WinForms/Dumping/Gen9a/Dumper9a.cs
@@ -99,6 +99,12 @@ private void B_DumpMisc_Click(object sender, EventArgs e)
System.Media.SystemSounds.Asterisk.Play();
}
+ private void B_DumpDimension_Click(object sender, EventArgs e)
+ {
+ Dumper.DumpDimension();
+ System.Media.SystemSounds.Asterisk.Play();
+ }
+
private void B_DumpScrubbed_Click(object sender, EventArgs e)
{
Dumper.DumpScrubbedInfo();
@@ -190,4 +196,10 @@ private void B_Triggers_Click(object sender, EventArgs e)
Dumper.DumpEventTriggers();
System.Media.SystemSounds.Asterisk.Play();
}
+
+ private void B_DonutLocalization_Click(object sender, EventArgs e)
+ {
+ Dumper.DumpDonutData();
+ System.Media.SystemSounds.Asterisk.Play();
+ }
}
diff --git a/pkNX.WinForms/Dumping/Gen9a/GameDumper9a.cs b/pkNX.WinForms/Dumping/Gen9a/GameDumper9a.cs
index c5c5ec11..55fcb3e7 100644
--- a/pkNX.WinForms/Dumping/Gen9a/GameDumper9a.cs
+++ b/pkNX.WinForms/Dumping/Gen9a/GameDumper9a.cs
@@ -121,7 +121,7 @@ public void DumpStrings()
{
var textConfig = new TextConfig(GameVersion.ZA);
ReadOnlySpan groups = ["ik_messagedat"];
- ReadOnlySpan types = ["script", "common"];
+ ReadOnlySpan types = ["script", "common", "sk"];
foreach (var group in groups)
{
foreach (var type in types)
@@ -512,6 +512,7 @@ public void DumpArchives()
public void DumpMisc()
{
+ Dump("world/ik_data/battle/plib_item_conversion/plib_item_conversion_array.bfbs", z => z.Table);
Dump("world/ik_data/rank_battle/reward/reward_array.bfbs", z => z.Table);
Dump("world/ik_data/ui/pokedex/black_list_main/black_list_main_array.bfbs", z => z.Table);
@@ -520,6 +521,7 @@ public void DumpMisc()
DumpJson("world/ik_data/capture/capture_zarank_data/capture_zarank_data.bin");
Dump("ik_event/bin/za_reward_item_data/za_reward_item_data.bin", z => z.Table);
+ Dump("world/ik_data/field/npc/npc_asset_data/npc_asset_data/npc_asset_data.bfbs", z => z.Table);
Dump("ik_event/bin/event_trainer_battle_data/trainer_btl_data_global.bin", z => z.Table);
Dump("world/exl/shop/shop_item_lineup/shop_item_lineup.bin", z => z.Table);
Dump("world/ik_data/field/random_pop_item/random_pop_item_table/random_pop_item_table_data/random_pop_item_table_data_array.bfbs", z => z.Table);
@@ -538,6 +540,27 @@ public void DumpMisc()
Dump("world/ik_data/field/weather/weather_happening/weather_happening_array.bin", z => z.Table);
DumpDressUp();
+
+ const string pst = "world/ik_data/field/spawner_transform_data/placement_npc_spawner_transform/placement_npc_spawner_transform/placement_npc_spawner_transform_array.bfbs";
+ var spawnerT = Dump(pst, z => z.Table);
+ for (var i = 0; i < spawnerT.Table.Count; i++)
+ {
+ var table = spawnerT.Table[i];
+ var lines = TableUtil.GetTable(table.Table);
+ var path = GetRawPath(pst);
+ var fileName = Path.ChangeExtension(path, $".{i:00}.txt");
+ File.WriteAllText(fileName, lines);
+ }
+
+ DumpJson("param_ai/data/ai/trigger/condition_rare/condition_rare_array.bin");
+
+ Dump("world/exl/donut/flavor/flavor.bin", z => z.Table);
+ Dump("world/exl/donut/donut_recipe/donut_recipe.bin", z => z.Table);
+ Dump("world/exl/donut/berry/berry.bin", z => z.Table);
+ Dump("world/exl/donut/donut/donut.bin", z => z.Table);
+ Dump("world/exl/donut/flavor_lottery/flavor_lottery.bin", z => z.Table);
+ Dump("world/exl/donut/flavor_parameter/flavor_parameter.bin", z => z.Table);
+ Dump("world/exl/donut/oneword/oneword.bin", z => z.Table);
}
private void DumpDressUp()
@@ -965,10 +988,18 @@ public void DumpPersonal()
var alphaMoves = Dump(alphaMovesPath, z => z.Table)
.Table;
+ // Only acknowledge the first set of entries.
foreach (var alpha in alphaMoves)
{
foreach (var form in alpha.FormTableList)
- pt[SpeciesConverterZA.GetNational9(alpha.DevNo), (byte)form.FormNo].AlphaMove = form.WazaNo;
+ {
+ var pi = pt[SpeciesConverterZA.GetNational9(alpha.DevNo), (byte)form.FormNo];
+ if (pi.AlphaMove == 0)
+ pi.AlphaMove = form.WazaNo;
+ // don't throw an exception if already non-zero -- Raichu's mega forms were incorrectly configured as 1/2 rather than 2/3.
+ // The Alpha Move search returns on first match.
+ // the only clash is Raichu-1, which needs to stay as Iron Tail (not Play Rough).
+ }
}
var bin = pt.Write();
@@ -1000,6 +1031,54 @@ public void DumpPersonal()
RipPersonal(pt, lang);
}
+ public void DumpDimension()
+ {
+ const string dimWild = "world/ik_data/field/dimension/dimension_wild_pokemon/dimension_wild_pokemon/dimension_wild_pokemon_array.bfbs";
+ Dump(dimWild, z => z.Table);
+ DumpSel(dimWild, z => z.Table.SelectMany(x => x.Table), "x");
+
+ const string dimSpecialLottery = "world/ik_data/field/dimension/dimension_special_lottery/dimension_special_lottery/dimension_special_lottery_array.bfbs";
+ Dump(dimSpecialLottery, z => z.Table);
+
+ const string dimItem = "world/ik_data/field/dimension/dimension_gimmick_item/dimension_gimmick_item/dimension_gimmick_item_array.bfbs";
+ Dump(dimItem, z => z.Table);
+
+ const string dimUniqueLottery = "world/ik_data/field/dimension/dimension_unique_lottery/dimension_unique_lottery/dimension_unique_lottery_array.bfbs";
+ Dump(dimUniqueLottery, z => z.Table);
+
+ const string dimZoneLottery = "world/ik_data/field/dimension/dimension_zone_lottery/dimension_zone_lottery/dimension_zone_lottery_array.bfbs";
+ Dump(dimZoneLottery, z => z.Table);
+
+ const string dimBossLottery = "world/ik_data/field/dimension/dimension_boss_lottery/dimension_boss_lottery/dimension_boss_lottery_array.bfbs";
+ Dump(dimBossLottery, z => z.Table);
+
+ const string dimGateSpawner = "world/ik_data/field/dimension/dimension_gate_spawner/dimension_gate_spawner/dimension_gate_spawner_array.bfbs";
+ Dump(dimGateSpawner, z => z.Table);
+
+ const string dimGateArray = "world/ik_data/field/dimension/dimension_gate_object/dimension_gate_object/dimension_gate_object_array.bfbs";
+ Dump(dimGateArray, z => z.Table);
+
+ const string dimProgress = "world/ik_data/field/dimension/dimension_progress/dimension_progress/dimension_progress_array.bfbs";
+ Dump(dimProgress, z => z.Table);
+
+ const string dimMapArray = "world/ik_data/field/dimension/dimension_map/dimension_map/dimension_map_array.bfbs";
+ Dump(dimMapArray, z => z.Table);
+
+ const string dimRankArray = "world/ik_data/field/dimension/dimension_rank/dimension_rank/dimension_rank_array.bfbs";
+ Dump(dimRankArray, z => z.Table);
+
+ const string dimConfig = "world/ik_data/field/dimension/dimension_config/dimension_config/dimension_config.bfbs";
+ DumpJson(dimConfig);
+
+ const string wazaConfig = "world/ik_data/field/wazagimmick_item_spawner/wazagimmick_item_spawner/wazagimmick_item_spawner.bfbs";
+ Dump(wazaConfig, z => z.Table);
+
+ const string itemTable = "world/ik_data/field/wazagimmick_item_table/wazagimmick_item_table/wazagimmick_item_table_array.bfbs";
+ Dump(itemTable, z => z.Table);
+
+ DumpDimensionGates();
+ }
+
private void RipPersonal(PersonalTable9ZA pt, string lang)
{
var cfg = new TextConfig(rom.Game);
@@ -1035,16 +1114,16 @@ private void RipPersonal(PersonalTable9ZA pt, string lang)
File.WriteAllLines(GetPath(lang, "MovePerPokemon.txt"), moveLines);
var dexOrder = pt.Table
- .Where(z => z.FB.Dex != 0 && z.FB.IsPresentInGame)
- .OrderBy(z => z.FB.Dex)
+ .Where(z => z.FB.Dex != null && z.FB.IsPresentInGame)
+ .OrderBy(z => z.FB.Dex!.Index)
.GroupBy(z => z.FB.Info.SpeciesNational)
.Select(z => z.First());
- var pc = dexOrder.Select(z => $"{z.FB.Dex:000}\t{specNames[z.FB.Info.SpeciesInternal]}");
+ var pc = dexOrder.Select(z => $"{z.FB.Dex!.Index:000}\t{specNames[z.FB.Info.SpeciesInternal]}");
File.WriteAllText(GetPath(lang, "dex.txt"), string.Join(Environment.NewLine, pc));
var foreign = pt.Table
- .Where(z => z.FB.Dex == 0 && z.IsPresentInGame)
+ .Where(z => z.FB.Dex == null && z.IsPresentInGame)
.GroupBy(z => z.FB.Info.SpeciesNational)
.Select(z => z.First());
@@ -1066,7 +1145,6 @@ private void RipPersonal(PersonalTable9ZA pt, string lang)
File.WriteAllText(GetPath(lang, "tm.txt"), string.Join(Environment.NewLine, tmNames));
}
-
private static byte[][] SerializeU16Pickle(PersonalTable9ZA pt, Func> sel)
{
var t = pt.Table;
@@ -1106,14 +1184,18 @@ static byte[] GetPickle(PersonalInfo9ZA e)
return Write(e.FB.Info.SpeciesNational, e.FB.Evolutions);
}
- static byte[] Write(int species, IEnumerable evos)
+ static byte[] Write(int species, IList evos)
{
using var ms = new MemoryStream();
using var bw = new BinaryWriter(ms);
- var list = evos.ToArray();
- if (species == (int)Species.Crabrawler)
- Array.Reverse(list); // put the levelup evo last.
+ var list = evos;
+ if (species is (int)Species.Crabrawler or (int)Species.Feebas)
+ {
+ var tmp = evos.ToArray();
+ Array.Reverse(tmp); // put the levelup evo last.
+ list = tmp;
+ }
foreach (var m in list) // just in case
{
@@ -1227,7 +1309,8 @@ public void DumpTrainers()
var classes = Get(trt);
var npcs = Get(npc).Table;
var trBtlData = Get(btl).Table;
- List<(string TrainerBattleID, string NPCAssetID)> trBtls = trBtlData.Select(z => (z.TrainerBattleID, z.NPCAssetID)).ToList()!;
+ List<(string TrainerBattleID1, string NPCAssetID1)> trBtls1 = trBtlData.Select(z => (z.TrainerBattleID1, z.NPCAssetID1)).ToList()!;
+ List<(string TrainerBattleID2, string NPCAssetID2)> trBtls2 = trBtlData.Select(z => (z.TrainerBattleID2, z.NPCAssetID2)).ToList()!;
// pretty
List result = [];
@@ -1276,6 +1359,7 @@ public void DumpTrainers()
var gender = pk.Sex is 0 ? string.Empty : pk.Sex is SexType.MALE ? " (♂)" : " (♀)";
var level = $" (Lv. {pk.Level})";
+ var alpha = pk.IsOybn ? " (Alpha)" : string.Empty;
var item = pk.Item is 0 ? string.Empty : $" (Held Item: {items[(int)pk.Item]})";
var rare = pk.RareType is RareType.RARE ? " ★" : string.Empty;
var move1 = PrettyDumpUtil.GetMoveText(pk.Waza1, moves);
@@ -1306,7 +1390,7 @@ public void DumpTrainers()
var eff = pk.EffortValue;
ReadOnlySpan evs = [eff.HP, eff.ATK, eff.DEF, eff.SPA, eff.SPD, eff.SPE];
var textEVs = evs.ContainsAnyExcept(0) ? $" (EVs: {PrettyDumpUtil.GetEVSpread(evs)})" : string.Empty;
- var line = $"- {name}{form}{gender}{rare}{level}{item}{ability}{nature}{ball}{moveOutput}{textIVs}{textEVs}";
+ var line = $"- {name}{form}{gender}{rare}{level}{alpha}{item}{ability}{nature}{ball}{moveOutput}{textIVs}{textEVs}";
result.Add(line);
}
@@ -1327,7 +1411,7 @@ static string GetTrainerNameFormatted(string type, string name, ushort flags)
var format = ((flags >> 7) & 0b11);
var display = format switch
{
- 0 when name is "Corbeau" or "Philippe" => "{1} of the {0}", // no clue how they differentiate these two from Grunts, hardcode it
+ 0 when type is "Rust Syndicate" && name is "Corbeau" or "Philippe" => "{1} of the {0}", // no clue how they differentiate these two from Grunts, hardcode it
0 => "{0} {1}",
1 when article => "{1} of the {0}",
1 => "{1} of {0}",
@@ -1339,6 +1423,10 @@ static string GetTrainerNameFormatted(string type, string name, ushort flags)
if (name is "representative")
display = display.Insert(0, "The ");
+ // no unique name
+ if (type is "Alpha Trainer" or "Hyperspace Trainer")
+ return type;
+
return string.Format(display, type, name);
}
@@ -1361,21 +1449,51 @@ string GetTrainerName(string id)
return $"{rivalF}/{rivalM}";
}
- var (trainerBattleId, npcAssetId) = trBtls.FirstOrDefault(z => z.TrainerBattleID == id);
- if (trainerBattleId is null)
+ // no unique name
+ if (id.StartsWith("dim_rank_"))
+ return string.Empty;
+
+ var (trainerBattleId1, npcAssetId1) = trBtls1.FirstOrDefault(z => z.TrainerBattleID1 == id);
+ var (trainerBattleId2, npcAssetId2) = trBtls2.FirstOrDefault(z => z.TrainerBattleID2 == id);
+ var btlToCheck = trainerBattleId2 is null ? trainerBattleId1 : trainerBattleId2;
+ var npcToCheck = npcAssetId2 is null ? npcAssetId1 : npcAssetId2;
+
+ if (btlToCheck is null || id is "Ev_sub_123_010" || id.Contains("Ev_sub_124"))
{
+ // not in trname
+ if (id is "Ev_sub_145_010_multi")
+ return "Marcia";
+
var fallback = GetFallbackTrainerName(id);
return ahtbNames.GetString(FnvHash.HashFnv1a_64(fallback), trNames);
}
// special name format
- if (npcAssetId.Contains("rest_trainer"))
+ if (npcToCheck.Contains("rest_trainer"))
{
- var restName = ahtbNames.GetString(FnvHash.HashFnv1a_64(npcAssetId.Replace("_trainer", "")), trNames);
+ var restName = ahtbNames.GetString(FnvHash.HashFnv1a_64(npcToCheck.Replace("_trainer", "")), trNames);
return restName;
}
- var target = npcs.FirstOrDefault(z => z.AssetId == npcAssetId)!;
+ if (npcToCheck.StartsWith("2on2_"))
+ npcToCheck = npcToCheck[5..];
+
+ var target = npcs.FirstOrDefault(z => z.AssetId == npcToCheck)!;
+
+ if (target is null)
+ {
+ var startQuest = npcToCheck.Length is 15 ? 8 : 7;
+ var startSub = npcToCheck.Length is 15 ? 12 : 11;
+ var questID = int.Parse(npcToCheck[startQuest..(startQuest + 3)]);
+ var subID = int.Parse(npcToCheck[startSub..(startSub + 3)]);
+
+ // this is off-by-two for some reason?
+ if (questID == 180)
+ subID += 2;
+
+ target = npcs.FirstOrDefault(z => z.AssetId == $"npc_sub{questID}_{subID:0000}")!;
+ }
+
var name = target.NpcInfoList.Where(z => !string.IsNullOrWhiteSpace(z.Name)).ToArray()[0].Name;
return ahtbNames.GetString(FnvHash.HashFnv1a_64(name), trNames);
}
@@ -1394,6 +1512,26 @@ string GetTrainerName(string id)
"Ev_m09_2400_multi_02" => "executive05", // Griselle
"Ev_sub_103_multi" => "alias01", // Andi
"Ev_sub_114_multi" => "battleg", // Josée
+
+ // DLC
+ "Ev_d02_0010" or "Ev_sub_161_010_multi" => "friend_01", // Naveen
+ "Ev_d00_2100_multi" or "Ev_d04_1100" or "Ev_sub_164_010_multi" => "friend_02", // Lida
+ "Ev_d01_3100" or "Ev_d02_2200" or "Ev_d03_1300" or "Ev_d04_2100" or "Ev_d05_1200" or "Ev_d06_1020" or "Ev_d07_1000" or "Ev_d07_1100" or "Ev_d07_2000" or "Ev_sub_162_010_multi" or "Ev_sub_203_010_multi_02" => "xyleader3_01", // Korrina
+ "Ev_d05_0100" => "boss03", // Corbeau
+ //"Ev_sub_145_010_multi" => "sub_145", // Marcia
+ "Ev_sub_148_020_client" => "sub_148", // Dixan
+ "Ev_sub_165_010_multi" or "Ev_sub_203_010_multi_01" => "detective", // Emma
+ "Ev_sub_166_010_multi" => "executive02", // Gwynn
+ "Ev_sub_167_010_multi" => "executive04", // Lebanne
+ "Ev_sub_168_010_multi" => "secretary", // Vinnie
+ "Ev_sub_169_010_multi" => "boss04", // Jacinthe
+ "Ev_sub_170_010_multi" => "executive03", // Philippe
+
+ // Some DLC Side Missions with NPCs from past Side Missions are, annoyingly, not mapped as such in the NPC db array
+ "Ev_sub_123_010" => "sub_002", // Trevelle
+ _ when id.Contains("megax") => "sub_001", // Tracie
+ _ when id.Contains("megay") => "sub_072", // Griddella
+
_ => throw new ArgumentOutOfRangeException(nameof(id), id, null),
};
}
@@ -1511,6 +1649,8 @@ public void ScanPatterns()
("rnd_", true),
("TITLE_", true),
("itd_", true),
+ ("donut", true),
+ ("dim_", true),
("rso_", false), // royale card spawn
("t1_", false),
@@ -1559,7 +1699,7 @@ private void CreateSaveHashDump(IEnumerable saveHashHits, string outPath
..FromFile("ik_event/bin/flag/temp_flag.bin").Table.Select(z => z.Name),
..FromFile("ik_event/bin/flag/temp_work.bin").Table.Select(z => z.Name),
- ..FromFile("ik_event/bin/title/title.bin").Table.Select(z => z.Name),
+ ..FromFile("ik_event/bin/title/title.bin").Table.Select(z => z.Name),
// works to grab first field of object as string, even though this isn't the right schema to parse as
..FromFile("ik_event/bin/momiji_count/momiji_count.bin").Table.Select(z => z.Name),
@@ -1838,7 +1978,7 @@ private void DumpPokemonPKHeX(PokemonDataDBArray pokemon)
var names = GetCommonText("monsname", lang, cfg);
var natures = GetCommonText("seikaku", lang, cfg);
- ReadOnlySpan Trades = ["sub_addpoke_araichu", "sub_tradepoke_riolu", "sub_addpoke_gyadon", "sub_tradepoke_heracros"];
+ ReadOnlySpan Trades = ["sub_addpoke_araichu", "sub_tradepoke_riolu", "sub_addpoke_gyadon", "sub_tradepoke_heracros", "sub_tradepoke_poligon2"];
foreach (var enc in pokemon.Table)
{
@@ -1963,6 +2103,7 @@ public void DumpEncountData()
var outPath = GetPath("spawner");
var dumper = new SpawnRipper9a(rom, "English", outPath);
dumper.ExportRippedMaps();
+ dumper.ExportHyperspacePickle();
}
private void DumpEncountPretty(EncountDataDBArray pokemon, PokemonDropItemTableDataDBArray drops)
@@ -2098,13 +2239,19 @@ private void DumpEncountPretty(EncountDataDBArray pokemon, PokemonDropItemTableD
{
foreach (var slot in encDropData.Where(z => z.ItemTableId == i.Id))
{
+ float totalWeight = 0;
+ foreach (var dropped in i.ItemLotteryDataList!)
+ totalWeight += dropped.Weight;
+
foreach (var dropped in i.ItemLotteryDataList!)
{
var itemID = int.TryParse(dropped.ItemId, out var id);
var weight = dropped.Weight;
var maxCount = dropped.MaxCount;
var type = dropped.Type;
- result.Add($"- {weight}% {items[id]} (×{maxCount})");
+ float rate = (float)(Math.Round((weight / totalWeight) * 100f, 2));
+ var countDisplay = maxCount == 1 ? $"×{maxCount}" : $"×1-{maxCount}";
+ result.Add($"- {rate:00.00}% {items[id]} ({countDisplay})");
}
}
}
@@ -2200,10 +2347,13 @@ private void DumpTradeNames()
result.Add(names[415]); // Riolouie (Riolu)
result.Add(names[416]); // Spicy (Slowpoke)
result.Add(names[417]); // Floffy (Raichu)
+ result.Add(names[496]); // Alias (Porygon)
+
result.Add(names[293]); // Tracie
result.Add(names[306]); // Bond
result.Add(names[310]); // Quille
result.Add(names[362]); // Griddella
+ result.Add(names[431]); // Trian
File.WriteAllLines(GetPath("pkhex", "trades", $"text_tradeza_{l}.txt"), result);
}
@@ -2444,7 +2594,7 @@ public void DumpEventTriggers()
private IEnumerable GetTriggerFileList()
{
- for (int i = 0; i <= 9; i++)
+ for (int i = 0; i <= 10; i++)
{
const string main = "ik_event/bin/trigger/main/trigger_main_{0:00}.bin";
var file = string.Format(main, i);
@@ -2458,6 +2608,106 @@ private IEnumerable GetTriggerFileList()
if (rom.HasFile(file))
yield return file;
}
+
+ yield return "ik_event/bin/trigger/map/trigger_map_t2.bin";
+ yield return "ik_event/bin/trigger/global/trigger_global.bin";
+ yield return "ik_event/bin/trigger/trainer_battle/trigger_trainer_battle_global.bin";
+ yield return "ik_event/bin/trigger/wild_battle/trigger_wild_battle_global.bin";
+ yield return "ik_event/bin/trigger/last_battle/trigger_last_battle_global.bin";
+ yield return "ik_event/bin/trigger/boss_battle/trigger_boss_battle_global.bin";
+ yield return "ik_event/bin/trigger/athletic_time_attack/trigger_athletic_time_attack.bin";
+ }
+
+ public void DumpDonutData()
+ {
+ var textConfig = new TextConfig(GameVersion.ZA);
+
+ foreach (var language in LanguageEnglishNames)
+ {
+ var code = GetLanguageCode(language);
+
+ // Get Donut Names
+ // Skip index 0, only keep 203 entries
+ var donuts = GetText("donut_name", language).AsSpan(1, 203);
+ foreach (ref var name in donuts)
+ {
+ // remove string format. don't care for now.
+ name = name.Replace("[VAR 01D9(0001)][VAR 0133(0000)] ", ""); // english
+ name = name.Replace("[VAR 0133(0000)]", ""); // otherwise, before & after
+ name = name.Replace("[VAR 01D9(0001)]", "");
+
+ }
+ WriteText("donutName", code, donuts.ToArray());
+
+ // Get Flavor Names
+ var flavors = GetText("donut_flavor", language).AsSpan(205, 290);
+ // Need to skip over the unused entries.
+ // Skip 3,4,5 and 147-152
+ var flavorNames = new List();
+ for (int i = 0; i < flavors.Length; i++)
+ {
+ if (i is 3 or 4 or 5 or >= 147 and <= 152)
+ continue;
+ flavorNames.Add(flavors[i]);
+ }
+ WriteText("donutFlavor", code, flavorNames);
+ }
+
+ return;
+
+ void WriteText(string name, string code, IEnumerable lines)
+ {
+ var path = GetPath("pkhex", "donut");
+ Directory.CreateDirectory(path);
+ var fileName = $"text_{name}_{code}.txt";
+ File.WriteAllText(Path.Combine(path, fileName), string.Join('\n', lines));
+ }
+
+ string[] GetText(string name, string lang)
+ {
+ var data = rom.GetPackedFile($"{message}/dat/{lang}/sk/{name}.dat");
+ return new TextFile(data, textConfig).Lines;
+ }
+ }
+
+ public void DumpDimensionGates()
+ {
+ var files = new[]
+ {
+ "world/ik_scene/field/area/t1/sub_scene/t1_gimmick/t1_dim_gate_spawner/t1_dim_gate_spawner_blue_/t1_dim_gate_spawner_blue_0.trscn",
+ "world/ik_scene/field/area/t1/sub_scene/t1_gimmick/t1_dim_gate_spawner/t1_dim_gate_spawner_jaune_/t1_dim_gate_spawner_jaune_0.trscn",
+ "world/ik_scene/field/area/t1/sub_scene/t1_gimmick/t1_dim_gate_spawner/t1_dim_gate_spawner_rose_/t1_dim_gate_spawner_rose_0.trscn",
+ "world/ik_scene/field/area/t1/sub_scene/t1_gimmick/t1_dim_gate_spawner/t1_dim_gate_spawner_rouge_/t1_dim_gate_spawner_rouge_0.trscn",
+ "world/ik_scene/field/area/t1/sub_scene/t1_gimmick/t1_dim_gate_spawner/t1_dim_gate_spawner_veil_/t1_dim_gate_spawner_veil_0.trscn",
+ "world/ik_scene/field/area/t1_i011/sub_scene/t1_i011_gimmick/t1_i011_dim_gate_spawner_/t1_i011_dim_gate_spawner_0.trscn",
+ "world/ik_scene/field/area/t1_i012_1/sub_scene/t1_i012_1_gimmick/t1_i012_1_dim_gate_spawner_/t1_i012_1_dim_gate_spawner_0.trscn",
+ "world/ik_scene/field/area/t1_i012_2/sub_scene/t1_i012_2_gimmick/t1_i012_2_dim_gate_spawner_/t1_i012_2_dim_gate_spawner_0.trscn",
+ "world/ik_scene/field/area/t1_i014_1/sub_scene/t1_i014_1_gimmick/t1_i014_1_dim_gate_spawner_/t1_i014_1_dim_gate_spawner_0.trscn",
+ "world/ik_scene/field/area/t2/sub_scene/t2_gimmick/t2_dim_gate_spawner_/t2_dim_gate_spawner_0.trscn",
+ "world/ik_scene/field/area/t3/sub_scene/t3_gimmick/t3_dim_gate_spawner_/t3_dim_gate_spawner_0.trscn",
+ };
+
+ List Result = [];
+ foreach (var t in files)
+ {
+ var data = rom.GetPackedFile(t);
+ var obj = FlatBufferConverter.DeserializeFrom(data);
+ Result.Add(obj.ObjectTemplateName);
+
+ foreach (var o in obj.Objects.Where(z => z.Type is "trinity_ObjectTemplate"))
+ {
+ var sceneObject = FlatBufferConverter.DeserializeFrom(o.Data);
+ var scenePoint = FlatBufferConverter.DeserializeFrom(sceneObject.Data);
+ var dim = sceneObject.ObjectTemplateName;
+ var pos = scenePoint.Position;
+
+ Result.Add($"{dim}\t{pos.X}\t{pos.Y}\t{pos.Z}");
+ }
+
+ Result.Add("");
+ }
+
+ File.WriteAllLines(GetPath("dim_gate", "dim_gate.txt"), Result);
}
}
diff --git a/pkNX.WinForms/Subforms/Gen9a/Map/Dumping/SpawnGlobalInfo.cs b/pkNX.WinForms/Subforms/Gen9a/Map/Dumping/SpawnGlobalInfo.cs
index f74e0001..f88d22c1 100644
--- a/pkNX.WinForms/Subforms/Gen9a/Map/Dumping/SpawnGlobalInfo.cs
+++ b/pkNX.WinForms/Subforms/Gen9a/Map/Dumping/SpawnGlobalInfo.cs
@@ -12,7 +12,8 @@ public sealed class SpawnGlobalInfo
private readonly GameManager9a _rom;
public readonly Dictionary Encounters;
- public readonly Dictionary SpawnerTransforms;
+ public readonly Dictionary> SpawnerTransforms;
+ public readonly List HyperspaceSpawnSets;
public readonly PokemonSpawnerDataDBArray SpawnerDatabase;
@@ -26,10 +27,45 @@ public SpawnGlobalInfo(GameManager9a rom)
const string pst = "world/ik_data/field/spawner_transform_data/pokemon_spawner_transform/pokemon_spawner_transform/pokemon_spawner_transform_array.bin";
var spawnerT = Get(pst);
- SpawnerTransforms = spawnerT.Table.SelectMany(z => z.Table).ToDictionary(z => z.Name ?? "", z => z);
+ // Build multimap of Name -> List; skip null/empty names to avoid collisions.
+ SpawnerTransforms = spawnerT.Table
+ .SelectMany(z => z.Table)
+ .Where(x => !string.IsNullOrEmpty(x.Name))
+ .GroupBy(x => x.Name)
+ .ToDictionary(g => g.Key!, g => g.ToList());
const string psp = "world/ik_data/field/pokemon_spawner/pokemon_spawner_data/pokemon_spawner_data_array.bin";
SpawnerDatabase = Get(psp);
+
+ const string dimWild = "world/ik_data/field/dimension/dimension_wild_pokemon/dimension_wild_pokemon/dimension_wild_pokemon_array.bin";
+ var dim = Get(dimWild);
+
+ // Since we don't care about the star count requirements (since distortions can be 1-5 regardless of game progress), we can lump them all together.
+ HyperspaceSpawnSets = GetHyperspaceSpawnSets(dim);
+ }
+
+ private static List GetHyperspaceSpawnSets(DimensionWildPokemonDBArray dim)
+ {
+ List result = [];
+ foreach (var a in dim.Table)
+ {
+ foreach (var b in a.Table)
+ {
+ foreach (var c in b.RankItems)
+ {
+ if (c.PatternItems is null) // no encounters for this type-set
+ continue;
+ foreach (var set in c.PatternItems)
+ {
+ if (set.SpawnerItems is not { } si)
+ continue;
+ result.AddRange(si.Select(x => x.SpawnerDataId));
+ }
+ }
+ }
+ }
+
+ return result;
}
private T Get(string path) where T : class, IFlatBufferSerializable
diff --git a/pkNX.WinForms/Subforms/Gen9a/Map/Exports/EncounterSlot9a.cs b/pkNX.WinForms/Subforms/Gen9a/Map/Exports/EncounterSlot9a.cs
index 0fb0f772..fd3dec69 100644
--- a/pkNX.WinForms/Subforms/Gen9a/Map/Exports/EncounterSlot9a.cs
+++ b/pkNX.WinForms/Subforms/Gen9a/Map/Exports/EncounterSlot9a.cs
@@ -80,11 +80,11 @@ public static void WritePickle(IReadOnlyList slotSets, string p
File.WriteAllBytes(path, mini);
}
- public static void AddSlots(List list, EncountData slot, EncountDataInfo enc)
+ public static void AddSlots(List list, EncountData slot, EncountDataInfo enc, bool hyperspaceBoost = false)
{
var species = SpeciesConverterZA.GetNational9((ushort)slot.DevNo);
var form = (byte)slot.FormNo;
- const int boost = 0; // enc.AdditionalLevel; // field is unused; assumed to be similar to Kitakami post-game level boost, but there is none applicable in Z-A.
+ int boost = !hyperspaceBoost ? 0 : enc.AdditionalLevel; // level boost used in hyperspace, applicable in Z-A.
var minLevel = (byte)(slot.MinLevel + boost);
var maxLevel = (byte)(slot.MaxLevel + boost);
var gender = (sbyte)slot.Sex;
diff --git a/pkNX.WinForms/Subforms/Gen9a/Map/MapSpawnerSet.cs b/pkNX.WinForms/Subforms/Gen9a/Map/MapSpawnerSet.cs
index e89b1e00..fd226bda 100644
--- a/pkNX.WinForms/Subforms/Gen9a/Map/MapSpawnerSet.cs
+++ b/pkNX.WinForms/Subforms/Gen9a/Map/MapSpawnerSet.cs
@@ -1,4 +1,6 @@
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using pkNX.Structures.FlatBuffers.ZA;
namespace pkNX.WinForms;
@@ -6,4 +8,18 @@ namespace pkNX.WinForms;
public sealed class MapSpawnerSet
{
public required Dictionary SpawnerInfo { get; init; }
+
+ public bool TryGetSpawnerFromEncounter(string encId, [NotNullWhen(true)] out PokemonSpawnerData? spawnerData)
+ {
+ foreach (var (key, value) in SpawnerInfo)
+ {
+ if (value.EncountDataInfoList.All(x => x.EncountDataId != encId))
+ continue;
+
+ spawnerData = value;
+ return true;
+ }
+ spawnerData = null;
+ return false;
+ }
}
diff --git a/pkNX.WinForms/Subforms/Gen9a/Map/SpawnRipper9a.cs b/pkNX.WinForms/Subforms/Gen9a/Map/SpawnRipper9a.cs
index f506ddb3..afc59131 100644
--- a/pkNX.WinForms/Subforms/Gen9a/Map/SpawnRipper9a.cs
+++ b/pkNX.WinForms/Subforms/Gen9a/Map/SpawnRipper9a.cs
@@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using pkNX.Structures;
namespace pkNX.WinForms;
@@ -19,6 +20,7 @@ public sealed class SpawnRipper9a
public readonly LumioseMap Overworld;
public const byte LocationLabs = 234;
public const byte LocationSewer = 235;
+ public const ushort LocationHyperspace = 273;
public readonly SpawnNameResolver Resolver;
public readonly SpawnGlobalInfo Shared;
@@ -60,7 +62,6 @@ private void FindAllPointsOnMaps()
RippedMaps.Add(SimulateScene(SceneInfo9a.ScenePathsT3, "t3", _ => LocationSewer));
}
-
public void DumpSceneToPickle(List rip)
{
var sets = rip.SelectMany(z => z.ToAreas()).ToArray();
@@ -106,7 +107,7 @@ public void ExportRippedMaps()
foreach (var obj in spawn.Table)
{
foreach (var sub in obj.AppearanceSpawnerObjectInfoList)
- result.Add(sub.ObjectName, obj);
+ result.TryAdd(sub.ObjectName, obj);
}
}
return result;
@@ -136,6 +137,79 @@ private SpawnSceneSimulation SimulateScene(string[] scenePaths, string map, Func
public string GetLocationName(int locationIndex) => Resolver.LocationNames[locationIndex];
public bool TryGetSpawner(string name, [NotNullWhen(true)] out PokemonSpawnerData? spawnerData) => RipInfo.SpawnerInfo.TryGetValue(name, out spawnerData);
public bool TryGetEncounter(string id, [NotNullWhen(true)] out EncountData? slot) => Shared.Encounters.TryGetValue(id, out slot);
+
+ public void ExportHyperspacePickle()
+ {
+ var list = new List();
+ var locationTable = new EncounterArea9a[] { new() { Location = LocationHyperspace, Slots = list } };
+
+ // Random set of spawns can appear at any spawner. All we care about is the encounters within that set.
+ AddRandomSpawns(list);
+
+ // Special Distortion boss spawners come with a pack of encounters to act as nuisance. We need them.
+ // ect_zdm40X_sp0Y_Z
+ // bosses are the special spawn with fixed IVs. Don't need them.
+ var band = Shared.Encounters.Select(z => z.Value)
+ .Where(z => IsHyperspaceBandSp(z.Id));
+
+ foreach (var enc in band)
+ {
+ // Need to get the spawner that has this encounter, for the relevant level boost.
+ if (!RipInfo.TryGetSpawnerFromEncounter(enc.Id, out var spawner))
+ {
+ System.Diagnostics.Debug.WriteLine($"Could not find spawner for hyperspace band encounter {enc.Id} - {(Species)enc.DevNo}");
+ continue;
+ }
+ AddSpawner(list, spawner);
+ }
+
+ foreach (var set in locationTable)
+ set.CondenseList();
+
+ var path = Path.Combine(_outDir, "encounter_hyperspace_za.pkl");
+ EncounterSlot9a.WritePickle(locationTable, path);
+
+ // Write a concatenated array of slots for visual pattern inspection.
+ EncounterSlot9a.WritePickleWithoutLocations(locationTable.SelectMany(z => z.Slots), path);
+ }
+
+ private static bool IsHyperspaceBandSp(string name) => name.StartsWith("ect_zdm40") && !name.EndsWith("_sp");
+
+ private void AddRandomSpawns(List list)
+ {
+ foreach (var set in Shared.HyperspaceSpawnSets)
+ {
+ foreach (var info in RipInfo.SpawnerInfo)
+ {
+ if (info.Value.Id != set)
+ continue;
+ AddSpawner(list, info.Value);
+ break;
+ }
+ }
+ }
+
+ private void AddSpawner(List list, PokemonSpawnerData spawner)
+ {
+ foreach (var info in spawner.AppearanceSpawnerObjectInfoList)
+ {
+ var appearInfo = info.Appearance;
+ if (appearInfo.MinCount == 0)
+ continue;
+
+ var encounters = spawner.EncountDataInfoList;
+ // don't care about randomness of the list of encounters. just add them.
+
+ foreach (var enc in encounters)
+ {
+ if (enc.EncountDataId is not { } id)
+ continue;
+ if (!TryGetEncounter(id, out var slot))
+ continue;
+ EncounterSlot9a.AddSlots(list, slot, enc, hyperspaceBoost: true);
+ }
+ }
+ }
}
public record PointDump