Last active
July 15, 2024 12:23
-
-
Save sora10pls/07d69600241f3a47959b802560ab8900 to your computer and use it in GitHub Desktop.
A Randomizer for Pokémon Scarlet and Pokémon Violet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PATHS | |
public const string DEST_MASTER = @""; | |
public const string PATH_MASTER = @""; | |
public const string DEST_LOG = @""; | |
public const string LANGUAGE = "English"; | |
public static string DEST_ZIP_ARCHIVE = Path.Combine(DEST_LOG, "ScarletVioletRandomizer.zip"); | |
// MAXIMUMS | |
public const int MAX_SPECIES_ID = 1025; | |
public const int MAX_MOVE_ID = 919; | |
public const int MAX_ABILITY_ID = 310; | |
public const int MAX_TM_COUNT = 229; | |
public const int MAX_TYPE_ID = 17; | |
public const int MAX_BALL_ID = 26; | |
public const int MAX_RIBBON_ID = 111; | |
// GLOBAL SETTINGS | |
public static bool ALLOW_SPECIES_GEN_1 = true; | |
public static bool ALLOW_SPECIES_GEN_2 = true; | |
public static bool ALLOW_SPECIES_GEN_3 = true; | |
public static bool ALLOW_SPECIES_GEN_4 = true; | |
public static bool ALLOW_SPECIES_GEN_5 = true; | |
public static bool ALLOW_SPECIES_GEN_6 = true; | |
public static bool ALLOW_SPECIES_GEN_7 = true; | |
public static bool ALLOW_SPECIES_GEN_8 = true; | |
public static bool ALLOW_SPECIES_GEN_9 = true; | |
public static bool ALLOW_SPECIES_LEGENDARY = true; | |
public static bool ALLOW_SPECIES_MYTHICAL = true; | |
public static bool ALLOW_SPECIES_PARADOX = true; | |
public static bool FORCE_SWAP_SPECIES_LEGENDARY = true; | |
public static bool FORCE_SWAP_SPECIES_MYTHICAL = true; | |
public static bool FORCE_SWAP_SPECIES_PARADOX = true; | |
public static bool RANDOMIZE_PERSONAL_ABILITY = false; | |
public static bool RANDOMIZE_PERSONAL_TYPE = false; | |
public static bool RANDOMIZE_PERSONAL_EVOLUTION = false; | |
public static bool RANDOMIZE_PERSONAL_LEARNSET = false; | |
public static bool RANDOMIZE_PERSONAL_BASE_STATS_WITHIN_BST = false; | |
public static bool RANDOMIZE_PERSONAL_BASE_STATS_COMPLETELY_RANDOM = false; | |
public static bool SHUFFLE_PERSONAL_BASE_STATS = false; | |
public static bool ABILITIES_TYPES_FOLLOW_EVOLUTIONS = false; | |
public static bool GUARANTEE_FOUR_MOVES_AT_LEVEL_ONE = false; | |
public static bool RANDOMIZE_MOVE_PROPERTIES_TYPE = false; | |
public static bool RANDOMIZE_MOVE_PROPERTIES_CATEGORY = false; | |
public static bool RANDOMIZE_MOVE_PROPERTIES_POWER = false; | |
public static bool RANDOMIZE_MOVE_PROPERTIES_ACCURACY = false; | |
public static bool RANDOMIZE_MOVE_PROPERTIES_PP = false; | |
public static bool FORCE_FULLY_EVOLVE = false; | |
public const byte FORCE_FULLY_EVOLVE_LEVEL = 40; | |
public static bool MODIFY_POKEMON_LEVELS = false; | |
public const sbyte LEVEL_MODIFIER_PERCENT = 10; | |
public static bool ALLOW_FULLY_RANDOM_TERA_TYPES = false; | |
public const byte STELLAR_TERA_TYPE_CHANCE_PERCENT = 2; | |
// ADDITIONAL SETTINGS | |
public static bool ALL_POKEMON_ONLY_KNOW_METRONOME = false; | |
public static bool ALLOW_RANDOM_PICNIC_ITEMS = false; | |
public static bool BAN_ABILITY_WONDER_GUARD = false; | |
public static bool DO_NOT_RANDOMIZE_TERA_BLAST_TM = false; | |
public static bool ENSURE_SANE_FORM_HELD_ITEMS = false; | |
public static bool ENSURE_STARTERS_HAVE_TWO_EVOLUTIONS = false; | |
public static bool GIVE_IMPORTANT_TRAINERS_SIX_POKEMON = false; | |
public static bool GIVE_TRAINERS_MAXIMUM_AI = false; | |
public static bool GIVE_TRAINERS_MAX_INDIVIDUAL_VALUES = false; | |
public static bool GIVE_TRAINERS_SMART_EFFORT_VALUES = false; | |
public static bool GIVE_POKEMON_HELD_ITEMS = false; | |
public static bool GIVE_POKEMON_RANDOM_POKE_BALLS = false; | |
public static bool GIVE_POKEMON_RANDOM_RIBBONS = false; | |
public static bool MAKE_ALL_TMS_CRAFTABLE_FROM_START = false; | |
public static bool MAKE_ALL_TRAINER_BATTLES_DOUBLE_BATTLES = false; | |
public static bool MAKE_POKEMON_EVOLVE_EVERY_LEVEL = false; | |
public static bool MAKE_WILD_POKEMON_TIME_OF_DAY_DEPENDENT = false; | |
public static bool RANDOMIZE_ROAMING_FORM_GIMMIGHOUL = false; | |
public static bool RANDOMIZE_TERA_RAID_ITEM_DROPS_BONUS = false; | |
public static bool RANDOMIZE_TERA_RAID_ITEM_DROPS_FIXED = false; | |
public static bool REMOVE_EFFORT_VALUE_YIELDS = false; | |
public static bool RIVAL_CARRIES_STARTER_THROUGHOUT_GAME = false; | |
public const byte TRAINER_MINIMUM_POKEMON_COUNT = 1; | |
public const byte TRAINER_MAXIMUM_POKEMON_COUNT = 6; | |
public const byte TRAINER_POKEMON_SHINY_CHANCE_PERCENT = 2; | |
// POTENTIAL CRASHES AND SOFTLOCKS -- PROCEED WITH CAUTION | |
public static bool RANDOMIZE_KORAIDON_MIRAIDON = false; | |
public static bool RANDOMIZE_GENERIC_OVERWORLD_POKEMON = false; | |
public const byte GENERIC_OVERWORLD_POKEMON_SHINY_CHANCE_PERCENT = 2; | |
// INITIALIZE LISTS | |
public static List<DevID> SpeciesBanlist = GetSpeciesBanlist(); | |
public static List<DevID> SpeciesList = GetSpeciesList(); | |
public static List<int> PermittedMoves = GetPermittedMoves(); | |
public static List<int> PermittedAbilities = GetPermittedAbilities(); | |
public static List<int> TechnicalMachineList = GetTMList(); | |
public static List<ushort> ItemList = GetItemList(); | |
public static List<string> RandomizerLog = new(); | |
void Main() | |
{ | |
if (String.IsNullOrWhiteSpace(DEST_MASTER) || String.IsNullOrWhiteSpace(PATH_MASTER) || String.IsNullOrWhiteSpace(DEST_LOG) || !Languages.Contains(LANGUAGE)) | |
{ | |
"Path definitions are invalid. Please revise your setup at the top of the LINQPad query. Aborting randomization.".Dump(); | |
} | |
else | |
{ | |
RandomizerLog.Clear(); // sometimes running LINQ query won't clear a prior list | |
AddCustomItemData(); | |
RandomizePokemonStats(); | |
RandomizeMoveProperties(); | |
"".Dump(); | |
RandomizeWildPokemon(); | |
RandomizeFixedSymbolEncounters(); | |
RandomizeStaticEncounters(); | |
RandomizeGiftEncounters(); | |
RandomizeTrainerPokemon(); | |
RandomizeFieldItems(); | |
RandomizeHiddenItems(); | |
RandomizePickupItemTables(); | |
RandomizeRummageItems(); | |
RandomizeTeraRaidEncounters(); | |
RandomizeTechnicalMachines(); | |
RandomizeTradeEncounters(); | |
RandomizeAcademyAceTournamentRewards(); | |
RandomizePokedexMilestoneRewards(); | |
RandomizeStarBarrageEncounters(); | |
RandomizeStarmobileStats(); | |
RandomizeOgreOustinRewards(); | |
RandomizeSpecialCoachRewards(); | |
RandomizePortoMarinadaAuctions(); | |
RandomizeItemPrinterItems(); | |
RandomizeTeraRaidItemDrops(); | |
"".Dump(); | |
EnhanceShopLineups(); | |
EnhanceItemData(); | |
UpdatePlibConversionTable(); | |
EnhanceEvolutionParameters(); | |
EnhanceBlueberryQuests(); | |
EnhanceBoutiqueAndSalonLineups(); | |
ExpandPaldeaPokedex(); | |
UpdateItemSortTables(); | |
ExtraChanges(); | |
"".Dump(); | |
if (RandomizerLog.Count is not 0) | |
GenerateRandomizerLogFile(); | |
FinalizeRandomizerData(); | |
} | |
} | |
private static void RandomizeWildPokemon() | |
{ | |
List<string> PokeData = | |
[ | |
"world/data/encount/pokedata/pokedata/pokedata_array.bin", | |
"world/data/encount/pokedata/pokedata_su1/pokedata_su1_array.bin", | |
"world/data/encount/pokedata/pokedata_su2/pokedata_su2_array.bin", | |
]; | |
foreach (string t in PokeData) | |
{ | |
int index = t.LastIndexOf("/") + 1; | |
string f = t[index..]; | |
string d = t[..index]; | |
string location = f switch | |
{ | |
"pokedata_array.bin" => "Paldea", | |
"pokedata_su1_array.bin" => "Kitakami", | |
"pokedata_su2_array.bin" => "Blueberry Academy", | |
_ => throw new ArgumentException($"Invalid file: {f}"), | |
}; | |
$"Randomizing wild Pokémon ({location})...".Dump(); | |
string file = Path.Combine(PATH_MASTER, d, f); | |
var obj = FlatBufferConverter.DeserializeFrom<EncountPokeDataArray>(file); | |
obj.Table.Clear(); // now we reconstruct PokeData from scratch! | |
int maxBiome = location is "Blueberry Academy" ? 24 : 23; // DENKI_ISHI does not exist in Paldea/Kitakami | |
for (int i = 0001; i <= 1025; i++) | |
{ | |
if (SpeciesBanlist.Contains((DevID)i)) | |
continue; | |
if (i is 1021) // prevent wild Terapagos due to the game generating an invalid Tera Type (Normal, not Stellar) | |
continue; | |
bool IsBisharp = i is 0625; | |
byte forms = GetFormCount((ushort)i); | |
for (int c = 0; c < forms; c++) | |
{ | |
if (!GetIsPresentInGame((ushort)i, (byte)c)) | |
continue; | |
if (!IsValidForm((ushort)i, (byte)c)) | |
continue; | |
BandType band = IsBisharp ? BandType.BOSS : (BandType)GetRandom(1, 3); | |
EncountPokeData entry = new() | |
{ | |
DevId = (DevID)i, | |
Sex = SexType.DEFAULT, | |
Form = (sbyte)c, | |
MinLevel = 1, | |
MaxLevel = 100, | |
LotValue = (short)GetRandom(30, 81), | |
Biome1 = (Biome)GetRandom(1, maxBiome), | |
LotValue1 = (short)GetRandom(30, 81), | |
Biome2 = (Biome)GetRandom(1, maxBiome), | |
LotValue2 = (short)GetRandom(30, 81), | |
Biome3 = (Biome)GetRandom(1, maxBiome), | |
LotValue3 = (short)GetRandom(30, 81), | |
Biome4 = (Biome)GetRandom(1, maxBiome), | |
LotValue4 = (short)GetRandom(30, 81), | |
Area = GenerateAreaList(), | |
LocationName = string.Empty, | |
MinHeight = 0, | |
MaxHeight = 0, | |
Enable = GetEnableTable((ushort)i, (byte)c), | |
Time = GetTimeTable(), | |
FlagName = string.Empty, | |
BandRate = IsBisharp ? (short)100 : (short)GetRandom(30, 81), | |
Band = band, | |
BandPoke = IsBisharp ? DevID.DEV_KOMATANA : (DevID)i, | |
BandSex = SexType.DEFAULT, | |
BandForm = IsBisharp ? (sbyte)0 : (sbyte)c, | |
OutbreakLotValue = (sbyte)GetRandom(30, 81), | |
PokeVoiceClassification = "ANIMAL", | |
Version = new VersionTable() { A = true, B = true }, | |
Item = GetBringItem((DevID)i, (sbyte)c), | |
}; | |
obj.Table.Add(entry); | |
} | |
} | |
// Area Zero spawns must have a blank area list, and there are only 5 possible biomes, so let's add an additional 200 spawns with special logic specifically for Area Zero | |
if (location is "Paldea") | |
{ | |
"Randomizing wild encounters (Area Zero)...".Dump(); | |
for (int i = 0; i < 200; i++) | |
{ | |
List<Biome> Biomes = GetAreaZeroBiomes(); | |
DevID dev = GetRandomSpecies(0); | |
// prevent wild Terapagos due to the game generating an invalid Tera Type (Normal, not Stellar) | |
if (dev is DevID.DEV_KODAIGAME) | |
{ | |
while (dev is DevID.DEV_KODAIGAME) | |
dev = GetRandomSpecies(0); | |
} | |
sbyte form = (sbyte)GetRandomForm(dev); | |
bool IsBisharp = dev is DevID.DEV_KIRIKIZAN; | |
EncountPokeData entry = new() | |
{ | |
DevId = dev, | |
Sex = SexType.DEFAULT, | |
Form = form, | |
MinLevel = 1, | |
MaxLevel = 100, | |
LotValue = (short)GetRandom(30, 81), | |
Biome1 = Biomes[0], | |
LotValue1 = (short)GetRandom(30, 81), | |
Biome2 = Biomes[1], | |
LotValue2 = (short)GetRandom(30, 81), | |
Biome3 = Biomes[2], | |
LotValue3 = (short)GetRandom(30, 81), | |
Biome4 = Biomes[3], | |
LotValue4 = (short)GetRandom(30, 81), | |
Area = string.Empty, | |
LocationName = "a_w23_d01,a_w23_d02,a_w23_d03,a_w23_d04,a_w23_field_1,a_w23_field_2,a_w23_field_3", | |
MinHeight = 0, | |
MaxHeight = 0, | |
Enable = GetEnableTable((ushort)dev, (byte)form), | |
Time = GetTimeTable(), | |
FlagName = string.Empty, | |
BandRate = (short)GetRandom(30, 81), | |
Band = (BandType)GetRandom(1, 3), | |
BandPoke = dev, | |
BandSex = SexType.DEFAULT, | |
BandForm = form, | |
OutbreakLotValue = 0, | |
PokeVoiceClassification = "ANIMAL", | |
Version = new VersionTable() { A = true, B = true }, | |
Item = GetBringItem(dev, form), | |
}; | |
obj.Table.Add(entry); | |
} | |
} | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
string dest = Path.Combine(DEST_MASTER, d); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
File.WriteAllBytes(Path.Combine(dest, f), data); | |
} | |
} | |
private static string GenerateAreaList() | |
{ | |
int[] group = new int[10]; | |
for (int i = 0; i < group.Length; i++) | |
group[i] = GetRandom(1, 23); | |
List<int> Areas = group.ToList(); | |
Areas.Sort(); | |
Areas = Areas.Distinct().ToList(); | |
return String.Join(",", Areas); | |
} | |
private static EnableTable GetEnableTable(ushort dev, byte form) | |
{ | |
ushort species = GetNationalDexID(dev); | |
EnableTable Enable = new() { Land = true }; | |
if (GetCanSwimOrBeAboveWater(species, form)) | |
Enable.UpWater = Enable.Underwater = true; | |
if (GetCanFlyOrLevitate(species, form)) | |
Enable.Air1 = Enable.Air2 = true; | |
return Enable; | |
} | |
private static TimeTable GetTimeTable() | |
{ | |
if (!MAKE_WILD_POKEMON_TIME_OF_DAY_DEPENDENT) | |
return new() { Morning = true, Noon = true, Evening = true, Night = true }; | |
int rand = GetRandom(4); | |
TimeTable Time = new(); | |
if (rand is 0) Time.Morning = true; | |
if (rand is 1) Time.Noon = true; | |
if (rand is 2) Time.Evening = true; | |
if (rand is 3) Time.Night = true; | |
return Time; | |
} | |
private static BringItem GetBringItem(DevID species, sbyte form) | |
{ | |
bool IsBisharp = species is DevID.DEV_KIRIKIZAN; | |
if (IsBisharp) | |
return new BringItem() { ItemID = ItemID.ITEMID_KASIRANOAKASI, BringRate = 100 }; // always give Leader’s Crest | |
if (ENSURE_SANE_FORM_HELD_ITEMS && IsFormHeldItemDependent((DevID)species)) | |
return new BringItem() { ItemID = GetFormSpecificItem((DevID)species, (short)form), BringRate = 100 }; | |
if (GIVE_POKEMON_HELD_ITEMS) | |
return new BringItem() { ItemID = GetRandomItem(), BringRate = (sbyte)GetRandom(101) }; | |
return new BringItem(); | |
} | |
private static List<Biome> GetAreaZeroBiomes() | |
{ | |
List<Biome> EligibleBiomes = [ Biome.GRASS, Biome.RIVER, Biome.UNDERGROUND, Biome.ROCKY, Biome.CAVE ]; | |
List<Biome> Biomes = new(); | |
// only pick 4 of the 5 eligible biomes | |
int exclude = GetRandom(5); | |
foreach (Biome b in EligibleBiomes) | |
{ | |
if (b == (Biome)exclude) | |
continue; | |
Biomes.Add(b); | |
} | |
return Biomes; | |
} | |
private static void RandomizeFixedSymbolEncounters() | |
{ | |
RandomizerLog.Add("== FIXED SYMBOL ENCOUNTERS =="); | |
"Randomizing fixed symbol encounters...".Dump(); | |
const string file = "fixed_symbol_table_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/field/fixed_symbol/fixed_symbol_table/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/field/fixed_symbol/fixed_symbol_table/"); | |
var obj = FlatBufferConverter.DeserializeFrom<FixedSymbolTableArray>(Path.Combine(path, file)); | |
string[] names = GetStringsCommon(LANGUAGE, "monsname"); | |
bool IsWaterFixedSymbol(string key) => key.StartsWith("area18_") || key is "area06_13" or "area11_16" or "area11_17" or "area11_18" or "area11_19" or "area15_04" or "area15_19" or "area15_21" or "area19_19" | |
or "su1_area07_04" or "su1_area08_07" or "su1_area09_02" or "su1_area09_03" or "su1_area09_07" or "su1_area09_10" or "su1_area09_12" or "su1_area11_21" or "su1_area11_24" | |
or "su2_area02_01" or "su2_area02_02" or "su2_area02_03" or "su2_area02_04" or "su2_area02_05" or "su2_area02_06" or "su2_area02_07" or "su2_area02_38" or "su2_area04_21" or "su2_area04_23" or "su2_area04_25" or "su2_area04_27" | |
or "t_su1_area08_10" or "t_su1_area11_14" or "t_su1_area11_23" | |
or "t_su2_area02_27" or "t_su2_area02_28" or "t_su2_area02_33"; | |
foreach (var enc in obj.Table) | |
{ | |
var t = enc.Symbol; | |
var oldDev = t.DevId; | |
var oldForm = t.FormId; | |
t.DevId = GetRandomSpecies(t.DevId); | |
t.FormId = GetRandomForm(t.DevId); | |
// keep randomizing water fixed symbols until we get an eligible species/form pair | |
if (IsWaterFixedSymbol(enc.TableKey)) | |
{ | |
while (!GetCanSwimOrBeAboveWater((ushort)t.DevId, t.FormId)) | |
{ | |
t.DevId = GetRandomSpecies(t.DevId); | |
t.FormId = GetRandomForm(t.DevId); | |
} | |
} | |
t.Sex = GetRandomGender(t.DevId, t.FormId); | |
t.RareType = RareType.DEFAULT; | |
t.WazaType = WazaType.DEFAULT; | |
t.TokuseiIndex = (TokuseiType)GetRandom(2, 5); | |
// random if wild Tera, preserve Stellar, otherwise randomize according to setting | |
t.GemType = t.GemType switch | |
{ | |
not (GemType.DEFAULT or GemType.RANDOM or GemType.NIJI) => (GemType)GetRandom(2, 20), | |
GemType.NIJI => GemType.NIJI, | |
_ => GetTeraType(t.DevId, (byte)t.FormId, t.GemType, false) | |
}; | |
if (MODIFY_POKEMON_LEVELS) | |
t.Level = Math.Clamp((int)(t.Level * (1.0f + ((float)LEVEL_MODIFIER_PERCENT / 100.0f))), 1, 100); | |
if (t.DevId is DevID.DEV_MAHOIPPU) | |
t.MahoippuViewId = (MahoippuViewID)GetRandom(7); | |
string oForm = oldForm is 0 ? string.Empty : $"-{oldForm}"; | |
string nForm = t.FormId is 0 ? string.Empty : $"-{t.FormId}"; | |
RandomizerLog.Add($"{names[(int)oldDev]}{oForm} -> {names[(int)t.DevId]}{nForm}"); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizeStaticEncounters() | |
{ | |
RandomizerLog.Add("== STATIC ENCOUNTERS =="); | |
"Randomizing static encounters...".Dump(); | |
List<PokeDataEventBattle> OldSpecies = GetOldStaticEncounters(); | |
List<PokeDataEventBattle> NewSpecies = new(); | |
List<string> Blacklist = [ "nusi_952_dummy_pink", "nusi_952_dummy_red", "nusi_952_dummy_yellow" ]; | |
const string file = "eventBattlePokemon_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/battle/eventBattlePokemon/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/battle/eventBattlePokemon/"); | |
var obj = FlatBufferConverter.DeserializeFrom<EventBattlePokemonArray>(Path.Combine(path, file)); | |
string[] names = GetStringsCommon(LANGUAGE, "monsname"); | |
var table = obj.Table; | |
if (!RANDOMIZE_KORAIDON_MIRAIDON) | |
{ | |
Blacklist.Add(table[031].Label); | |
Blacklist.Add(table[032].Label); | |
Blacklist.Add(table[115].Label); | |
Blacklist.Add(table[116].Label); | |
} | |
foreach (var enc in table.Where(z => !Blacklist.Contains(z.Label))) | |
{ | |
var t = enc.PokeData; | |
t.DevId = GetRandomSpecies(t.DevId); | |
t.FormId = GetRandomForm(t.DevId); | |
t.Sex = GetRandomGender(t.DevId, t.FormId); | |
t.Seikaku = t.SeikakuHosei = SeikakuType.DEFAULT; | |
t.RareType = RareType.DEFAULT; | |
t.WazaType = WazaType.DEFAULT; | |
t.Tokusei = (TokuseiType)GetRandom(2, 5); | |
t.DropItem = GetRandomItem(); | |
// keep Area Zero Underdepths Stellars as Stellar, handle Ogerpon edge case, otherwise, pick fitting Tera Type | |
t.GemType = t.GemType is GemType.NIJI ? GemType.NIJI : enc.Label switch | |
{ | |
"SDC01_kamenoni_1" or "SDC01_kamenoni_1_strong" => GemType.HONOO, | |
"SDC01_kamenoni_2" or "SDC01_kamenoni_2_strong" => GemType.MIZU, | |
"SDC01_kamenoni_3" or "SDC01_kamenoni_3_strong" => GemType.IWA, | |
"SDC01_kamenoni_4" or "SDC01_kamenoni_4_strong" => GemType.KUSA, | |
_ => GetTeraType(t.DevId, (byte)t.FormId, t.GemType, false), | |
}; | |
if (GIVE_POKEMON_RANDOM_RIBBONS) | |
t.SetRibbon = (RibbonType)GetRandom(1, MAX_RIBBON_ID + 1); | |
if (MODIFY_POKEMON_LEVELS) | |
t.Level = Math.Clamp((int)(t.Level * (1.0f + ((float)LEVEL_MODIFIER_PERCENT / 100.0f))), 1, 100); | |
if (GIVE_POKEMON_HELD_ITEMS) | |
t.Item = GetRandomItem(); | |
if (FORCE_FULLY_EVOLVE && t.Level >= FORCE_FULLY_EVOLVE_LEVEL) | |
{ | |
t.DevId = (DevID)ForceEvolveSpeciesFully((ushort)t.DevId, (byte)t.FormId); | |
t.FormId = GetRandomForm(t.DevId); // refresh | |
t.Sex = GetRandomGender(t.DevId, t.FormId); // refresh | |
} | |
if (enc.Label is "sub018_BIGUANA") | |
t = ApplyParadiseSoftlockPrevention(t); | |
if (enc.Label is "SDC02_0330_kodaikame") | |
t = ApplyTerapagosSoftlockPrevention(t); | |
if (ENSURE_SANE_FORM_HELD_ITEMS && IsFormHeldItemDependent(t.DevId)) | |
t.Item = GetFormSpecificItem(t.DevId, t.FormId); | |
NewSpecies.Add(t); | |
} | |
obj = StandardizeStaticEncounters(obj); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
List<DevID> BossBattles = [ table[043].PokeData.DevId, table[035].PokeData.DevId, table[041].PokeData.DevId, table[033].PokeData.DevId, table[037].PokeData.DevId, table[045].PokeData.DevId, table[031].PokeData.DevId, table[032].PokeData.DevId ]; | |
UpdateBossText(BossBattles); | |
UpdateAllStaticScenes(obj); | |
UpdateAllKitakamiScenes(obj); | |
int count = table.Count - Blacklist.Count; | |
for (int i = 0; i < count; i++) | |
{ | |
var o = OldSpecies[i]; | |
var n = NewSpecies[i]; | |
string oForm = o.FormId is 0 ? string.Empty : $"-{o.FormId}"; | |
string nForm = n.FormId is 0 ? string.Empty : $"-{n.FormId}"; | |
RandomizerLog.Add($"{names[(int)o.DevId]}{oForm} -> {names[(int)n.DevId]}{nForm}"); | |
} | |
RandomizerLog.Add(""); | |
} | |
private static List<PokeDataEventBattle> GetOldStaticEncounters() | |
{ | |
List<PokeDataEventBattle> StaticEncounters = new(); | |
List<string> Blacklist = [ "nusi_952_dummy_pink", "nusi_952_dummy_red", "nusi_952_dummy_yellow" ]; | |
const string file = "eventBattlePokemon_array.bin"; | |
string path = Path.Combine(PATH_MASTER, "world/data/battle/eventBattlePokemon/"); | |
var obj = FlatBufferConverter.DeserializeFrom<EventBattlePokemonArray>(Path.Combine(path, file)); | |
if (!RANDOMIZE_KORAIDON_MIRAIDON) | |
{ | |
Blacklist.Add(obj.Table[031].Label); | |
Blacklist.Add(obj.Table[032].Label); | |
Blacklist.Add(obj.Table[115].Label); | |
Blacklist.Add(obj.Table[116].Label); | |
} | |
foreach (var t in obj.Table.Where(z => !Blacklist.Contains(z.Label))) | |
StaticEncounters.Add(t.PokeData); | |
return StaticEncounters; | |
} | |
private static void RandomizeGiftEncounters() | |
{ | |
RandomizerLog.Add("== GIFT ENCOUNTERS =="); | |
"Randomizing gift encounters...".Dump(); | |
const string file = "eventAddPokemon_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/event/event_add_pokemon/eventAddPokemon/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/event/event_add_pokemon/eventAddPokemon/"); | |
var obj = FlatBufferConverter.DeserializeFrom<EventAddPokemonArray>(Path.Combine(path, file)); | |
string[] names = GetStringsCommon(LANGUAGE, "monsname"); | |
List<PokeDataFull> OldSpecies = GetOldGiftEncounters(); | |
List<PokeDataFull> NewSpecies = new(); | |
List<string> Blacklist = [ "add_0195", "add_0193" ]; | |
bool IsStarter(string id) => id is "common_0065_hono" or "common_0065_kusa" or "common_0065_mizu"; | |
if (!RANDOMIZE_KORAIDON_MIRAIDON) | |
{ | |
Blacklist.Add(obj.Table[005].Label); | |
Blacklist.Add(obj.Table[006].Label); | |
} | |
foreach (var enc in obj.Table.Where(z => !Blacklist.Contains(z.Label))) | |
{ | |
var t = enc.PokeData; | |
t.DevId = GetRandomSpecies(t.DevId); | |
if (ENSURE_STARTERS_HAVE_TWO_EVOLUTIONS && IsStarter(enc.Label)) | |
{ | |
while (!GetIsThreeStageFamily(GetNationalDexID((ushort)t.DevId))) | |
t.DevId = GetRandomSpecies(0); | |
} | |
t.FormId = GetRandomForm(t.DevId); | |
t.Sex = GetRandomGender(t.DevId, t.FormId); | |
t.Seikaku = t.SeikakuHosei = SeikakuType.DEFAULT; | |
t.RareType = RareType.DEFAULT; | |
t.WazaType = WazaType.DEFAULT; | |
t.Tokusei = (TokuseiType)GetRandom(2, 5); | |
t.GemType = GetTeraType(t.DevId, (byte)t.FormId, t.GemType, false); | |
if (GIVE_POKEMON_RANDOM_POKE_BALLS) | |
t.BallId = (BallType)GetRandom(1, MAX_BALL_ID + 1); | |
if (GIVE_POKEMON_RANDOM_RIBBONS) | |
t.SetRibbon = (RibbonType)GetRandom(1, MAX_RIBBON_ID + 1); | |
if (MODIFY_POKEMON_LEVELS) | |
t.Level = Math.Clamp((int)(t.Level * (1.0f + ((float)LEVEL_MODIFIER_PERCENT / 100.0f))), 1, 100); | |
if (GIVE_POKEMON_HELD_ITEMS) | |
t.Item = GetRandomItem(); | |
if (ENSURE_SANE_FORM_HELD_ITEMS && IsFormHeldItemDependent(t.DevId)) | |
t.Item = GetFormSpecificItem(t.DevId, t.FormId); | |
NewSpecies.Add(t); | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
UpdateAllGiftScenes(obj); | |
if (RANDOMIZE_KORAIDON_MIRAIDON) | |
{ | |
string statics = Path.Combine(DEST_MASTER, "world/data/battle/eventBattlePokemon/eventBattlePokemon_array.bin"); | |
var objS = FlatBufferConverter.DeserializeFrom<EventBattlePokemonArray>(statics); | |
obj = StandardizeGiftEncounters(obj, objS); | |
UpdateAllIguanaScenes(obj); | |
} | |
int count = obj.Table.Count - Blacklist.Count; | |
for (int i = 0; i < count; i++) | |
{ | |
var o = OldSpecies[i]; | |
var n = NewSpecies[i]; | |
string oForm = o.FormId is 0 ? string.Empty : $"-{o.FormId}"; | |
string nForm = n.FormId is 0 ? string.Empty : $"-{n.FormId}"; | |
RandomizerLog.Add($"{names[(int)o.DevId]}{oForm} -> {names[(int)n.DevId]}{nForm}"); | |
} | |
RandomizerLog.Add(""); | |
} | |
private static List<PokeDataFull> GetOldGiftEncounters() | |
{ | |
List<PokeDataFull> GiftEncounters = new(); | |
List<string> Blacklist = [ "add_0195", "add_0193" ]; | |
const string file = "eventAddPokemon_array.bin"; | |
string path = Path.Combine(PATH_MASTER, "world/data/event/event_add_pokemon/eventAddPokemon/"); | |
var obj = FlatBufferConverter.DeserializeFrom<EventAddPokemonArray>(Path.Combine(path, file)); | |
if (!RANDOMIZE_KORAIDON_MIRAIDON) | |
{ | |
Blacklist.Add(obj.Table[005].Label); | |
Blacklist.Add(obj.Table[006].Label); | |
} | |
foreach (var t in obj.Table.Where(z => !Blacklist.Contains(z.Label))) | |
GiftEncounters.Add(t.PokeData); | |
return GiftEncounters; | |
} | |
private static void RandomizeTrainerPokemon() | |
{ | |
RandomizerLog.Add("== TRAINER BATTLES =="); | |
"Randomizing trainer Pokémon...".Dump(); | |
const string fileTrData = "trdata_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/trainer/trdata/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/trainer/trdata/"); | |
const string fileTrType = "trtype_array.bin"; | |
string pathType = Path.Combine(PATH_MASTER, "world/data/trainer/trtype/"); | |
var obj = FlatBufferConverter.DeserializeFrom<TrDataMainArray>(Path.Combine(path, fileTrData)); | |
var typ = FlatBufferConverter.DeserializeFrom<TrainerTypeArray>(Path.Combine(pathType, fileTrType)); | |
string[] species = GetStringsCommon(LANGUAGE, "monsname"); | |
string[] trNames = GetStringsCommon(LANGUAGE, "trname"); | |
string[] trTypes = GetStringsCommon(LANGUAGE, "trtype"); | |
string gifts = Path.Combine(DEST_MASTER, "world/data/event/event_add_pokemon/eventAddPokemon/eventAddPokemon_array.bin"); | |
var objG = FlatBufferConverter.DeserializeFrom<EventAddPokemonArray>(gifts); | |
List<string> IncreasedCountBlacklist = | |
[ | |
"brother_02_02", | |
"pepper_00", | |
"pepper_nusi_01", | |
"pepper_nusi_02", | |
"pepper_nusi_03", | |
"pepper_nusi_04", | |
"pepper_nusi_05", | |
"professor_A_02", | |
"professor_B_02", | |
"rival_01_hono", | |
"rival_01_kusa", | |
"rival_01_mizu", | |
]; | |
bool IsNemona(string id) => id.Contains("rival_") || id.Contains("nemo_"); | |
bool IsClavell(string id) => id.Contains("clavel_") || id.Contains("claver_"); | |
bool IsNemonaStage1(string id) => IsNemona(id) && !id.Contains("rival_02_01") && (id.Contains("rival_01_") || id.Contains("rival_02_") || id.Contains("rival_03_")); | |
bool IsNemonaStage2(string id) => IsNemona(id) && id.Contains("rival_04_"); | |
bool IsValidDoubleBattle(string id) => !id.EndsWith("_boss_01") && !id.Contains("_multi") && !id.StartsWith("pepper_nusi_") && !id.StartsWith("professor_") && !id.Contains("raid_assist") && id is not "brother_02_02"; | |
PokeDataFull StarterG1 = objG.Table[001].PokeData; | |
PokeDataFull StarterF1 = objG.Table[000].PokeData; | |
PokeDataFull StarterW1 = objG.Table[002].PokeData; | |
PokeDataFull StarterG2 = ForceEvolveSpeciesOnce(new PokeDataFull { DevId = StarterG1.DevId, FormId = StarterG1.FormId }); | |
PokeDataFull StarterF2 = ForceEvolveSpeciesOnce(new PokeDataFull { DevId = StarterF1.DevId, FormId = StarterF1.FormId }); | |
PokeDataFull StarterW2 = ForceEvolveSpeciesOnce(new PokeDataFull { DevId = StarterW1.DevId, FormId = StarterW1.FormId }); | |
PokeDataFull StarterG3 = ForceEvolveSpeciesOnce(new PokeDataFull { DevId = StarterG2.DevId, FormId = StarterG2.FormId }); | |
PokeDataFull StarterF3 = ForceEvolveSpeciesOnce(new PokeDataFull { DevId = StarterF2.DevId, FormId = StarterF2.FormId }); | |
PokeDataFull StarterW3 = ForceEvolveSpeciesOnce(new PokeDataFull { DevId = StarterW2.DevId, FormId = StarterW2.FormId }); | |
foreach (var t in obj.Table) | |
{ | |
List<PokeDataBattle> Team = [ t.Poke1, t.Poke2, t.Poke3, t.Poke4, t.Poke5, t.Poke6 ]; | |
int count = Team.Count(z => z.DevId is not DevID.DEV_NULL); | |
// add Pokémon to party to meet required minimum | |
if (count < TRAINER_MINIMUM_POKEMON_COUNT && !IncreasedCountBlacklist.Contains(t.TrId)) | |
{ | |
int min = Math.Clamp((int)TRAINER_MINIMUM_POKEMON_COUNT, 1, 6); | |
if (min is 6 && t.TrId.EndsWith("_boss_01")) | |
min = 5; | |
for (int i = 0; i < min; i++) | |
Team[i] = GetBlankPokemon(Team[0].Level, Team[0].TalentValue, Team[0].EffortValue); | |
} | |
// remove Pokémon from party to meet required maximum | |
if (count > TRAINER_MAXIMUM_POKEMON_COUNT) | |
{ | |
int max = Math.Clamp((int)TRAINER_MAXIMUM_POKEMON_COUNT, 1, 6); | |
for (int i = max; i < 6; i++) | |
Team[i].DevId = DevID.DEV_NULL; | |
} | |
// turn all battles into double battles, with some exceptions | |
if (MAKE_ALL_TRAINER_BATTLES_DOUBLE_BATTLES && IsValidDoubleBattle(t.TrId)) | |
{ | |
if ((count & 1) is not 0) | |
Team[count] = GetBlankPokemon(Team[0].Level, Team[0].TalentValue, Team[0].EffortValue); | |
t.BattleType = BattleType.DOUBLE; | |
t.AiDouble = true; | |
} | |
if (GIVE_IMPORTANT_TRAINERS_SIX_POKEMON && ImportantTrainers.Contains(t.TrId)) | |
{ | |
// fill empty slots with a placeholder | |
int max = t.TrId.EndsWith("_boss_01") ? 5 : 6; | |
for (int i = 0; i < max; i++) | |
{ | |
if (Team[i].DevId is not DevID.DEV_NULL) | |
continue; | |
Team[i] = GetBlankPokemon(Team[0].Level, Team[0].TalentValue, Team[0].EffortValue); | |
} | |
} | |
// refresh team data before randomizing, and sort by level ascending, with empty slots at the end | |
Team = Team.OrderBy(z => z.Level is 0).ThenBy(z => z.Level).ToList(); | |
t.Poke1 = Team[0]; | |
t.Poke2 = Team[1]; | |
t.Poke3 = Team[2]; | |
t.Poke4 = Team[3]; | |
t.Poke5 = Team[4]; | |
t.Poke6 = Team[5]; | |
foreach (var pk in Team.Where(z => z.DevId is not DevID.DEV_NULL)) | |
{ | |
if (!RANDOMIZE_KORAIDON_MIRAIDON && t.TrId is "professor_A_02" or "professor_B_02") | |
continue; | |
RandomizeTrainerPokemonSingle(pk); | |
} | |
// special handling to give AI Sada/Turo a full party of unique Paradoxes | |
if (FORCE_SWAP_SPECIES_PARADOX && ALLOW_SPECIES_GEN_9 && t.TrId is "professor_A_01" or "professor_B_01") | |
{ | |
int max = Math.Clamp((int)TRAINER_MAXIMUM_POKEMON_COUNT, 1, 6); | |
pkNX.Randomization.Util.Shuffle(Paradox); | |
Team[max - 1].Item = ItemID.ITEMID_BUUSUTOENAJII; // give last Pokémon Booster Energy, true to the vanilla game | |
for (int i = 0; i < max; i++) | |
{ | |
Team[i].DevId = (DevID)GetDevID((ushort)Paradox[i]); | |
Team[i].FormId = 0; | |
Team[i].Sex = SexType.DEFAULT; | |
if (GIVE_TRAINERS_SMART_EFFORT_VALUES) | |
Team[i].EffortValue = SetSmartEVs((ushort)Team[i].DevId, Team[i].FormId); | |
} | |
} | |
if (RIVAL_CARRIES_STARTER_THROUGHOUT_GAME && (IsNemona(t.TrId) || IsClavell(t.TrId))) | |
{ | |
// unused rival battles | |
if (!t.TrId.Contains("kusa") && !t.TrId.Contains("hono") && !t.TrId.Contains("mizu")) | |
continue; | |
int ace = Team.IndexOf(Team.OrderBy(z => z.Level).Last()); | |
int index = !t.TrId.Contains("rival_02_01") && t.TrId.StartsWith("rival_02_") ? 0 : ace; // starter is always last slot, except for Nemona's Terastal tutorial battle | |
PokeDataFull Starter = new(); | |
if (IsClavell(t.TrId)) | |
{ | |
if (t.TrId.Contains("kusa")) Starter = StarterF3; | |
if (t.TrId.Contains("hono")) Starter = StarterW3; | |
if (t.TrId.Contains("mizu")) Starter = StarterG3; | |
} | |
if (IsNemonaStage1(t.TrId)) | |
{ | |
if (t.TrId.Contains("kusa")) Starter = StarterW1; | |
if (t.TrId.Contains("hono")) Starter = StarterG1; | |
if (t.TrId.Contains("mizu")) Starter = StarterF1; | |
} | |
else if (IsNemonaStage2(t.TrId)) | |
{ | |
if (t.TrId.Contains("kusa")) Starter = StarterW2; | |
if (t.TrId.Contains("hono")) Starter = StarterG2; | |
if (t.TrId.Contains("mizu")) Starter = StarterF2; | |
} | |
else | |
{ | |
if (t.TrId.Contains("kusa")) Starter = StarterW3; | |
if (t.TrId.Contains("hono")) Starter = StarterG3; | |
if (t.TrId.Contains("mizu")) Starter = StarterF3; | |
} | |
Team[index].DevId = Starter.DevId; | |
Team[index].FormId = Starter.FormId; | |
Team[index].Sex = Starter.Sex; | |
Team[index].RareType = RareType.NO_RARE; | |
if (GIVE_TRAINERS_SMART_EFFORT_VALUES) | |
Team[index].EffortValue = SetSmartEVs((ushort)Team[index].DevId, Team[index].FormId); | |
} | |
if (GIVE_TRAINERS_MAXIMUM_AI) | |
t.AiBasic = t.AiHigh = t.AiExpert = t.AiChange = true; | |
} | |
string statics = Path.Combine(DEST_MASTER, "world/data/battle/eventBattlePokemon/eventBattlePokemon_array.bin"); | |
var objS = FlatBufferConverter.DeserializeFrom<EventBattlePokemonArray>(statics); | |
obj = StandardizeTrainerData(obj, objS); | |
foreach (var t in obj.Table) | |
{ | |
string type = GetTrainerClass(t.TrainerType); | |
string name = GetStringFromAHTBCommon("trname", FnvHash.HashFnv1a_64(t.TrNameLabel), LANGUAGE); | |
string display = $"{type} {name}".Trim(); | |
// unused | |
if ((String.IsNullOrWhiteSpace(type) && String.IsNullOrWhiteSpace(name)) || name.Contains("[~ ")) | |
continue; | |
if (display.Contains("INVALID") && t.TrId is not ("professor_A_01" or "professor_A_02" or "professor_B_01" or "professor_B_02")) | |
continue; | |
if (!t.TrId.Contains("kusa") && !t.TrId.Contains("hono") && !t.TrId.Contains("mizu") && (IsNemona(t.TrId) || IsClavell(t.TrId))) | |
continue; | |
display = display.Replace("INVALID", " ").Trim(); | |
List<string> Members = new(); | |
List<PokeDataBattle> Team = [ t.Poke1, t.Poke2, t.Poke3, t.Poke4, t.Poke5, t.Poke6 ]; | |
foreach (var pk in Team.Where(z => z.DevId is not DevID.DEV_NULL)) | |
{ | |
string form = pk.FormId is 0 ? string.Empty : $"-{pk.FormId}"; | |
string rare = pk.RareType is RareType.RARE ? " ★" : string.Empty; | |
string line = $"{species[(int)pk.DevId]}{form}{rare} (Lv. {pk.Level})"; | |
Members.Add(line); | |
} | |
RandomizerLog.Add($"{display} - {String.Join(" / ", Members)}"); | |
} | |
PokeDataBattle GetBlankPokemon(int level, ParamSet talent, ParamSet effort) | |
{ | |
PokeDataBattle pk = new() | |
{ | |
DevId = DevID.DEV_HUSIGIDANE, | |
Level = level, | |
Waza1 = new WazaSet { WazaId = WazaID.WAZA_NULL, PointUp = 0 }, | |
Waza2 = new WazaSet { WazaId = WazaID.WAZA_NULL, PointUp = 0 }, | |
Waza3 = new WazaSet { WazaId = WazaID.WAZA_NULL, PointUp = 0 }, | |
Waza4 = new WazaSet { WazaId = WazaID.WAZA_NULL, PointUp = 0 }, | |
TalentValue = talent, | |
EffortValue = effort, | |
}; | |
return pk; | |
} | |
string GetTrainerClass(string trClass) | |
{ | |
var trainer = typ.Table.FirstOrDefault(z => z.NameLabel == trClass); | |
if (trainer is null) | |
return string.Empty; | |
return GetStringFromAHTBCommon("trtype", FnvHash.HashFnv1a_64(trainer.MsgLabel), LANGUAGE); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, fileTrData), data); | |
UpdateAllTrainerScenes(obj); | |
} | |
private static PokeDataBattle RandomizeTrainerPokemonSingle(PokeDataBattle pk) | |
{ | |
pk.DevId = GetRandomSpecies(pk.DevId); | |
pk.FormId = GetRandomForm(pk.DevId); | |
pk.Sex = GetRandomGender(pk.DevId, pk.FormId); | |
pk.WazaType = WazaType.DEFAULT; | |
pk.GemType = GetTeraType(pk.DevId, (byte)pk.FormId, pk.GemType, false); | |
pk.Seikaku = SeikakuType.DEFAULT; | |
pk.Tokusei = (TokuseiType)GetRandom(2, 5); | |
pk.RareType = GetRandom(100) < TRAINER_POKEMON_SHINY_CHANCE_PERCENT ? RareType.RARE : RareType.NO_RARE; | |
if (GIVE_POKEMON_RANDOM_POKE_BALLS) | |
pk.BallId = (BallType)GetRandom(1, MAX_BALL_ID + 1); | |
if (MODIFY_POKEMON_LEVELS) | |
pk.Level = Math.Clamp((int)(pk.Level * (1.0f + ((float)LEVEL_MODIFIER_PERCENT / 100.0f))), 1, 100); | |
if (GIVE_POKEMON_HELD_ITEMS) | |
pk.Item = GetRandomItem(); | |
if (GIVE_TRAINERS_MAX_INDIVIDUAL_VALUES) | |
{ | |
pk.TalentType = TalentType.VALUE; | |
pk.TalentValue = new ParamSet { HP = 31, ATK = 31, DEF = 31, SPA = 31, SPD = 31, SPE = 31 }; | |
} | |
if (FORCE_FULLY_EVOLVE && pk.Level >= FORCE_FULLY_EVOLVE_LEVEL) | |
{ | |
pk.DevId = (DevID)ForceEvolveSpeciesFully((ushort)pk.DevId, (byte)pk.FormId); | |
pk.FormId = GetRandomForm(pk.DevId); // refresh | |
pk.Sex = GetRandomGender(pk.DevId, pk.FormId); // refresh | |
} | |
if (GIVE_TRAINERS_SMART_EFFORT_VALUES) | |
pk.EffortValue = SetSmartEVs((ushort)pk.DevId, pk.FormId); | |
if (ENSURE_SANE_FORM_HELD_ITEMS && IsFormHeldItemDependent(pk.DevId)) | |
pk.Item = GetFormSpecificItem(pk.DevId, pk.FormId); | |
return pk; | |
} | |
private static ParamSet SetSmartEVs(ushort species, short form) | |
{ | |
string source = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(source); | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == species && z.Info.Form == form); | |
List<byte> BaseStats = [ sel.Base.HP, sel.Base.ATK, sel.Base.DEF, sel.Base.SPA, sel.Base.SPD, sel.Base.SPE ]; | |
var highest = BaseStats | |
.Select((stat, index) => new Tuple<int, byte>(index, stat)) | |
.OrderByDescending(t => t.Item2) | |
.Take(3) | |
.ToArray(); | |
ParamSet Spread = new(); | |
if (highest[0].Item1 is 0) Spread.HP = 252; | |
if (highest[0].Item1 is 1) Spread.ATK = 252; | |
if (highest[0].Item1 is 2) Spread.DEF = 252; | |
if (highest[0].Item1 is 3) Spread.SPA = 252; | |
if (highest[0].Item1 is 4) Spread.SPD = 252; | |
if (highest[0].Item1 is 5) Spread.SPE = 252; | |
if (highest[1].Item1 is 0) Spread.HP = 252; | |
if (highest[1].Item1 is 1) Spread.ATK = 252; | |
if (highest[1].Item1 is 2) Spread.DEF = 252; | |
if (highest[1].Item1 is 3) Spread.SPA = 252; | |
if (highest[1].Item1 is 4) Spread.SPD = 252; | |
if (highest[1].Item1 is 5) Spread.SPE = 252; | |
if (highest[2].Item1 is 0) Spread.HP = 6; | |
if (highest[2].Item1 is 1) Spread.ATK = 6; | |
if (highest[2].Item1 is 2) Spread.DEF = 6; | |
if (highest[2].Item1 is 3) Spread.SPA = 6; | |
if (highest[2].Item1 is 4) Spread.SPD = 6; | |
if (highest[2].Item1 is 5) Spread.SPE = 6; | |
return Spread; | |
} | |
private static void RandomizeFieldItems() | |
{ | |
"Randomizing field items...".Dump(); | |
List<string> FieldItems = | |
[ | |
"world/scene/parts/field/streaming_event/world_item_/world_item_0.trscn", // Paldea | |
"world/scene/parts/field/streaming_event/c01_item_/c01_item_0.trscn", // Mesagoza | |
"world/scene/parts/field/room/a_w23_field/a_w23_field_event/a_w23_field_item_/a_w23_field_item_0.trscn", // Area Zero | |
"world/scene/parts/field/room/a_w23_i21/a_w23_i21_event/a_w23_i21_item_/a_w23_i21_item_0.trscn", // Zero Lab | |
"world/scene/parts/field/streaming_event/su1_world_item_/su1_world_item_0.trscn", // Kitakami | |
"world/scene/parts/field/streaming_event/su2_world_item_/su2_world_item_0.trscn", // Blueberry Academy | |
"world/scene/parts/field/room/a_w23_d10/a_w23_d10_event/a_w23_d10_field_item_/a_w23_d10_field_item_0.trscn", // Area Zero Underdepths | |
]; | |
bool IsTeraShard(ulong item) => item is (>= 1862 and <= 1879) or 2549; | |
foreach (string t in FieldItems) | |
{ | |
int index = t.LastIndexOf("/") + 1; | |
string f = t[index..]; | |
string d = t[..index]; | |
string location = f switch | |
{ | |
"world_item_0.trscn" => "PALDEA", | |
"c01_item_0.trscn" => "MESAGOZA", | |
"a_w23_field_item_0.trscn" => "AREA ZERO", | |
"a_w23_i21_item_0.trscn" => "ZERO LAB", | |
"su1_world_item_0.trscn" => "KITAKAMI", | |
"su2_world_item_0.trscn" => "BLUEBERRY ACADEMY", | |
"a_w23_d10_field_item_0.trscn" => "AREA ZERO UNDERDEPTHS", | |
_ => throw new ArgumentException($"Invalid file: {f}"), | |
}; | |
RandomizerLog.Add($"== FIELD ITEMS ({location}) =="); | |
string file = Path.Combine(PATH_MASTER, d, f); | |
var obj = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplate>(file); | |
string[] names = GetStringsCommon(LANGUAGE, "itemname"); | |
foreach (var o in obj.Objects) | |
{ | |
foreach (var s in o.SubObjects.Where(z => z.Type is "trinity_PropertySheet")) | |
{ | |
var tps = FlatBufferConverter.DeserializeFrom<TrinityPropertySheet>(s.Data); | |
foreach (var p in tps.Properties) | |
{ | |
foreach (var i in p.Fields.Where(z => z.Name is "itemInfo")) | |
{ | |
bool IsModifyQuantity = false; | |
var item = i.Data.Item9.Fields.FirstOrDefault(z => z.Name is "itemNo").Data.TrinityPropertySheetField1; | |
var quantity = i.Data.Item9.Fields.FirstOrDefault(z => z.Name is "num").Data.TrinityPropertySheetField1; | |
bool IsBlueberryTeraShard = location is "BLUEBERRY ACADEMY" or "AREA ZERO UNDERDEPTHS" && IsTeraShard(item.Value); | |
if (TechnicalMachineList.Contains((int)item.Value)) | |
continue; | |
if (IsBlueberryTeraShard) // do not randomize due to excessive quantity | |
continue; | |
string oItem = names[item.Value]; | |
item.Value = (ulong)GetRandomItem(); | |
string nItem = names[item.Value]; | |
IsModifyQuantity = IsTeraShard(item.Value); | |
string oQuantity = $"×{quantity.Value}"; | |
if (IsModifyQuantity) | |
quantity.Value = GetRandomWeightedQuantity(); // give Tera Shards a special, weighted quantity | |
string nQuantity = $"×{quantity.Value}"; | |
RandomizerLog.Add($"- {oItem} ({oQuantity}) -> {nItem} ({nQuantity})"); | |
} | |
} | |
s.Data = FlatBufferConverter.SerializeFrom(tps); | |
} | |
} | |
RandomizerLog.Add(""); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
string dest = Path.Combine(DEST_MASTER, d); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
File.WriteAllBytes(Path.Combine(dest, f), data); // Scarlet | |
File.WriteAllBytes(Path.Combine(dest, f.Replace("_0.", "_1.")), data); // Violet | |
} | |
} | |
private static ulong GetRandomWeightedQuantity() => GetRandom(100) switch | |
{ | |
<= 99 and >= 65 => 15, // 35% | |
<= 64 and >= 40 => 20, // 25% | |
<= 39 and >= 20 => 25, // 20% | |
<= 19 and >= 10 => 30, // 10% | |
<= 9 and >= 5 => 35, // 5% | |
<= 4 and >= 1 => 40, // 4% | |
_ => 50, // 1% -- enough to change a Pokémon's Tera Type! | |
}; | |
private static void RandomizeHiddenItems() | |
{ | |
"Randomizing hidden items...".Dump(); | |
string[] names = GetStringsCommon(LANGUAGE, "itemname"); | |
List<string> HiddenItems = | |
[ | |
"world/data/item/hiddenItemDataTable/hiddenItemDataTable_array.bin", | |
"world/data/item/hiddenItemDataTable_lc/hiddenItemDataTable_lc_array.bin", | |
"world/data/item/hiddenItemDataTable_su1/hiddenItemDataTable_su1_array.bin", | |
"world/data/item/hiddenItemDataTable_su2/hiddenItemDataTable_su2_array.bin", | |
]; | |
foreach (string h in HiddenItems) | |
{ | |
int index = h.LastIndexOf("/") + 1; | |
string f = h[index..]; | |
string d = h[..index]; | |
string location = f switch | |
{ | |
"hiddenItemDataTable_array.bin" => "PALDEA", | |
"hiddenItemDataTable_lc_array.bin" => "AREA ZERO", | |
"hiddenItemDataTable_su1_array.bin" => "KITAKAMI", | |
"hiddenItemDataTable_su2_array.bin" => "BLUEBERRY ACADEMY", | |
_ => throw new ArgumentException($"Invalid file: {f}"), | |
}; | |
RandomizerLog.Add($"== HIDDEN ITEMS ({location}) =="); | |
string file = Path.Combine(PATH_MASTER, d, f); | |
var obj = FlatBufferConverter.DeserializeFrom<HiddenItemDataTableArray>(file); | |
foreach (var t in obj.Table) | |
{ | |
List<HiddenItemDataTableInfo> Items = [ t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6, t.Item7, t.Item8, t.Item9, t.Item10 ]; | |
Items = Items.OrderByDescending(z => z.EmergePercent).ToList(); | |
RandomizerLog.Add($"Table {obj.Table.IndexOf(t) + 1}"); | |
foreach (var item in Items.Where(z => z.ItemId is not 0 && z.EmergePercent is not 0)) | |
{ | |
float rate = item.EmergePercent / 10f; | |
string oldItem = $"{names[(int)item.ItemId]} (×{item.DropCount})"; | |
item.ItemId = GetRandomItem(); | |
string newItem = $"{names[(int)item.ItemId]} (×{item.DropCount})"; | |
string line = $"- {rate:00.00}% {oldItem} -> {newItem}"; | |
RandomizerLog.Add(line); | |
} | |
RandomizerLog.Add(""); | |
} | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
string dest = Path.Combine(DEST_MASTER, d); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
File.WriteAllBytes(Path.Combine(dest, f), data); | |
} | |
} | |
private static void RandomizePickupItemTables() | |
{ | |
RandomizerLog.Add("== PICKUP ITEMS =="); | |
"Randomizing Pickup item tables...".Dump(); | |
const string file = "monohiroiItemData_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/item/monohiroiItemData/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/item/monohiroiItemData/"); | |
var obj = FlatBufferConverter.DeserializeFrom<MonohiroiItemArray>(Path.Combine(path, file)); | |
string[] names = GetStringsCommon(LANGUAGE, "itemname"); | |
List<string> Level = [ "Level", "Niveau", "Level", "Livello", "Lv", "Lv", "레벨", "等级", "Nivel", "等級" ]; | |
List<string> Tables = [ "1-10", "11-20", "21-30", "31-40", "41-50", "51-60", "61-70", "71-80", "81-90", "91-100" ]; | |
for (int i = 0; i < obj.Table.Count; i++) | |
{ | |
var t = obj.Table[i]; | |
string lv = Level[Languages.IndexOf(LANGUAGE)] + " " + Tables[i]; | |
RandomizerLog.Add(lv); | |
List<OneMonohiroiItem> Items = [ t.Item01, t.Item02, t.Item03, t.Item04, t.Item05, t.Item06, t.Item07, t.Item08, t.Item09, t.Item10, | |
t.Item11, t.Item12, t.Item13, t.Item14, t.Item15, t.Item16, t.Item17, t.Item18, t.Item19, t.Item20, | |
t.Item21, t.Item22, t.Item23, t.Item24, t.Item25, t.Item26, t.Item27, t.Item28, t.Item29, t.Item30, | |
]; | |
Items = Items.OrderByDescending(z => z.Rate).ToList(); | |
foreach (var item in Items.Where(z => z.ItemId is not ItemID.ITEMID_NONE)) | |
{ | |
item.ItemId = GetRandomItem(); | |
RandomizerLog.Add($"- {item.Rate}% {names[(int)item.ItemId]}"); | |
} | |
RandomizerLog.Add(""); | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizeRummageItems() | |
{ | |
"Randomizing rummaging items...".Dump(); | |
const string file = "rummagingItemDataTable_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/item/rummagingItemDataTable/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/item/rummagingItemDataTable/"); | |
var obj = FlatBufferConverter.DeserializeFrom<RummagingItemDataTableArray>(Path.Combine(path, file)); | |
foreach (var t in obj.Table) | |
{ | |
if (t.Item00 is not ItemID.ITEMID_NONE) t.Item00 = GetRandomItem(); | |
if (t.Item01 is not ItemID.ITEMID_NONE) t.Item01 = GetRandomItem(); | |
if (t.Item02 is not ItemID.ITEMID_NONE) t.Item02 = GetRandomItem(); | |
if (t.Item03 is not ItemID.ITEMID_NONE) t.Item03 = GetRandomItem(); | |
if (t.Item04 is not ItemID.ITEMID_NONE) t.Item04 = GetRandomItem(); | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizeTeraRaidEncounters() | |
{ | |
"Randomizing Tera Raid Battle encounters...".Dump(); | |
List<string> RaidBosses = | |
[ | |
// Paldea | |
"world/data/raid/raid_enemy_01/raid_enemy_01_array.bin", | |
"world/data/raid/raid_enemy_02/raid_enemy_02_array.bin", | |
"world/data/raid/raid_enemy_03/raid_enemy_03_array.bin", | |
"world/data/raid/raid_enemy_04/raid_enemy_04_array.bin", | |
"world/data/raid/raid_enemy_05/raid_enemy_05_array.bin", | |
"world/data/raid/raid_enemy_06/raid_enemy_06_array.bin", | |
// Kitakami | |
"world/data/raid/su1_raid_enemy_01/su1_raid_enemy_01_array.bin", | |
"world/data/raid/su1_raid_enemy_02/su1_raid_enemy_02_array.bin", | |
"world/data/raid/su1_raid_enemy_03/su1_raid_enemy_03_array.bin", | |
"world/data/raid/su1_raid_enemy_04/su1_raid_enemy_04_array.bin", | |
"world/data/raid/su1_raid_enemy_05/su1_raid_enemy_05_array.bin", | |
"world/data/raid/su1_raid_enemy_06/su1_raid_enemy_06_array.bin", | |
// Blueberry Academy | |
"world/data/raid/su2_raid_enemy_01/su2_raid_enemy_01_array.bin", | |
"world/data/raid/su2_raid_enemy_02/su2_raid_enemy_02_array.bin", | |
"world/data/raid/su2_raid_enemy_03/su2_raid_enemy_03_array.bin", | |
"world/data/raid/su2_raid_enemy_04/su2_raid_enemy_04_array.bin", | |
"world/data/raid/su2_raid_enemy_05/su2_raid_enemy_05_array.bin", | |
"world/data/raid/su2_raid_enemy_06/su2_raid_enemy_06_array.bin", | |
]; | |
foreach (string r in RaidBosses) | |
{ | |
int index = r.LastIndexOf("/") + 1; | |
string f = r[index..]; | |
string d = r[..index];; | |
string file = Path.Combine(PATH_MASTER, d, f); | |
var obj = FlatBufferConverter.DeserializeFrom<RaidEnemyTableArray>(file); | |
bool IsFiveOrSixStar = file.Contains("enemy_05") || file.Contains("enemy_06"); | |
foreach (var boss in obj.Table) | |
{ | |
var t = boss.Info.BossPokePara; | |
var e = boss.Info.BossDesc; | |
t.DevId = GetRandomSpecies(t.DevId); | |
// Terapagos can only be Stellar Tera Type, and the game does not load Stellar raid crystals | |
if (t.DevId is DevID.DEV_KODAIGAME) | |
{ | |
while (t.DevId is DevID.DEV_KODAIGAME) | |
t.DevId = GetRandomSpecies(0); | |
} | |
t.FormId = GetRandomForm(t.DevId); | |
t.Sex = GetRandomGender(t.DevId, t.FormId); | |
t.WazaType = WazaType.DEFAULT; | |
t.GemType = GetTeraType(t.DevId, (byte)t.FormId, t.GemType, true); | |
if (GIVE_POKEMON_HELD_ITEMS) | |
t.Item = GetRandomItem(); | |
if (MODIFY_POKEMON_LEVELS) | |
{ | |
t.Level = Math.Clamp((int)(t.Level * (1.0f + ((float)LEVEL_MODIFIER_PERCENT / 100.0f))), 1, 100); | |
boss.Info.CaptureLv = (sbyte)Math.Clamp((int)(boss.Info.CaptureLv * (1.0f + ((float)LEVEL_MODIFIER_PERCENT / 100.0f))), 1, 100); | |
} | |
if (FORCE_FULLY_EVOLVE && IsFiveOrSixStar) | |
{ | |
t.DevId = (DevID)ForceEvolveSpeciesFully((ushort)t.DevId, (byte)t.FormId); | |
t.FormId = GetRandomForm(t.DevId); // refresh | |
t.Sex = GetRandomGender(t.DevId, t.FormId); // refresh | |
} | |
if (ENSURE_SANE_FORM_HELD_ITEMS && IsFormHeldItemDependent(t.DevId)) | |
t.Item = GetFormSpecificItem(t.DevId, t.FormId); | |
// replace move extra actions with more sane actions | |
List<RaidBossExtraData> ExtraActions = [ e.ExtraAction1, e.ExtraAction2, e.ExtraAction3, e.ExtraAction4, e.ExtraAction5, e.ExtraAction6 ]; | |
if (ExtraActions.Any(z => z.Wazano is not WazaID.WAZA_NULL)) | |
{ | |
foreach (var a in ExtraActions.Where(z => IsExtraActionValid(z) && z.Action is RaidBossExtraActType.WAZA && z.Wazano is not WazaID.WAZA_NULL)) | |
{ | |
RaidBossExtraActType action = (RaidBossExtraActType)GetRandom(1, 3); | |
RaidBossExtraTimingType type = (RaidBossExtraTimingType)GetRandom(1, 3); | |
a.Action = action; | |
a.Timing = type; | |
a.Value = (short)GetRandom(1, 100); | |
} | |
} | |
} | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
string dest = Path.Combine(DEST_MASTER, d); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
File.WriteAllBytes(Path.Combine(dest, f), data); | |
} | |
} | |
private static bool IsExtraActionValid(RaidBossExtraData action) | |
{ | |
if (action.Action is RaidBossExtraActType.NONE) | |
return false; | |
if (action.Value is 0) | |
return false; | |
if (action.Action is RaidBossExtraActType.WAZA && action.Wazano is WazaID.WAZA_NULL) | |
return false; | |
return true; | |
} | |
private static void RandomizeTechnicalMachines() | |
{ | |
RandomizerLog.Add("== TECHNICAL MACHINES =="); | |
"Randomizing TMs...".Dump(); | |
const string file = "itemdata_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/item/itemdata/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/item/itemdata/"); | |
string[] items = GetStringsCommon(LANGUAGE, "itemname"); | |
string[] moves = GetStringsCommon(LANGUAGE, "wazaname"); | |
List<int> Moves = GetRandomMoveList(); | |
List<int> TMs = new(); | |
string machine = items[0328].Remove(items[0328].Length - 3); | |
byte ctr = 0; | |
byte idx = 1; | |
// keep TM171 unchanged | |
if (DO_NOT_RANDOMIZE_TERA_BLAST_TM) | |
{ | |
Moves.Remove(851); | |
Moves.Insert(171, 851); | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
var obj = FlatBufferConverter.DeserializeFrom<ItemDataArray>(Path.Combine(path, file)); | |
foreach (var t in obj.Table) | |
{ | |
if (ctr > MAX_TM_COUNT) | |
{ | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
break; | |
} | |
if (t.MachineWaza is WazaID.WAZA_NULL) | |
continue; | |
t.MachineWaza = (WazaID)Moves[ctr]; | |
TMs.Add(Moves[ctr]); | |
ctr++; | |
} | |
foreach (var t in obj.Table) | |
{ | |
if (t.MachineWaza is not WazaID.WAZA_NULL && t.Id is not 1230) | |
{ | |
RandomizerLog.Add($"{machine}{idx:000} {moves[(ushort)t.MachineWaza]}"); | |
idx++; | |
} | |
} | |
RandomizerLog.Add(""); | |
FixTMCompatibility(TMs); | |
FixTMMachine(TMs); | |
FixTMIcons(); | |
} | |
private static void FixTMCompatibility(List<int> TMs) | |
{ | |
const string file = "personal_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "avalon/data/"); | |
string source = Directory.Exists(dest) ? Path.Combine(dest, file) : Path.Combine(PATH_MASTER, "avalon/data/", file); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(source); | |
foreach (var t in obj.Table) | |
{ | |
if (ALL_POKEMON_ONLY_KNOW_METRONOME) | |
t.TechnicalMachine.Clear(); | |
else | |
{ | |
List<ushort> Revised = new(); | |
List<int> Indexes = new(); | |
IList<ushort> Learnable = t.TechnicalMachine; | |
IList<ushort> Moves = PersonalDumperSV.TMIndexes; | |
for (int i = 0; i < Moves.Count; i++) | |
{ | |
if (Learnable.Contains(Moves[i])) | |
Indexes.Add(i); | |
} | |
for (int i = 1; i < TMs.Count; i++) | |
{ | |
if (!Indexes.Contains(i)) | |
continue; | |
int index = i > 100 ? i : i - 1; | |
Revised.Add((ushort)TMs[index]); | |
} | |
t.TechnicalMachine = Revised.ToList(); | |
} | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void FixTMMachine(List<int> TMs) | |
{ | |
const string file = "shop_wazamachine_data_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/ui/shop/shop_wazamachine/shop_wazamachine_data/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/ui/shop/shop_wazamachine/shop_wazamachine_data/"); | |
var obj = FlatBufferConverter.DeserializeFrom<ShopWazamachineDataArray>(Path.Combine(path, file)); | |
for (int i = 0; i < obj.Table.Count; i++) | |
{ | |
// DLC TMs are not appended to the bottom of the table... they're placed at the start! | |
int index = i switch | |
{ | |
>= 000 and <= 057 => i + 172, // TM172 to TM229 | |
>= 058 and <= 156 => i - 058, // TM001 to TM099 | |
>= 157 and <= 228 => i - 057, // TM100 to TM171 | |
_ => 0, | |
}; | |
// write back to table | |
var item = obj.Table[i]; | |
item.WazaNo = TMs[index]; | |
if (MAKE_ALL_TMS_CRAFTABLE_FROM_START) | |
{ | |
item.Cond = CondEnum.NONE; | |
item.CondValue = string.Empty; | |
} | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void FixTMIcons() | |
{ | |
const string file = "itemdata_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/item/itemdata/"); | |
string destIcon = Path.Combine(DEST_MASTER, "appli/tex/icon_item/"); | |
string pathIcon = Path.Combine(PATH_MASTER, "appli/tex/icon_item/"); | |
var obj = FlatBufferConverter.DeserializeFrom<ItemDataArray>(Path.Combine(dest, file)); | |
foreach (var t in obj.Table) | |
{ | |
if (t.MachineWaza is WazaID.WAZA_NULL || t.Id is 1230) | |
continue; | |
string current = $"item_{t.Id:0000}"; | |
string desired = GetIconName((ushort)t.MachineWaza); | |
if (!Directory.Exists(Path.Combine(destIcon, current))) | |
Directory.CreateDirectory(Path.Combine(destIcon, current)); | |
File.Copy(Path.Combine(pathIcon, desired, $"{desired}.bntx"), Path.Combine(destIcon, current, $"{current}.bntx"), true); | |
} | |
} | |
private static void RandomizePokemonStats() | |
{ | |
const string file = "personal_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "avalon/data/"); | |
string path = Path.Combine(PATH_MASTER, "avalon/data/"); | |
string[] species = GetStringsCommon(LANGUAGE, "monsname"); | |
string[] ability = GetStringsCommon(LANGUAGE, "tokusei"); | |
string[] types = GetStringsCommon(LANGUAGE, "typename"); | |
string[] moves = GetStringsCommon(LANGUAGE, "wazaname"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(Path.Combine(path, file)); | |
obj = AddRegionalFormEvolutions(obj); | |
var ordered = obj.Table.OrderBy(z => z.Info.SpeciesNational); | |
// give evolution moves to Stantler and Dipplin, regardless of randomizer settings | |
var stan = ordered.FirstOrDefault(z => z.Info.SpeciesNational is 0234); | |
var dipp = ordered.FirstOrDefault(z => z.Info.SpeciesNational is 1011); | |
stan.Learnset.Add(new PersonalInfoMove { Move = 828, Level = 30 }); | |
dipp.ReminderMoves.Add(913); | |
if (RANDOMIZE_PERSONAL_ABILITY) | |
{ | |
RandomizerLog.Add("== POKÉMON ABILITIES =="); | |
"Randomizing Pokémon abilities...".Dump(); | |
if (ABILITIES_TYPES_FOLLOW_EVOLUTIONS) | |
{ | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
t.Ability1 = (ushort)GetRandomAbility(); | |
t.Ability2 = (ushort)GetRandomAbility(); | |
t.AbilityH = (ushort)GetRandomAbility(); | |
} | |
foreach (var t in ordered.Where(z => z.IsPresentInGame && z.Info.SpeciesNational is not 0133 && z.Evolutions.Count is not 0)) | |
{ | |
// first evo | |
foreach (var e in t.Evolutions.Where(z => z.SpeciesInternal is not 0)) | |
{ | |
var evoS1 = e.SpeciesInternal; | |
var evoF1 = e.Form; | |
var evo1 = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == evoS1 && z.Info.Form == evoF1); | |
evo1.Ability1 = t.Ability1; | |
evo1.Ability2 = t.Ability2; | |
evo1.AbilityH = t.AbilityH; | |
// second evo | |
foreach (var f in evo1.Evolutions.Where(z => z.SpeciesInternal is not 0)) | |
{ | |
var evoS2 = f.SpeciesInternal; | |
var evoF2 = f.Form; | |
var evo2 = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == evoS2 && z.Info.Form == evoF2); | |
evo2.Ability1 = t.Ability1; | |
evo2.Ability2 = t.Ability2; | |
evo2.AbilityH = t.AbilityH; | |
} | |
} | |
} | |
// Gimmighoul edge case | |
var gimmi = ordered.FirstOrDefault(z => z.Info.SpeciesNational is 0999 && z.Info.Form is 0); | |
var ghold = ordered.FirstOrDefault(z => z.Info.SpeciesNational is 1000); | |
gimmi.Ability1 = ghold.Ability1; | |
gimmi.Ability2 = ghold.Ability2; | |
gimmi.AbilityH = ghold.AbilityH; | |
} | |
else | |
{ | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
t.Ability1 = (ushort)GetRandomAbility(); | |
t.Ability2 = (ushort)GetRandomAbility(); | |
t.AbilityH = (ushort)GetRandomAbility(); | |
} | |
} | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
if (t.Info.SpeciesNational is 1007 or 1008 && t.Info.Form is not 0) | |
continue; | |
string form = t.Info.Form is 0 ? string.Empty : $"-{t.Info.Form}"; | |
string name = $"{t.Info.SpeciesNational:0000} {species[t.Info.SpeciesInternal]}{form}"; | |
string abil = $"{ability[t.Ability1]} (1) / {ability[t.Ability2]} (2) / {ability[t.AbilityH]} (H)"; | |
RandomizerLog.Add($"{name} - {abil}"); | |
} | |
RandomizerLog.Add(""); | |
} | |
if (RANDOMIZE_PERSONAL_TYPE) | |
{ | |
RandomizerLog.Add("== POKÉMON TYPES =="); | |
"Randomizing Pokémon types...".Dump(); | |
if (ABILITIES_TYPES_FOLLOW_EVOLUTIONS) | |
{ | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
byte type1 = (byte)GetRandom(MAX_TYPE_ID + 1); | |
byte type2 = type1; | |
// give a 40% chance at dual-type | |
if (GetRandom(100) < 40) | |
{ | |
while (GetRandom(MAX_TYPE_ID + 1) != type1) | |
type2 = (byte)GetRandom(MAX_TYPE_ID + 1); | |
} | |
t.Type1 = type1; | |
t.Type2 = type2; | |
} | |
bool GainNewTypeFirst = GetRandom(100) < 30 ? true : false; | |
foreach (var t in ordered.Where(z => z.IsPresentInGame && z.Info.SpeciesNational is not 0133 && z.Evolutions.Count is not 0)) | |
{ | |
// first evo | |
foreach (var e in t.Evolutions.Where(z => z.SpeciesInternal is not 0)) | |
{ | |
var evoS1 = e.SpeciesInternal; | |
var evoF1 = e.Form; | |
var evo1 = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == evoS1 && z.Info.Form == evoF1); | |
evo1.Type1 = t.Type1; | |
evo1.Type2 = t.Type2; | |
// give a 30% chance at gaining a second type upon first evolution | |
if (evo1.Type1 == evo1.Type2 && GainNewTypeFirst) | |
{ | |
while (GetRandom(MAX_TYPE_ID + 1) != t.Type1) | |
evo1.Type2 = (byte)GetRandom(MAX_TYPE_ID + 1); | |
} | |
// second evo | |
bool GainNewTypeSecond = GetRandom(100) < 40 ? true : false; | |
foreach (var f in evo1.Evolutions.Where(z => z.SpeciesInternal is not 0)) | |
{ | |
var evoS2 = f.SpeciesInternal; | |
var evoF2 = f.Form; | |
var evo2 = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == evoS2 && z.Info.Form == evoF2); | |
evo2.Type1 = evo1.Type1; | |
evo2.Type2 = evo1.Type2; | |
// give a 40% chance at gaining a second type upon second evolution | |
if (evo2.Type1 == evo2.Type2 && GainNewTypeSecond) | |
{ | |
while (GetRandom(MAX_TYPE_ID + 1) != evo1.Type1) | |
evo2.Type2 = (byte)GetRandom(MAX_TYPE_ID + 1); | |
} | |
} | |
} | |
} | |
// Gimmighoul edge case | |
var gimmi = ordered.FirstOrDefault(z => z.Info.SpeciesNational is 0999 && z.Info.Form is 0); | |
var ghold = ordered.FirstOrDefault(z => z.Info.SpeciesNational is 1000); | |
gimmi.Type1 = ghold.Type1; | |
gimmi.Type2 = ghold.Type2; | |
} | |
else | |
{ | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
byte type1 = (byte)GetRandom(MAX_TYPE_ID + 1); | |
byte type2 = type1; | |
// give a 40% chance at dual-type | |
if (GetRandom(100) < 40) | |
{ | |
while (GetRandom(MAX_TYPE_ID + 1) != type1) | |
type2 = (byte)GetRandom(MAX_TYPE_ID + 1); | |
} | |
t.Type1 = type1; | |
t.Type2 = type2; | |
} | |
} | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
if (t.Info.SpeciesNational is 1007 or 1008 && t.Info.Form is not 0 || t.Info.SpeciesNational is 1017 && t.Info.Form >= 4) | |
continue; | |
string form = t.Info.Form is 0 ? string.Empty : $"-{t.Info.Form}"; | |
string name = $"{t.Info.SpeciesNational:0000} {species[t.Info.SpeciesInternal]}{form}"; | |
string type = t.Type1 == t.Type2 ? $"{types[t.Type1]}" : $"{types[t.Type1]} / {types[t.Type2]}"; | |
RandomizerLog.Add($"{name} ({type})"); | |
} | |
RandomizerLog.Add(""); | |
} | |
if (RANDOMIZE_PERSONAL_EVOLUTION) | |
{ | |
"Randomizing Pokémon evolutions...".Dump(); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
t.EXPGrowth = 5; // standardize growth rates to Slow | |
var evos = t.Evolutions; | |
foreach (var evo in evos.Where(z => z.SpeciesInternal is not 0)) | |
{ | |
evo.SpeciesInternal = (ushort)GetRandomSpecies(0); | |
evo.Form = GetRandomForm((DevID)evo.SpeciesInternal); | |
} | |
} | |
} | |
if (MAKE_POKEMON_EVOLVE_EVERY_LEVEL) | |
{ | |
"Making Pokémon evolve every level...".Dump(); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
t.EXPGrowth = 5; | |
var evos = t.Evolutions; | |
DevID s = GetRandomSpecies(0); | |
byte f = GetRandomForm(s); | |
PersonalInfoEvolution evo = new() | |
{ | |
Level = 1, | |
Method = (int)EvolutionType.LevelUp, | |
Argument = 0, | |
SpeciesInternal = (ushort)s, | |
Form = f, | |
}; | |
evos.Clear(); | |
evos.Add(evo); | |
} | |
} | |
if (RANDOMIZE_PERSONAL_EVOLUTION || MAKE_POKEMON_EVOLVE_EVERY_LEVEL) | |
{ | |
RandomizerLog.Add("== POKÉMON EVOLUTIONS =="); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
var evos = t.Evolutions; | |
foreach (var e in evos) | |
{ | |
if (t.Info.SpeciesNational is 1007 or 1008 && t.Info.Form is not 0 || t.Info.SpeciesNational is 1017 && t.Info.Form >= 4) | |
continue; | |
string form = t.Info.Form is 0 ? string.Empty : $"-{t.Info.Form}"; | |
string formE = e.Form is 0 ? string.Empty : $"-{e.Form}"; | |
string name = $"{t.Info.SpeciesNational:0000} [{evos.IndexOf(e)}] {species[t.Info.SpeciesInternal]}{form}"; | |
string evol = $"Evolves into {species[e.SpeciesInternal]}{formE}"; | |
RandomizerLog.Add($"{name} - {evol}"); | |
} | |
} | |
RandomizerLog.Add(""); | |
} | |
if (RANDOMIZE_PERSONAL_LEARNSET && !ALL_POKEMON_ONLY_KNOW_METRONOME) | |
{ | |
RandomizerLog.Add("== POKÉMON LEARNSETS =="); | |
"Randomizing Pokémon learnsets...".Dump(); | |
List<int> PokemonWithImportantMoves = | |
[ | |
0057, // Primeape | |
0190, // Aipom | |
0193, // Yanma | |
0203, // Girafarig | |
0206, // Dunsparce | |
0211, // Qwilfish | |
0221, // Piloswine | |
0438, // Bonsly | |
0647, // Keldeo | |
0648, // Meloetta | |
0762, // Steenee | |
]; | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
IList<PersonalInfoMove> Learnset = t.Learnset; | |
IList<ushort> Reminder = t.ReminderMoves; | |
List<int> Moves = GetRandomMoveList(); | |
int ctr = 0; | |
// level up | |
for (int i = 0; i < Learnset.Count; i++) | |
{ | |
if (t.Info.SpeciesNational is 0234 && Learnset[i].Move is 828) // keep Psyshield Bash for Stantler | |
continue; | |
Learnset[i].Move = (ushort)Moves[i]; | |
ctr++; | |
} | |
// reminder | |
for (int i = 0; i < Reminder.Count; i++) | |
{ | |
if (t.Info.SpeciesNational is 1011 && Reminder[i] is 913) // keep Dragon Cheer for Dipplin | |
continue; | |
Reminder[i] = (ushort)Moves[ctr]; | |
ctr++; | |
} | |
if (GUARANTEE_FOUR_MOVES_AT_LEVEL_ONE && Learnset.Count(z => z.Level is 1) is < 4) | |
{ | |
while (Learnset.Count(z => z.Level is 1) is < 4) | |
{ | |
Learnset.Add(new PersonalInfoMove { Move = (ushort)Moves[ctr], Level = 1 }); | |
ctr++; | |
} | |
} | |
// add evolution moves and form changing moves to reminders, now that they may be inaccessible | |
if (PokemonWithImportantMoves.Contains(t.Info.SpeciesNational)) | |
{ | |
// only Qwilfish-1 can evolve | |
if (t.Info.SpeciesNational is 0211 && t.Info.Form is not 1) | |
continue; | |
ushort move = t.Info.SpeciesNational switch | |
{ | |
0057 => 889, // Rage Fist | |
0193 or 0221 => 246, // Ancient Power | |
0190 or 0438 => 102, // Mimic | |
0203 => 888, // Twin Beam | |
0206 => 887, // Hyper Drill | |
0211 => 839, // Barb Barrage | |
0647 => 548, // Secret Sword | |
0648 => 547, // Relic Song | |
0762 => 023, // Stomp | |
_ => throw new ArgumentException($"Invalid species: {t.Info.SpeciesNational}"), | |
}; | |
Reminder.Add(move); | |
} | |
t.Learnset = t.Learnset.OrderBy(z => z.Level).ToList(); | |
string form = t.Info.Form is 0 ? string.Empty : $"-{t.Info.Form}"; | |
string name = $"{t.Info.SpeciesNational:0000} {species[t.Info.SpeciesInternal]}{form}"; | |
if (t.Info.SpeciesNational is 1007 or 1008 && t.Info.Form is not 0 || t.Info.SpeciesNational is 1017 && t.Info.Form >= 4) | |
continue; | |
RandomizerLog.Add(name); | |
foreach (var r in t.ReminderMoves) | |
RandomizerLog.Add($"- [000] {moves[r]}"); | |
foreach (var m in t.Learnset) | |
{ | |
int level = m.Level is -3 ? 0 : m.Level; | |
RandomizerLog.Add($"- [{level:000}] {moves[m.Move]}"); | |
} | |
RandomizerLog.Add(""); | |
} | |
} | |
if (ALL_POKEMON_ONLY_KNOW_METRONOME) | |
{ | |
"Modifying all learnsets to only include Metronome...".Dump(); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
t.TechnicalMachine.Clear(); | |
t.EggMoves.Clear(); | |
t.ReminderMoves.Clear(); | |
t.Learnset.Clear(); | |
PersonalInfoMove Metronome = new() { Move = 118, Level = 1 }; | |
t.Learnset.Add(Metronome); | |
} | |
} | |
if (RANDOMIZE_PERSONAL_BASE_STATS_WITHIN_BST) | |
{ | |
"Randomizing Pokémon base stats (within BST)...".Dump(); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
List<int> BaseStats = [ t.Base.HP, t.Base.ATK, t.Base.DEF, t.Base.SPA, t.Base.SPD, t.Base.SPE ]; | |
List<float> Weighted = new(); | |
List<int> Final = new(); | |
int BaseStatTotal = BaseStats.Sum(); | |
for (int i = 0; i < 6; i++) | |
Weighted.Add(GetRandom(5, BaseStats[i])); | |
float factor = Weighted.Sum() / BaseStatTotal; | |
for (int i = 0; i < 6; i++) | |
Final.Add((byte)(Weighted[i] /= factor)); | |
// throw in some extra points to meet the original BST | |
if (Final.Sum() < BaseStatTotal) | |
{ | |
int diff = BaseStatTotal - Final.Sum(); | |
for (int i = 0; i < diff; i++) | |
{ | |
int index = GetRandom(Final.Count); | |
Final[index]++; | |
} | |
} | |
t.Base.HP = (byte)Final[0]; | |
t.Base.ATK = (byte)Final[1]; | |
t.Base.DEF = (byte)Final[2]; | |
t.Base.SPA = (byte)Final[3]; | |
t.Base.SPD = (byte)Final[4]; | |
t.Base.SPE = (byte)Final[5]; | |
} | |
} | |
else if (RANDOMIZE_PERSONAL_BASE_STATS_COMPLETELY_RANDOM) | |
{ | |
"Randomizing Pokémon base stats (completely random)...".Dump(); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
t.Base.HP = (byte)GetRandom(5, 256); | |
t.Base.ATK = (byte)GetRandom(5, 256); | |
t.Base.DEF = (byte)GetRandom(5, 256); | |
t.Base.SPA = (byte)GetRandom(5, 256); | |
t.Base.SPD = (byte)GetRandom(5, 256); | |
t.Base.SPE = (byte)GetRandom(5, 256); | |
} | |
} | |
else if (SHUFFLE_PERSONAL_BASE_STATS) | |
{ | |
"Shuffling Pokémon base stats...".Dump(); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
List<byte> BaseStats = [ t.Base.HP, t.Base.ATK, t.Base.DEF, t.Base.SPA, t.Base.SPD, t.Base.SPE ]; | |
pkNX.Randomization.Util.Shuffle(BaseStats); | |
t.Base.HP = BaseStats[0]; | |
t.Base.ATK = BaseStats[1]; | |
t.Base.DEF = BaseStats[2]; | |
t.Base.SPA = BaseStats[3]; | |
t.Base.SPD = BaseStats[4]; | |
t.Base.SPE = BaseStats[5]; | |
} | |
} | |
if (RANDOMIZE_PERSONAL_BASE_STATS_WITHIN_BST || RANDOMIZE_PERSONAL_BASE_STATS_COMPLETELY_RANDOM || SHUFFLE_PERSONAL_BASE_STATS) | |
{ | |
RandomizerLog.Add("== POKÉMON BASE STATS =="); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
{ | |
if (t.Info.SpeciesNational is 1007 or 1008 && t.Info.Form is not 0 || t.Info.SpeciesNational is 1017 && t.Info.Form >= 4) | |
continue; | |
string form = t.Info.Form is 0 ? string.Empty : $"-{t.Info.Form}"; | |
string name = $"{t.Info.SpeciesNational:0000} {species[t.Info.SpeciesInternal]}{form}"; | |
int total = t.Base.HP + t.Base.ATK + t.Base.DEF + t.Base.SPA + t.Base.SPD + t.Base.SPE; | |
string stats = $"{t.Base.HP}/{t.Base.ATK}/{t.Base.DEF}/{t.Base.SPA}/{t.Base.SPD}/{t.Base.SPE} (BST: {total})"; | |
RandomizerLog.Add($"{name} - {stats}"); | |
} | |
RandomizerLog.Add(""); | |
} | |
if (REMOVE_EFFORT_VALUE_YIELDS) | |
{ | |
"Removing EV yields...".Dump(); | |
foreach (var t in ordered.Where(z => z.IsPresentInGame)) | |
t.EVYield = new PersonalInfoStats { HP = 0, ATK = 0, DEF = 0, SPA = 0, SPD = 0, SPE = 0 }; | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static PersonalTable AddRegionalFormEvolutions(PersonalTable obj) | |
{ | |
List<int> Alola = [ 0025, 0102 ]; | |
List<int> Galar = [ 0109 ]; | |
List<int> Hisui = [ 0156, 0502, 0548, 0627, 0704, 0712, 0723 ]; | |
List<int> RegionalForms = Alola.Concat(Galar).Concat(Hisui).ToList(); | |
foreach (int t in RegionalForms) | |
{ | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesNational == t); | |
PersonalInfoEvolution evo = new() | |
{ | |
Level = 0, | |
Method = (int)EvolutionType.UseItem, | |
Argument = 57, | |
SpeciesInternal = (ushort)(t + 1), | |
Form = 1, | |
}; | |
sel.Evolutions.Add(evo); | |
} | |
return obj; | |
} | |
private static void RandomizeMoveProperties() | |
{ | |
const string file = "waza_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "avalon/data/"); | |
string path = Path.Combine(PATH_MASTER, "avalon/data/"); | |
var obj = FlatBufferConverter.DeserializeFrom<WazaTable>(Path.Combine(path, file)); | |
List<byte> PP = [ 5, 10, 15, 20, 25, 30, 35, 40 ]; | |
string[] moves = GetStringsCommon(LANGUAGE, "wazaname"); | |
string[] types = GetStringsCommon(LANGUAGE, "typename"); | |
bool IsNotStatusMove(byte category) => category is not 0; | |
string localePP = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_skill_01"), LANGUAGE); | |
string localeCategory = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_skill_category_00"), LANGUAGE); | |
string localePower = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_skill_power_00"), LANGUAGE); | |
string localeAccuracy = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_skill_hit_00"), LANGUAGE); | |
string localeType = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_pokemon_type_00"), LANGUAGE); | |
string[][] Categories = | |
[ | |
[ "Status", "Physical", "Special" ], | |
[ "Statut", "Physique", "Spéciale" ], | |
[ "Status", "Physische", "Spezial" ], | |
[ "Stato", "Fisica", "Speciale" ], | |
[ "変化", "物理", "特殊" ], | |
[ "変化", "物理", "特殊" ], | |
[ "변화", "물리", "특수" ], | |
[ "變化", "物理", "特殊" ], | |
[ "Estado", "Físico", "Especial" ], | |
[ "變化", "物理", "特殊" ], | |
]; | |
if (RANDOMIZE_MOVE_PROPERTIES_TYPE || RANDOMIZE_MOVE_PROPERTIES_POWER || RANDOMIZE_MOVE_PROPERTIES_ACCURACY || RANDOMIZE_MOVE_PROPERTIES_PP) | |
{ | |
RandomizerLog.Add("== MOVE PROPERTIES =="); | |
"Randomizing move properties...".Dump(); | |
foreach (var t in obj.Table.Where(z => z.CanUseMove && z.MoveID is not 0)) | |
{ | |
if (RANDOMIZE_MOVE_PROPERTIES_TYPE) | |
t.Type = (byte)GetRandom(MAX_TYPE_ID + 1); | |
if (RANDOMIZE_MOVE_PROPERTIES_POWER && IsNotStatusMove(t.Category) && t.Power is not (0 or 1)) | |
t.Power = GetRandomMovePower(); | |
if (RANDOMIZE_MOVE_PROPERTIES_ACCURACY && IsNotStatusMove(t.Category)) | |
t.Accuracy = GetRandomMoveAccuracy(); | |
if (RANDOMIZE_MOVE_PROPERTIES_CATEGORY && IsNotStatusMove(t.Category)) | |
t.Category = (byte)GetRandom(1, 3); | |
if (RANDOMIZE_MOVE_PROPERTIES_PP) | |
t.PP = PP[GetRandom(PP.Count)]; | |
} | |
foreach (var t in obj.Table.Where(z => z.CanUseMove && z.MoveID is not 0)) | |
{ | |
string type = $"- {localeType}: {types[(int)t.Type]}"; | |
string category = $"- {localeCategory}: {Categories[Languages.IndexOf(LANGUAGE)][t.Category]}"; | |
string power = $"- {localePower}: {t.Power}"; | |
string accuracy = $"- {localeAccuracy}: {t.Accuracy}"; | |
string pp = $"- {localePP}: {t.PP}"; | |
RandomizerLog.Add($"{moves[t.MoveID]}"); | |
RandomizerLog.Add(type); | |
RandomizerLog.Add(category); | |
RandomizerLog.Add(power); | |
RandomizerLog.Add(accuracy); | |
RandomizerLog.Add(pp); | |
RandomizerLog.Add(""); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
} | |
private static byte GetRandomMovePower() => GetRandom(100) switch | |
{ | |
<= 99 and >= 65 => 40, // 35% | |
<= 64 and >= 40 => 60, // 25% | |
<= 39 and >= 20 => 80, // 20% | |
<= 19 and >= 10 => 100, // 10% | |
<= 9 and >= 5 => 120, // 5% | |
<= 4 and >= 2 => 20, // 3% | |
1 => 10, // 1% | |
_ => 150, // 1% | |
}; | |
private static byte GetRandomMoveAccuracy() => GetRandom(100) switch | |
{ | |
<= 99 and >= 50 => 100, // 50% | |
<= 49 and >= 30 => 90, // 20% | |
<= 29 and >= 20 => 80, // 10% | |
<= 19 and >= 10 => 70, // 10% | |
<= 9 and >= 5 => 50, // 5% | |
<= 4 and >= 2 => 30, // 3% | |
1 => 10, // 1% | |
_ => 101, // 1% | |
}; | |
private static void RandomizeTradeEncounters() | |
{ | |
RandomizerLog.Add("== IN-GAME TRADES =="); | |
"Randomizing in-game trades...".Dump(); | |
const string file = "eventTradePokemon_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/event/eventTradePokemon/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/event/eventTradePokemon/"); | |
var obj = FlatBufferConverter.DeserializeFrom<EventTradePokemonArray>(Path.Combine(path, file)); | |
string[] names = GetStringsCommon(LANGUAGE, "monsname"); | |
List<string> Coaches = | |
[ | |
string.Empty, | |
string.Empty, | |
"bb4kings_honoo", | |
"gym_nomal", | |
"paldea_botan", | |
"paldea4kings_zimen", | |
"teacher_head", | |
"gym_koori", | |
"gym_mizu", | |
"teacher_dragon", | |
"gym_mushi", | |
"bb4kings_dragon", | |
"teacher_battle", | |
"gym_kusa", | |
"teacher_health", | |
"gym_denki", | |
"paldea_rival", | |
"bb4kings_hagane", | |
"paldea_top", | |
"paldea_friend", | |
"paldea4kings_hagane", | |
"gym_ghost", | |
"teacher_history", | |
"gym_esper", | |
"teacher_home", | |
"teacher_language", | |
"bbteacher_head", | |
"bb_brother", | |
"bb4kings_fairy", | |
"teacher_math", | |
"bb_sister", | |
"teacher_ecology", | |
string.Empty, | |
]; | |
bool IsBlueberryTrade(string id) => id is not ("trade_c02_0194" or "trade_c03_0093" or "trade_t03_0872"); | |
for (int i = 0; i < obj.Table.Count; i++) | |
{ | |
var t = obj.Table[i].PokeData; | |
DevID oldSpecies = t.DevId; | |
short oldForm = t.FormId; | |
t.DevId = GetRandomSpecies(t.DevId); | |
t.FormId = GetRandomForm(t.DevId); | |
t.Sex = GetRandomGender(t.DevId, t.FormId); | |
t.Tokusei = (TokuseiType)GetRandom(2, 5); | |
t.GemType = GetTeraType(t.DevId, (byte)t.FormId, t.GemType, false); | |
t.RareType = RareType.DEFAULT; | |
t.Seikaku = t.SeikakuHosei = SeikakuType.DEFAULT; | |
t.WazaType = WazaType.DEFAULT; | |
DevID newSpecies = t.DevId; | |
if (GIVE_POKEMON_RANDOM_POKE_BALLS) | |
t.BallId = (BallType)GetRandom(1, MAX_BALL_ID + 1); | |
if (GIVE_POKEMON_RANDOM_RIBBONS) | |
t.SetRibbon = (RibbonType)GetRandom(1, MAX_RIBBON_ID + 1); | |
if (MODIFY_POKEMON_LEVELS) | |
t.Level = Math.Clamp((int)(t.Level * (1.0f + ((float)LEVEL_MODIFIER_PERCENT / 100.0f))), 1, 100); | |
if (GIVE_POKEMON_HELD_ITEMS) | |
t.Item = GetRandomItem(); | |
if (ENSURE_SANE_FORM_HELD_ITEMS && IsFormHeldItemDependent(t.DevId)) | |
t.Item = GetFormSpecificItem(t.DevId, t.FormId); | |
if (IsBlueberryTrade(obj.Table[i].Label)) | |
{ | |
t.UseNickName = false; | |
FixTradeTextBlueberry(Coaches[i], (ushort)oldSpecies, (ushort)newSpecies); | |
} | |
string oForm = oldForm is 0 ? string.Empty : $"-{oldForm}"; | |
string nForm = t.FormId is 0 ? string.Empty : $"-{t.FormId}"; | |
RandomizerLog.Add($"{names[(int)oldSpecies]}{oForm} -> {names[(int)t.DevId]}{nForm}"); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
RandomizeTradeRequirements(obj); | |
} | |
private static void RandomizeTradeRequirements(EventTradePokemonArray give) | |
{ | |
RandomizerLog.Add("== IN-GAME TRADE REQUIREMENTS =="); | |
const string file = "eventTradeList_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/event/eventTradeList/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/event/eventTradeList/"); | |
var obj = FlatBufferConverter.DeserializeFrom<EventTradeListArray>(Path.Combine(path, file)); | |
string[] names = GetStringsCommon(LANGUAGE, "monsname"); | |
for (int i = 0; i < obj.Table.Count; i++) | |
{ | |
var t = obj.Table[i]; | |
t.SendPokeDevId = GetRandomSpecies(t.SendPokeDevId); | |
t.SendPokeFormId = -1; // be permissive, allow any form | |
} | |
var give1 = give.Table[00].PokeData; | |
var give2 = give.Table[01].PokeData; | |
var give3 = give.Table[32].PokeData; | |
var take1 = obj.Table[00]; | |
var take2 = obj.Table[01]; | |
var take3 = obj.Table[02]; | |
string GetForm(byte form) => form is 0 ? string.Empty : $"-{form}"; | |
List<string> Trainers = | |
[ | |
GetStringFromAHTBScript("field_trade", give1.ParentNameLabel, LANGUAGE), // Sue | |
GetStringFromAHTBScript("field_trade", give2.ParentNameLabel, LANGUAGE), // Blossom | |
GetStringFromAHTBScript("field_trade", give3.ParentNameLabel, LANGUAGE), // Glen | |
]; | |
RandomizerLog.Add($"- {Trainers[0]}: Trade {names[(int)take1.SendPokeDevId]} for {names[(int)give1.DevId]}{GetForm((byte)give1.FormId)}"); | |
RandomizerLog.Add($"- {Trainers[1]}: Trade {names[(int)take2.SendPokeDevId]} for {names[(int)give2.DevId]}{GetForm((byte)give2.FormId)}"); | |
RandomizerLog.Add($"- {Trainers[2]}: Trade {names[(int)take3.SendPokeDevId]} for {names[(int)give3.DevId]}{GetForm((byte)give3.FormId)}"); | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
FixTradeTextPaldea(give, obj); | |
} | |
private static void FixTradeTextPaldea(EventTradePokemonArray give, EventTradeListArray take) | |
{ | |
var give1 = (int)give.Table[00].PokeData.DevId; | |
var give2 = (int)give.Table[01].PokeData.DevId; | |
var give3 = (int)give.Table[32].PokeData.DevId; | |
var take1 = (int)take.Table[00].SendPokeDevId; | |
var take2 = (int)take.Table[01].SendPokeDevId; | |
var take3 = (int)take.Table[02].SendPokeDevId; | |
foreach (string lang in Languages) | |
{ | |
string dest = Path.Combine(DEST_MASTER, $"message/dat/{lang}/script/"); | |
string[] lines = GetStringsScript(lang, "field_trade"); | |
string[] names = GetStringsCommon(lang, "monsname"); | |
TextConfig cfg = new(GameVersion.SV); | |
string TRADE_A = names[0194]; // Wooper | |
string TRADE_B_1 = names[0093]; // Haunter | |
string TRADE_B_2 = names[0871]; // Pincurchin | |
string TRADE_C_1 = names[0872]; // Snom | |
string TRADE_C_2 = names[0669]; // Flabébé | |
// special handling for Wooper -> Wooper | |
List<int> WooperStrings = new(); | |
int index = lines[09].IndexOf(TRADE_A); | |
while (index is not -1) | |
{ | |
WooperStrings.Add(index); | |
index = lines[09].IndexOf(TRADE_A, index + 1); | |
} | |
if (lang is "English") | |
lines[09] = lines[09].Remove(WooperStrings[2], TRADE_A.Length).Insert(WooperStrings[2], names[give1]); | |
if (lang is not "Italian") | |
lines[09] = lines[09].Remove(WooperStrings[1], TRADE_A.Length).Insert(WooperStrings[1], names[take1]); | |
lines[09] = lines[09].Remove(WooperStrings[0], TRADE_A.Length).Insert(WooperStrings[0], names[give1]); | |
lines[10] = lines[10].Replace(TRADE_A, names[give1]); | |
lines[11] = lines[11].Replace(TRADE_A, names[give1]); | |
lines[12] = lines[12].Replace(TRADE_A, names[take1]); | |
lines[13] = lines[13].Replace(TRADE_A, names[give1]); | |
for (int t = 0; t < lines.Length; t++) | |
{ | |
lines[t] = lines[t].Replace(TRADE_B_1, names[take2]).Replace(TRADE_B_2, names[give2]); | |
lines[t] = lines[t].Replace(TRADE_C_1, names[take3]).Replace(TRADE_C_2, names[give3]); | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] write = TextFile.GetBytes(lines, cfg); | |
File.WriteAllBytes(Path.Combine(dest, "field_trade.dat"), write); | |
} | |
} | |
private static void FixTradeTextBlueberry(string coach, int oldSpecies, int newSpecies) | |
{ | |
List<string> Botan = [ "Veevee", "Volivoli", "Evoli", "Vivì", "のブイを", "のブイを", "브이브이", "布布", "Vivi", "布布" ]; | |
List<string> Ghost = [ "DJ G-Rave", "Toutombe", "Gruff", "DJ G-Rave", "DJ BOCHI", "DJ BOCHI", "DJ MANGMANG-E", "DJ MUZAIGOU", "DJ Nitxo", "DJ GHOSTDOG" ]; | |
for (int i = 0; i < Languages.Count; i++) | |
{ | |
string lang = Languages[i]; | |
string dest = Path.Combine(DEST_MASTER, $"message/dat/{lang}/script/"); | |
string text = coach is "teacher_history" or "bbteacher_head" ? "trade_01_02" : "trade_01_03"; | |
ulong hash = FnvHash.HashFnv1a_64($"coach_{coach}_{text}"); | |
string[] names = GetStringsCommon(lang, "monsname"); | |
string[] lines = GetStringsScript(lang, $"coach_{coach}"); | |
byte[] data = File.ReadAllBytes(Path.Combine(PATH_MASTER, $"message/dat/{lang}/script/coach_{coach}.tbl")); | |
List<AHTBEntry> Entries = new AHTB(data).Entries.ToList(); | |
AHTBEntry target = Entries.FirstOrDefault(z => z.Hash == hash); | |
int index = Entries.IndexOf(target); | |
string species = coach switch | |
{ | |
"paldea_botan" => Botan[i], // Veevee | |
"gym_ghost" => Ghost[i], // DJ G-Rave | |
_ => names[oldSpecies], | |
}; | |
string line = lines[index].Replace(species, names[newSpecies]); | |
lines[index] = line; | |
} | |
} | |
private static void RandomizeAcademyAceTournamentRewards() | |
{ | |
RandomizerLog.Add("== ACADEMY ACE TOURNAMENT REWARDS =="); | |
"Randomizing Academy Ace Tournament item rewards...".Dump(); | |
const string file = "sub_012_reward_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/event/sub_012/sub_012_reward/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/event/sub_012/sub_012_reward/"); | |
var obj = FlatBufferConverter.DeserializeFrom<RewardTableArray>(Path.Combine(path, file)); | |
string[] items = GetStringsCommon(LANGUAGE, "itemname"); | |
float totalRate = 0; | |
for (int i = 0; i < obj.Table.Count; i++) | |
totalRate += obj.Table[i].LotteryWeight; | |
var ordered = obj.Table.OrderByDescending(z => z.LotteryWeight); | |
foreach (var t in ordered.Where(z => z.ItemId is not 0)) | |
{ | |
t.ItemId = GetRandomItem(); | |
float rate = (float)(Math.Round((t.LotteryWeight / totalRate) * 100f)); | |
RandomizerLog.Add($"- {rate}% {items[(int)t.ItemId]} (×{t.ItemNum})"); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizePokedexMilestoneRewards() | |
{ | |
"Randomizing Pokédex milestone rewards...".Dump(); | |
List<string> RewardData = | |
[ | |
"world/data/ui/pokedex/reward_data/reward_data_array.bin", | |
"world/data/ui/pokedex/reward_data_dlc1/reward_data_dlc1_array.bin", | |
"world/data/ui/pokedex/reward_data_dlc2/reward_data_dlc2_array.bin", | |
]; | |
string[] items = GetStringsCommon(LANGUAGE, "itemname"); | |
foreach (string r in RewardData) | |
{ | |
int index = r.LastIndexOf("/") + 1; | |
string f = r[index..]; | |
string d = r[..index]; | |
string dex = f switch | |
{ | |
"reward_data_array.bin" => "PALDEA", | |
"reward_data_dlc1_array.bin" => "KITAKAMI", | |
"reward_data_dlc2_array.bin" => "BLUEBERRY", | |
_ => throw new ArgumentException($"Invalid file: {f}"), | |
}; | |
string file = Path.Combine(PATH_MASTER, d, f); | |
var obj = FlatBufferConverter.DeserializeFrom<RewardDataArray>(file); | |
// since we expanded the Paldea Pokédex, we need to adjust the milestones to account for 733 available species | |
// unfortunately, the game cuts off milestones in sets of 40, so each page can display 4 milestones | |
// because of this, we need to stop at 720 total captured, which is only 13 away from the total, so it's not a big deal | |
if (f is "reward_data_array.bin") | |
{ | |
int GetItemQuantity(int num) => num switch | |
{ | |
>= 130 and <= 240 => 3, | |
>= 250 and <= 360 => 5, | |
>= 370 and <= 480 => 8, | |
>= 490 and <= 600 => 10, | |
>= 610 and <= 710 => 15, | |
_ => 1, | |
}; | |
obj.Table.Clear(); | |
for (int i = 10; i <= 710; i += 10) | |
{ | |
RewardData Reward = new() | |
{ | |
PokedexType = PokedexType.TITAN, | |
CaptureNum = i, | |
ItemId = GetRandomItem(), | |
ItemNum = GetItemQuantity(i), | |
}; | |
obj.Table.Add(Reward); | |
} | |
// for the final reward, award 100 of a random valuable item, but if it's Gimmighoul Coins, give 999 of them | |
ItemID item = (ItemID)ValuableItems[GetRandom(ValuableItems.Count)]; | |
int num = item is ItemID.ITEMID_SOZAI182 ? 999 : 100; | |
RewardData FinalReward = new() | |
{ | |
PokedexType = PokedexType.TITAN, | |
CaptureNum = 720, | |
ItemId = item, | |
ItemNum = num, | |
}; | |
obj.Table.Add(FinalReward); | |
} | |
else | |
{ | |
foreach (var t in obj.Table) | |
{ | |
t.ItemId = GetRandomItem(); | |
t.ItemNum = 1; | |
} | |
} | |
RandomizerLog.Add($"== POKÉDEX MILESTONE REWARDS ({dex}) =="); | |
foreach (var t in obj.Table) | |
RandomizerLog.Add($"- {t.CaptureNum} Captured: {items[(int)t.ItemId]} (×{t.ItemNum})"); | |
RandomizerLog.Add(""); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
string dest = Path.Combine(DEST_MASTER, d); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
File.WriteAllBytes(Path.Combine(dest, f), data); | |
} | |
RandomizerLog.Add(""); | |
} | |
private static void RandomizeStarBarrageEncounters() | |
{ | |
RandomizerLog.Add("== TEAM STAR BASES: STAR BARRAGE =="); | |
"Randomizing Star Barrage encounters...".Dump(); | |
const string file = "AjitoPokemon_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/ajito/AjitoPokemon/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/ajito/AjitoPokemon/"); | |
var obj = FlatBufferConverter.DeserializeFrom<AjitoPokemonArray>(Path.Combine(path, file)); | |
string[] names = GetStringsCommon(LANGUAGE, "monsname"); | |
foreach (var enc in obj.Table) | |
{ | |
var t = enc.Table; | |
var oldDev = t.DevId; | |
var oldForm = t.FormNo; | |
t.DevId = GetRandomSpecies(t.DevId); | |
t.FormNo = GetRandomForm(t.DevId); | |
string oForm = oldForm is 0 ? string.Empty : $"-{oldForm}"; | |
string nForm = t.FormNo is 0 ? string.Empty : $"-{t.FormNo}"; | |
RandomizerLog.Add($"{names[(int)oldDev]}{oForm} -> {names[(int)t.DevId]}{nForm}"); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizeStarmobileStats() | |
{ | |
RandomizerLog.Add("== STARMOBILE STATS =="); | |
"Randomizing Starmobile stats...".Dump(); | |
const string file = "dan_car_battle_data_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/danbattle/boss/dan_car_battle_data/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/danbattle/boss/dan_car_battle_data/"); | |
var obj = FlatBufferConverter.DeserializeFrom<DanCarBattleDataArray>(Path.Combine(path, file)); | |
string[] cars = GetStringsScript(LANGUAGE, "futatsuna"); | |
string[] types = GetStringsCommon(LANGUAGE, "typename"); | |
string[] abils = GetStringsCommon(LANGUAGE, "tokusei"); | |
string[] moves = GetStringsCommon(LANGUAGE, "wazaname"); | |
string localeType = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_pokemon_type_00"), LANGUAGE); | |
string localeAbility = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_pokemon_chara_00"), LANGUAGE); | |
string localeMove = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_skill_05"), LANGUAGE); | |
List<string> Level = [ "Level", "Niveau", "Level", "Livello", "Lv", "Lv", "레벨", "等级", "Nivel", "等級" ]; | |
List<string> StarmobileNames = cars[18..23].ToList(); | |
for (int i = 0; i < obj.Table.Count; i++) | |
{ | |
List<int> Moves = GetRandomMoveList(); | |
var t = obj.Table[i].DanBossCarBattleStruct; | |
t.Waza1 = (WazaID)Moves[0]; | |
t.Waza2 = (WazaID)Moves[1]; | |
t.Waza3 = (WazaID)Moves[2]; | |
t.Waza4 = (WazaID)Moves[3]; | |
t.TokuseiId = (TokuseiID)GetRandomAbility(); | |
t.Type1 = t.Type2 = (MoveType)GetRandom(MAX_TYPE_ID + 1); | |
if (MODIFY_POKEMON_LEVELS) | |
t.Level = Math.Clamp((int)(t.Level * (1.0f + ((float)LEVEL_MODIFIER_PERCENT / 100.0f))), 1, 100); | |
RandomizerLog.Add(StarmobileNames[i]); | |
RandomizerLog.Add($"{Level[Languages.IndexOf(LANGUAGE)]}: {t.Level}"); | |
RandomizerLog.Add($"{localeType}: {types[(int)t.Type1]}"); | |
RandomizerLog.Add($"{localeAbility}: {abils[(int)t.TokuseiId]}"); | |
RandomizerLog.Add($"{localeMove}:"); | |
RandomizerLog.Add($"- {moves[(int)t.Waza1]}"); | |
RandomizerLog.Add($"- {moves[(int)t.Waza2]}"); | |
RandomizerLog.Add($"- {moves[(int)t.Waza3]}"); | |
RandomizerLog.Add($"- {moves[(int)t.Waza4]}"); | |
RandomizerLog.Add(""); | |
} | |
FixStarmobileText(obj); | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void FixStarmobileText(DanCarBattleDataArray obj) | |
{ | |
TextConfig cfg = new(GameVersion.SV); | |
foreach (string lang in Languages) | |
{ | |
byte[] source = File.ReadAllBytes(Path.Combine(DEST_MASTER, $"message/dat/{lang}/script/futatsuna.dat")); | |
string[] bosses = new TextFile(source, cfg).Lines; | |
string[] types = GetStringsCommon(lang, "typename"); | |
string[] cars = bosses[18..23]; | |
string localeType = GetStringFromAHTBCommon("status", FnvHash.HashFnv1a_64("msg_ui_status_pokemon_type_00"), LANGUAGE); | |
for (int i = 0; i < obj.Table.Count; i++) | |
{ | |
var t = obj.Table[i].DanBossCarBattleStruct; | |
string car = cars[i]; | |
string type = types[(int)t.Type1]; | |
string line = $"{car} ({localeType}: {type})"; | |
bosses[18 + i] = line; | |
} | |
string dest = Path.Combine(DEST_MASTER, $"message/dat/{lang}/script/"); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] write = TextFile.GetBytes(bosses, cfg); | |
File.WriteAllBytes(Path.Combine(dest, "futatsuna.dat"), write); | |
} | |
} | |
private static void RandomizeOgreOustinRewards() | |
{ | |
RandomizerLog.Add("== OGRE OUSTIN' ITEM REWARDS =="); | |
"Randomizing Ogre Oustin' item rewards...".Dump(); | |
const string file = "reward_level_param_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/oniballoon/reward_level_param/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/oniballoon/reward_level_param/"); | |
var obj = FlatBufferConverter.DeserializeFrom<pkNX.Structures.FlatBuffers.SV.Balloon.RewardTableArray>(Path.Combine(path, file)); | |
string[] items = GetStringsCommon(LANGUAGE, "itemname"); | |
foreach (var t in obj.Table) | |
{ | |
foreach (var item in t.Param.RewardItems.Where(z => z.ItemId is not 0)) | |
{ | |
item.ItemId = GetRandomItem(); | |
RandomizerLog.Add($"- {items[(int)item.ItemId]} (×{item.Num})"); | |
} | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizeSpecialCoachRewards() | |
{ | |
RandomizerLog.Add("== SPECIAL COACH ITEM REWARDS =="); | |
"Randomizing Special Coach item rewards...".Dump(); | |
const string file = "ClubNpcRewardList_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/club/ClubNpcRewardList/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/club/ClubNpcRewardList/"); | |
var obj = FlatBufferConverter.DeserializeFrom<ClubNpcRewardListArray>(Path.Combine(path, file)); | |
List<int> Coaches = [ 015, 013, 009, 014, 007, 011, 010, 012, 008, 006, 034, 027, 029, 030, 033, 028, 005, 032, 031, 002, 016, 017, 001, 406, 405, 403, 404, 365, 364, 407 ]; | |
string[] names = GetStringsCommon(LANGUAGE, "trname"); | |
string[] items = GetStringsCommon(LANGUAGE, "itemname"); | |
foreach (var t in obj.Table) | |
{ | |
var reward1 = t.Reward01; | |
var reward2 = t.Reward02; | |
var reward3 = t.Reward03; | |
if (reward1.RewardType is not ClubRewardType.ITEM && reward2.RewardType is not ClubRewardType.ITEM && reward3.RewardType is not ClubRewardType.ITEM) | |
continue; | |
string line = "- " + names[Coaches[(int)t.Coach]]; | |
if (reward1.RewardType is ClubRewardType.ITEM) { reward1.RewardId = (int)GetRandomItem(); line += $": {items[(int)reward1.RewardId]} (×{reward1.Count})"; } | |
if (reward2.RewardType is ClubRewardType.ITEM) { reward2.RewardId = (int)GetRandomItem(); line += $": {items[(int)reward2.RewardId]} (×{reward2.Count})"; } | |
if (reward3.RewardType is ClubRewardType.ITEM) { reward3.RewardId = (int)GetRandomItem(); line += $": {items[(int)reward3.RewardId]} (×{reward3.Count})"; } | |
RandomizerLog.Add(line); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizePortoMarinadaAuctions() | |
{ | |
RandomizerLog.Add("== PORTO MARINADA AUCTION ITEMS =="); | |
"Randomizing Porto Marinada auction items...".Dump(); | |
const string file = "gym_mizu_seri_item_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/gym/gym_mizu_seri_item/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/gym/gym_mizu_seri_item/"); | |
var obj = FlatBufferConverter.DeserializeFrom<SeriItemTableArray>(Path.Combine(path, file)); | |
string[] items = GetStringsCommon(LANGUAGE, "itemname"); | |
foreach (var t in obj.Table.Where(z => z.ItemId is not 0 && z.DevId is 0)) | |
{ | |
t.ItemId = GetRandomItem(); | |
string line = $"- {items[(int)t.ItemId]} (Min: {t.MinItemNum} / Max: {t.MaxItemNum})"; | |
RandomizerLog.Add(line); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizeItemPrinterItems() | |
{ | |
RandomizerLog.Add("== ITEM PRINTER ITEMS =="); | |
"Randomizing Item Printer items...".Dump(); | |
const string file = "item_table_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/ui/item_machine/item_table/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/ui/item_machine/item_table/"); | |
var obj = FlatBufferConverter.DeserializeFrom<ItemMachineItemTableArray>(Path.Combine(path, file)); | |
string[] items = GetStringsCommon(LANGUAGE, "itemname"); | |
bool IsTeraShard(ushort item) => item is (>= 1862 and <= 1879) or 2549; | |
var ordered = obj.Table.OrderByDescending(z => z.Param.Value.EmergePercent).ToList(); | |
foreach (var t in ordered.Where(z => !IsTeraShard((ushort)z.Param.Value.ItemId))) | |
{ | |
t.Param.Value.ItemId = GetRandomItem(); | |
string item = $"{items[(int)t.Param.Value.ItemId]}"; | |
float rate = t.Param.Value.EmergePercent / 100f; | |
string num = $"Min: {t.Param.Value.LotteryItemNumMin} / Max: {t.Param.Value.LotteryItemNumMax}"; | |
string line = $"- {rate:0.00}% {item} ({num})"; | |
RandomizerLog.Add(line); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void RandomizeTeraRaidItemDrops() | |
{ | |
if (!RANDOMIZE_TERA_RAID_ITEM_DROPS_BONUS && !RANDOMIZE_TERA_RAID_ITEM_DROPS_FIXED) | |
return; | |
"Randomizing Tera Raid Battle item drops...".Dump(); | |
const string fileB = "raid_lottery_reward_item_array.bin"; | |
string destB = Path.Combine(DEST_MASTER, "world/data/raid/raid_lottery_reward_item/"); | |
string pathB = Path.Combine(PATH_MASTER, "world/data/raid/raid_lottery_reward_item/"); | |
var objB = FlatBufferConverter.DeserializeFrom<RaidLotteryRewardItemArray>(Path.Combine(pathB, fileB)); | |
const string fileF = "raid_fixed_reward_item_array.bin"; | |
string destF = Path.Combine(DEST_MASTER, "world/data/raid/raid_fixed_reward_item/"); | |
string pathF = Path.Combine(PATH_MASTER, "world/data/raid/raid_fixed_reward_item/"); | |
var objF = FlatBufferConverter.DeserializeFrom<RaidFixedRewardItemArray>(Path.Combine(pathF, fileF)); | |
string[] items = GetStringsCommon(LANGUAGE, "itemname"); | |
if (RANDOMIZE_TERA_RAID_ITEM_DROPS_BONUS) | |
{ | |
RandomizerLog.Add("== TERA RAID BATTLES: BONUS ITEM DROPS =="); | |
const int count = RaidLotteryRewardItem.RewardItemCount; | |
foreach (var t in objB.Table) | |
{ | |
RandomizerLog.Add($"Table 0x{t.TableName:X16}"); | |
float totalRate = 0; | |
for (int i = 0; i < count; i++) | |
totalRate += t.GetRewardItem(i).Rate; | |
for (int i = 0; i < count; i++) | |
{ | |
var drop = t.GetRewardItem(i); | |
if (drop.ItemID is 0) | |
continue; | |
float rate = (float)(Math.Round((drop.Rate / totalRate) * 100f, 2)); | |
string oldItem = $"{items[(int)drop.ItemID]} (×{drop.Num})"; | |
drop.ItemID = GetRandomItem(); | |
string newItem = $"{items[(int)drop.ItemID]} (×{drop.Num})"; | |
RandomizerLog.Add($"- {rate:00.00}% {oldItem} -> {newItem}"); | |
} | |
RandomizerLog.Add(""); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(destB)) | |
Directory.CreateDirectory(destB); | |
byte[] data = FlatBufferConverter.SerializeFrom(objB); | |
File.WriteAllBytes(Path.Combine(destB, fileB), data); | |
} | |
if (RANDOMIZE_TERA_RAID_ITEM_DROPS_FIXED) | |
{ | |
RandomizerLog.Add("== TERA RAID BATTLES: FIXED ITEM DROPS =="); | |
const int count = RaidFixedRewardItem.Count; | |
foreach (var t in objF.Table) | |
{ | |
RandomizerLog.Add($"Table 0x{t.TableName:X16}"); | |
for (int i = 0; i < count; i++) | |
{ | |
var drop = t.GetReward(i); | |
if (drop.ItemID is 0) | |
continue; | |
string oldItem = $"{items[(int)drop.ItemID]} (×{drop.Num})"; | |
drop.ItemID = GetRandomItem(); | |
string newItem = $"{items[(int)drop.ItemID]} (×{drop.Num})"; | |
RandomizerLog.Add($"- {oldItem} -> {newItem}"); | |
} | |
RandomizerLog.Add(""); | |
} | |
RandomizerLog.Add(""); | |
if (!Directory.Exists(destF)) | |
Directory.CreateDirectory(destF); | |
byte[] data = FlatBufferConverter.SerializeFrom(objF); | |
File.WriteAllBytes(Path.Combine(destF, fileF), data); | |
} | |
} | |
private static string GetIconName(ushort move) => GetMoveType(move) switch | |
{ | |
00 => "item_0328", // Normal | |
01 => "item_0339", // Fighting | |
02 => "item_0341", // Flying | |
03 => "item_0340", // Poison | |
04 => "item_0332", // Ground | |
05 => "item_0363", // Rock | |
06 => "item_0342", // Bug | |
07 => "item_0344", // Ghost | |
08 => "item_0358", // Steel | |
09 => "item_0335", // Fire | |
10 => "item_0338", // Water | |
11 => "item_0347", // Grass | |
12 => "item_0336", // Electric | |
13 => "item_0331", // Psychic | |
14 => "item_0337", // Ice | |
15 => "item_0371", // Dragon | |
16 => "item_0330", // Dark | |
17 => "item_0329", // Fairy | |
_ => throw new ArgumentException($"Invalid move: {move}"), | |
}; | |
private static void EnhanceShopLineups() | |
{ | |
"Enhancing Poké Mart and Delibird Presents inventories...".Dump(); | |
const string item = "itemdata_array.bin"; | |
string pathItem = Path.Combine(PATH_MASTER, "world/data/item/itemdata/"); | |
var ioa = FlatBufferConverter.DeserializeFrom<ItemDataArray>(Path.Combine(pathItem, item)); | |
List<int> Blacklist = [ 2100, 2101, 2102, 2124, 2125, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149, 2150, 2151, 2152, 2153, 2154, 2155 ]; | |
List<ushort> Materials = ioa.Table.Where(z => z.FieldPocket is FieldPocket.FPOCKET_MATERIAL && !Blacklist.Contains(z.Id)).Select(z => (ushort)z.Id).ToList(); | |
const string file = "friendlyshop_lineup_data_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/ui/shop/friendlyshop/friendlyshop_lineup_data/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/ui/shop/friendlyshop/friendlyshop_lineup_data/"); | |
var obj = FlatBufferConverter.DeserializeFrom<LineupDataArray>(Path.Combine(path, file)); | |
List<ushort> ItemIDs = [ 2354, 0080, 0081, 0107, 0108, 0109, 0235, 0252, 0321, 0322, 0323, 0324, 0325, 0327, 0537, 0849, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1253, 1254, 1582, 1592, 2351, 2352, 1861, 2344, 2402, 2403, 2404, 2482, 2353 ]; | |
List<LineupData> Items = new(); | |
int ctr = 26; | |
// remove Steel Bottle R/Y/B and Silver Bottle, will be replaced with our custom items afterwards | |
obj.Table.Remove(obj.Table.FirstOrDefault(z => z.Item is ItemID.ITEMID_SUITOU9)); | |
obj.Table.Remove(obj.Table.FirstOrDefault(z => z.Item is ItemID.ITEMID_SUITOU10)); | |
obj.Table.Remove(obj.Table.FirstOrDefault(z => z.Item is ItemID.ITEMID_SUITOU11)); | |
obj.Table.Remove(obj.Table.FirstOrDefault(z => z.Item is ItemID.ITEMID_SUITOU12)); | |
for (int i = 0; i <= 2; i++) | |
{ | |
int sort = 21; | |
for (int k = 0; k < ItemIDs.Count; k++) | |
{ | |
LineupData entry = new() | |
{ | |
ItemCondkind = CondEnum.GYMBADGENUM, | |
ItemCondvalue = "BADGE3", | |
GymBadgeNum = 3, | |
Lineupid = $"shop_delibird_{i:00}_lineup2", | |
Item = (ItemID)ItemIDs[k], | |
Sortnum = sort, | |
}; | |
obj.Table.Add(entry); | |
sort++; | |
} | |
} | |
foreach (ushort t in Materials) | |
{ | |
LineupData Material = new() | |
{ | |
Lineupid = "shop_00_lineup", | |
Sortnum = ctr, | |
Item = (ItemID)t, | |
ItemCondkind = CondEnum.NONE, | |
ItemCondvalue = string.Empty, | |
GymBadgeNum = 0, | |
}; | |
obj.Table.Add(Material); | |
ctr++; | |
} | |
foreach (var t in obj.Table.Where(z => z.Lineupid is "shop_syouten_lineup")) | |
t.Sortnum++; | |
LineupData MythicalPechaBerry = new() | |
{ | |
ItemCondkind = CondEnum.SYSTEM_FLAG, | |
ItemCondvalue = "FSYS_SCENARIO_GAME_CLEAR_SU2", | |
GymBadgeNum = 8, | |
Lineupid = "shop_syouten_lineup", | |
Item = ItemID.ITEMID_GOKUZYOUMOMON, | |
Sortnum = 1, | |
}; | |
obj.Table.Add(MythicalPechaBerry); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void EnhanceItemData() | |
{ | |
"Enhancing item properties...".Dump(); | |
const string file = "itemdata_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/item/itemdata/"); | |
string source = Directory.Exists(dest) ? Path.Combine(dest, file) : Path.Combine(PATH_MASTER, "world/data/item/itemdata/", file); | |
var obj = FlatBufferConverter.DeserializeFrom<ItemDataArray>(source); | |
List<int> TradeEvolutions = [ 0221, 0226, 0227, 0233, 0235, 0252, 0321, 0322, 0323, 0324, 0325, 0537, 0646, 0647 ]; | |
// adjust Mythical Pecha Berry price, make trade evolution items usable | |
foreach (var t in obj.Table.Where(z => TradeEvolutions.Contains(z.Id))) | |
{ | |
if (t.Id is 2550) | |
t.Price = 100000; | |
t.FieldFunctionType = FieldFunctionType.FIELDFUNC_EVOLUTION; | |
t.WorkType = WorkType.WORKTYPE_EffectPokemon; | |
t.WorkEvolutional = 1; | |
t.EquipEffect = EquipEffect.SOUBI_NONE; | |
t.EquipPower = 0; | |
} | |
// the game sanitizes invalid items from the bag on boot, so we need to replace existing, valid items to insert our custom items | |
// this removes Steel Bottle R/Y/B and Silver Bottle, so nothing of major value was lost | |
obj.Table.Remove(obj.Table.FirstOrDefault(z => z.Id is 2351)); | |
obj.Table.Remove(obj.Table.FirstOrDefault(z => z.Id is 2352)); | |
obj.Table.Remove(obj.Table.FirstOrDefault(z => z.Id is 2353)); | |
obj.Table.Remove(obj.Table.FirstOrDefault(z => z.Id is 2354)); | |
ItemData BlackAugurite = new() | |
{ | |
FieldFunctionType = FieldFunctionType.FIELDFUNC_EVOLUTION, | |
FieldPocket = FieldPocket.FPOCKET_OTHER, | |
IconName = "item_2351", | |
Id = 2351, | |
ItemType = ItemType.ITEMTYPE_EQUIP, | |
NaturalGiftType = 18, | |
Price = 3000, | |
SetToPoke = true, | |
SlotMaxNum = 999, | |
SortNum = 255, | |
ThrowPower = 30, | |
WorkEvolutional = 1, | |
WorkType = WorkType.WORKTYPE_EffectPokemon, | |
}; | |
ItemData PeatBlock = new() | |
{ | |
FieldFunctionType = FieldFunctionType.FIELDFUNC_EVOLUTION, | |
FieldPocket = FieldPocket.FPOCKET_OTHER, | |
IconName = "item_2352", | |
Id = 2352, | |
ItemType = ItemType.ITEMTYPE_EQUIP, | |
NaturalGiftType = 18, | |
Price = 3000, | |
SetToPoke = true, | |
SlotMaxNum = 999, | |
SortNum = 255, | |
ThrowPower = 30, | |
WorkEvolutional = 1, | |
WorkType = WorkType.WORKTYPE_EffectPokemon, | |
}; | |
ItemData ForeignTreat = new() | |
{ | |
FieldFunctionType = FieldFunctionType.FIELDFUNC_EVOLUTION, | |
FieldPocket = FieldPocket.FPOCKET_OTHER, | |
IconName = "item_2353", | |
Id = 2353, | |
ItemType = ItemType.ITEMTYPE_EQUIP, | |
NaturalGiftType = 18, | |
Price = 3000, | |
SetToPoke = true, | |
SlotMaxNum = 999, | |
SortNum = 255, | |
ThrowPower = 30, | |
WorkEvolutional = 1, | |
WorkType = WorkType.WORKTYPE_EffectPokemon, | |
}; | |
ItemData LinkingCord = new() | |
{ | |
FieldFunctionType = FieldFunctionType.FIELDFUNC_EVOLUTION, | |
FieldPocket = FieldPocket.FPOCKET_OTHER, | |
IconName = "item_2354", | |
Id = 2354, | |
ItemType = ItemType.ITEMTYPE_EQUIP, | |
NaturalGiftType = 18, | |
Price = 3000, | |
SetToPoke = true, | |
SlotMaxNum = 999, | |
SortNum = 255, | |
ThrowPower = 30, | |
WorkEvolutional = 1, | |
WorkType = WorkType.WORKTYPE_EffectPokemon, | |
}; | |
// add the custom items! | |
obj.Table.Add(BlackAugurite); | |
obj.Table.Add(PeatBlock); | |
obj.Table.Add(ForeignTreat); | |
obj.Table.Add(LinkingCord); | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void UpdatePlibConversionTable() | |
{ | |
"Updating plib item conversion table...".Dump(); | |
const string file = "plib_item_conversion_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/battle/plib_item_conversion/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/battle/plib_item_conversion/"); | |
var obj = FlatBufferConverter.DeserializeFrom<ItemTableArray>(Path.Combine(path, file)); | |
// plib table has a hardcoded max, so need to modify empty slots | |
for (int i = 0; i < obj.Table.Count; i++) | |
{ | |
if (obj.Table[i].PlibID is 53) obj.Table[i].ItemID = 0221; // King’s Rock | |
if (obj.Table[i].PlibID is 54) obj.Table[i].ItemID = 0233; // Metal Coat | |
if (obj.Table[i].PlibID is 55) obj.Table[i].ItemID = 2351; // Black Augurite | |
if (obj.Table[i].PlibID is 56) obj.Table[i].ItemID = 2352; // Peat Block | |
if (obj.Table[i].PlibID is 57) obj.Table[i].ItemID = 2353; // Foreign Treat | |
if (obj.Table[i].PlibID is 58) obj.Table[i].ItemID = 2354; // Linking Cord | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void EnhanceEvolutionParameters() | |
{ | |
"Enhancing evolution methods...".Dump(); | |
const string file = "personal_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "avalon/data/"); | |
string source = Directory.Exists(dest) ? Path.Combine(dest, file) : Path.Combine(PATH_MASTER, "avalon/data/", file); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(source); | |
foreach (var t in obj.Table) | |
{ | |
var evos = t.Evolutions; | |
if (evos.Any(z => z.Method is (int)EvolutionType.Trade)) // add a Linking Cord alternative | |
{ | |
PersonalInfoEvolution newEvo = new() | |
{ | |
Level = 0, | |
Method = (int)EvolutionType.UseItem, | |
Argument = 58, | |
SpeciesInternal = (ushort)(t.Info.SpeciesNational + 1), | |
Form = t.Info.Form, | |
}; | |
evos.Add(newEvo); | |
} | |
for (int i = 0; i < evos.Count; i++) | |
{ | |
var evo = evos[i]; | |
if (evo.Method is (int)EvolutionType.TradeHeldItem or (int)EvolutionType.Hisui) | |
{ | |
var fbs = FlatBufferConverter.DeserializeFrom<ItemTableArray>(Path.Combine(DEST_MASTER, "world/data/battle/plib_item_conversion/plib_item_conversion_array.bin")); | |
var lib = fbs.Table; | |
for (int p = 0; p < lib.Count; p++) | |
{ | |
// Hisui | |
if (evo.Argument is 0) | |
{ | |
if (t.Info.SpeciesNational is 0123) | |
{ | |
evo.Method = (int)EvolutionType.UseItem; | |
evo.Argument = 55; // Black Augurite | |
} | |
if (t.Info.SpeciesNational is 0217) | |
{ | |
evo.Method = (int)EvolutionType.UseItem; | |
evo.Argument = 56; // Peat Block | |
} | |
if (t.Info.SpeciesNational is 0234) | |
{ | |
evo.Method = (int)EvolutionType.LevelUpKnowMove; | |
evo.Argument = 828; // Psyshield Bash | |
} | |
} | |
// trade with held item | |
else if (evo.Argument == lib[p].ItemID) | |
{ | |
evo.Method = (int)EvolutionType.UseItem; | |
evo.Argument = (ushort)lib[p].PlibID; | |
} | |
} | |
} | |
// version specific | |
if (evo.Method is (int)EvolutionType.LevelUpVersion) | |
{ | |
if (evo.Argument is 50) | |
evo.Method = (int)EvolutionType.LevelUpMorning; | |
if (evo.Argument is 51) | |
evo.Method = (int)EvolutionType.LevelUpNight; | |
} | |
// multiplayer | |
if (evo.Method is (int)EvolutionType.LevelUpUnionCircle) | |
evo.Method = (int)EvolutionType.LevelUp; | |
} | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void EnhanceBlueberryQuests() | |
{ | |
"Updating Snacksworth BBQ requirements...".Dump(); | |
const string file = "release_poke_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/event/s2_sub_012_legend_poke/release_poke/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/event/s2_sub_012_legend_poke/release_poke/"); | |
var obj = FlatBufferConverter.DeserializeFrom<ReleasePokeTableArray>(Path.Combine(path, file)); | |
foreach (var t in obj.Table) | |
{ | |
t.MissionA = t.MissionB = true; | |
t.TeamMissionA = t.TeamMissionB = false; | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void EnhanceBoutiqueAndSalonLineups() | |
{ | |
"Unlocking all boutique clothing items and hairstyle options...".Dump(); | |
const string file = "dressup_shop_data_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/ui/shop/dressup_shop/dressup_shop_data/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/ui/shop/dressup_shop/dressup_shop_data/"); | |
var obj = FlatBufferConverter.DeserializeFrom<DressupShopDataArray>(Path.Combine(path, file)); | |
foreach (var t in obj.Table) | |
t.UnlockType = DressupHairsalonUnlockType.NONE; | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void ExpandPaldeaPokedex() | |
{ | |
"Expanding Paldea Pokédex data to include all species...".Dump(); | |
const string dir = "demo/appli/pokedex/zk0000_00/"; | |
string dirP = "appli/tex/pokedex_capture_thum"; | |
const string dirK = "_dlc1"; | |
const string dirB = "_dlc2"; | |
const string file = "personal_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "avalon/data/"); | |
string source = Directory.Exists(dest) ? Path.Combine(dest, file) : Path.Combine(PATH_MASTER, "avalon/data/", file); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(source); | |
byte GetForm(ushort species) => species is 0128 or 0194 or 0901 ? (byte)1 : (byte)0; | |
// Pokédex images (1024x1024) | |
foreach (var t in obj.Table.Where(z => z.IsPresentInGame && z.Info.Form == GetForm(z.Info.SpeciesInternal) && z.Dex is null)) | |
{ | |
int dev = t.Info.SpeciesInternal; | |
string thumb = $"pokedex_capture_thum_{dev:000}"; | |
string target = $"{DEST_MASTER}{dir}{thumb}.bntx"; | |
string original = Path.Combine(PATH_MASTER, dir) + t switch | |
{ | |
_ when t.KitakamiDex is not 0 => $"{thumb}_01.bntx", | |
_ when t.BlueberryDex is not 0 => $"{thumb}_02.bntx", | |
_ => "pokedex_capture_thum_001.bntx", | |
}; | |
if (!Directory.Exists(Path.Combine(DEST_MASTER, dir))) | |
Directory.CreateDirectory(Path.Combine(DEST_MASTER, dir)); | |
File.Copy(original, target, true); | |
} | |
// Pokédex images (204x238) | |
foreach (var t in obj.Table.Where(z => z.IsPresentInGame && z.Info.Form == GetForm(z.Info.SpeciesInternal) && z.Dex is null)) | |
{ | |
int dev = t.Info.SpeciesInternal; | |
string thumb = $"pokedex_capture_thum_{dev:000}"; | |
string targetDir = $"{DEST_MASTER}{dirP}/{thumb}/"; | |
string target = $"{targetDir}{thumb}.bntx"; | |
string original = t switch | |
{ | |
_ when t.KitakamiDex is not 0 => $"{PATH_MASTER}{dirP}{dirK}/{thumb}/{thumb}.bntx", | |
_ when t.BlueberryDex is not 0 => $"{PATH_MASTER}{dirP}{dirB}/{thumb}/{thumb}.bntx", | |
_ => Path.Combine(PATH_MASTER, dir, "pokedex_capture_thum_001.bntx"), | |
}; | |
if (!Directory.Exists(targetDir)) | |
Directory.CreateDirectory(targetDir); | |
File.Copy(original, target, true); | |
} | |
// Pokédex indexes | |
foreach (var t in obj.Table.Where(z => z.IsPresentInGame)) | |
t.Dex = new PersonalInfoDex { Index = t.Info.SpeciesNational, Group = 0 }; | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
WipePokedexBlacklistData(); | |
FillEmptyPokedexEntries(); | |
} | |
private static void WipePokedexBlacklistData() | |
{ | |
"Wiping Pokédex blacklist data...".Dump(); | |
const string file = "blacklist_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "world/data/ui/pokedex/blacklist/"); | |
string path = Path.Combine(PATH_MASTER, "world/data/ui/pokedex/blacklist/"); | |
var obj = FlatBufferConverter.DeserializeFrom<BlacklistArray>(Path.Combine(path, file)); | |
obj.Table.Clear(); | |
for (int i = 1; i <= 4; i++) obj.Table.Add(new Blacklist { DevNo = 0998, FormNo = i }); // Koraidon | |
for (int i = 1; i <= 4; i++) obj.Table.Add(new Blacklist { DevNo = 0999, FormNo = i }); // Miraidon | |
for (int i = 4; i <= 7; i++) obj.Table.Add(new Blacklist { DevNo = 1011, FormNo = i }); // Ogerpon | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
private static void FillEmptyPokedexEntries() | |
{ | |
"Filling in empty Pokédex entries...".Dump(); | |
TextConfig cfg = new(GameVersion.SV); | |
const int MAGIC = 0x42544841; // AHTB | |
List<string> MissingEntries = | |
[ | |
"ZKN_COMMENT_A_058_001", // Growlithe (Hisuian Form) | |
"ZKN_COMMENT_A_059_001", // Arcanine (Hisuian Form) | |
"ZKN_COMMENT_A_100_001", // Voltorb (Hisuian Form) | |
"ZKN_COMMENT_A_101_001", // Electrode (Hisuian Form) | |
"ZKN_COMMENT_A_157_001", // Typhlosion (Hisuian Form) | |
"ZKN_COMMENT_A_215_001", // Sneasel (Hisuian Form) | |
"ZKN_COMMENT_A_483_001", // Dialga (Origin Forme) | |
"ZKN_COMMENT_A_484_001", // Palkia (Origin Forme) | |
"ZKN_COMMENT_A_503_001", // Samurott (Hisuian Form) | |
"ZKN_COMMENT_A_549_001", // Lilligant (Hisuian Form) | |
"ZKN_COMMENT_A_570_001", // Zorua (Hisuian Form) | |
"ZKN_COMMENT_A_571_001", // Zoroark (Hisuian Form) | |
"ZKN_COMMENT_A_628_001", // Braviary (Hisuian Form) | |
"ZKN_COMMENT_A_705_001", // Sliggoo (Hisuian Form) | |
"ZKN_COMMENT_A_706_001", // Goodra (Hisuian Form) | |
"ZKN_COMMENT_A_713_001", // Avalugg (Hisuian Form) | |
"ZKN_COMMENT_A_724_001", // Decidueye (Hisuian Form) | |
"ZKN_COMMENT_A_899_000", // Wyrdeer | |
"ZKN_COMMENT_A_901_000", // Ursaluna | |
"ZKN_COMMENT_A_903_000", // Sneasler | |
"ZKN_COMMENT_A_905_000", // Enamorus (Incarnate Forme) | |
"ZKN_COMMENT_A_905_001", // Enamorus (Therian Forme) | |
]; | |
for (int i = 0; i <= 1; i++) | |
{ | |
char ver = i is 0 ? 'A' : 'B'; | |
foreach (string lang in Languages) | |
{ | |
byte[] data = File.ReadAllBytes(Path.Combine(PATH_MASTER, $"message/dat/{lang}/common/zukan_comment_{ver}.tbl")); | |
List<AHTBEntry> OldEntries = new AHTB(data).Entries.ToList(); | |
List<AHTBEntry> NewEntries = new(); | |
for (int k = 0; k < OldEntries.Count - 1; k++) | |
NewEntries.Add(OldEntries[k]); | |
foreach (string t in MissingEntries) | |
{ | |
string name = t; | |
if (i is 1) | |
name = name.Replace("_A_", "_B_"); | |
AHTBEntry Entry = new(FnvHash.HashFnv1a_64(name), (ushort)(name.Length + 1), name); | |
NewEntries.Add(Entry); | |
} | |
NewEntries.Add(OldEntries[OldEntries.Count - 1]); // always last: msg_zukan_comment_{ver}_max | |
string dest = Path.Combine(DEST_MASTER, $"message/dat/{lang}/common/zukan_comment_{ver}.tbl"); | |
using MemoryStream ms = new(); | |
{ | |
using BinaryWriter bw = new(File.Open(dest, FileMode.Create)); | |
{ | |
bw.Write(MAGIC); | |
bw.Write(NewEntries.Count); | |
foreach (AHTBEntry t in NewEntries) | |
{ | |
bw.Write((ulong)t.Hash); | |
bw.Write((ushort)t.NameLength); | |
bw.Write(Encoding.UTF8.GetBytes(t.Name)); | |
bw.Write((byte)0); | |
} | |
} | |
} | |
} | |
} | |
for (int i = 0; i < Languages.Count; i++) | |
{ | |
string lang = Languages[i]; | |
string dest = Path.Combine(DEST_MASTER, $"message/dat/{lang}/common/"); | |
for (int j = 0; j <= 1; j++) | |
{ | |
char ver = j is 0 ? 'A' : 'B'; | |
string zkn = $"zukan_comment_{ver}"; | |
string[] original = GetStringsCommon(lang, $"zukan_comment_{ver}"); | |
string[] lines = new string[original.Length + MissingEntries.Count]; | |
// add original entries | |
for (int a = 0; a < original.Length; a++) | |
lines[a] = original[a]; | |
// add temporary empty entries | |
for (int a = original.Length; a < lines.Length; a++) | |
lines[a] = string.Empty; | |
for (int k = 0; k < lines.Length; k++) | |
{ | |
if (String.IsNullOrWhiteSpace(lines[k]) || lines[k].StartsWith("[~ ")) | |
lines[k] = PlaceholderPokedexEntries[i]; | |
} | |
// clean up form entries | |
lines[1228] = lines[0658]; // Greninja | |
lines[1330] = lines[0744]; // Rockruff | |
for (int l = 1230; l <= 1248; l++) lines[l] = lines[0664]; // Scatterbug | |
for (int l = 1249; l <= 1267; l++) lines[l] = lines[0665]; // Spewpa | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] write = TextFile.GetBytes(lines, cfg); | |
File.WriteAllBytes(Path.Combine(dest, zkn + ".dat"), write); | |
} | |
} | |
} | |
private static void UpdateItemSortTables() | |
{ | |
"Updating item sort tables...".Dump(); | |
foreach (string lang in Languages) | |
{ | |
Dictionary<ushort, string> ItemDictionary = new(); | |
string[] items = GetStringsCommon(lang, "itemname"); | |
for (ushort j = 0; j < items.Length; j++) | |
ItemDictionary.Add(j, items[j]); | |
ItemDictionary = ItemDictionary.OrderBy(z => z.Value).ToDictionary(); | |
string dir = Path.Combine(DEST_MASTER, $"message/sort/{lang}/"); | |
if (!Directory.Exists(dir)) | |
Directory.CreateDirectory(dir); | |
string dest = Path.Combine(dir, "item_sort_table.dat"); | |
using var ms = new MemoryStream(); | |
{ | |
using var bw = new BinaryWriter(File.Open(dest, FileMode.Create)); | |
{ | |
foreach (var t in ItemDictionary) | |
bw.Write(t.Key); | |
} | |
} | |
} | |
} | |
private static void ExtraChanges() | |
{ | |
if (RANDOMIZE_ROAMING_FORM_GIMMIGHOUL) | |
{ | |
"Randomizing Roaming Form Gimmighoul...".Dump(); | |
DevID species = GetRandomSpecies(0); | |
byte form = GetRandomForm(species); | |
SexType sex = GetRandomGender(species, form); | |
UpdateOverworldSceneSingle("world/obj_template/parts/coin_symbol/coin_symbol_walk_/coin_symbol_walk_0.trsot", species, form, sex); | |
} | |
if (ALL_POKEMON_ONLY_KNOW_METRONOME) | |
{ | |
"Increasing Metronome base PP...".Dump(); | |
const string file = "waza_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "avalon/data/"); | |
string source = Directory.Exists(dest) ? Path.Combine(dest, file) : Path.Combine(PATH_MASTER, "avalon/data/", file); | |
var obj = FlatBufferConverter.DeserializeFrom<WazaTable>(source); | |
var mov = obj.Table.FirstOrDefault(z => z.MoveID is 118); | |
mov.PP = 40; | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] data = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, file), data); | |
} | |
if (RANDOMIZE_GENERIC_OVERWORLD_POKEMON) | |
{ | |
"Randomizing all generic overworld Pokémon (this may take a while)...".Dump(); | |
foreach (string t in GenericOverworldPokemonScenes) | |
UpdateOverworldSceneGeneric(t); | |
} | |
} | |
private static List<DevID> GetSpeciesBanlist() | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
List<DevID> Banned = new(); | |
if (!ALLOW_SPECIES_GEN_1) Banned.AddRange(Enumerable.Range(0001, 151).Select(z => (DevID)z)); | |
if (!ALLOW_SPECIES_GEN_2) Banned.AddRange(Enumerable.Range(0152, 100).Select(z => (DevID)z)); | |
if (!ALLOW_SPECIES_GEN_3) Banned.AddRange(Enumerable.Range(0252, 135).Select(z => (DevID)z)); | |
if (!ALLOW_SPECIES_GEN_4) Banned.AddRange(Enumerable.Range(0387, 107).Select(z => (DevID)z)); | |
if (!ALLOW_SPECIES_GEN_5) Banned.AddRange(Enumerable.Range(0494, 156).Select(z => (DevID)z)); | |
if (!ALLOW_SPECIES_GEN_6) Banned.AddRange(Enumerable.Range(0650, 072).Select(z => (DevID)z)); | |
if (!ALLOW_SPECIES_GEN_7) Banned.AddRange(Enumerable.Range(0722, 088).Select(z => (DevID)z)); | |
if (!ALLOW_SPECIES_GEN_8) Banned.AddRange(Enumerable.Range(0810, 096).Select(z => (DevID)z)); | |
if (!ALLOW_SPECIES_GEN_9) Banned.AddRange(Enumerable.Range(0906, 120).Select(z => (DevID)z)); | |
// failsafe if we ban all species | |
if (Banned.Count is 1025) | |
Banned.Clear(); | |
if (!ALLOW_SPECIES_LEGENDARY) foreach (ushort species in Legendary) Banned.Add((DevID)GetDevID(species)); | |
if (!ALLOW_SPECIES_MYTHICAL) foreach (ushort species in Mythical) Banned.Add((DevID)GetDevID(species)); | |
if (!ALLOW_SPECIES_PARADOX) foreach (ushort species in Paradox) Banned.Add((DevID)GetDevID(species)); | |
foreach (var t in obj.Table.Where(z => z.Info.Form is 0)) | |
{ | |
if (!t.IsPresentInGame) | |
Banned.Add((DevID)t.Info.SpeciesInternal); | |
} | |
Banned = Banned.Distinct().ToList(); | |
return Banned; | |
} | |
private static List<DevID> GetSpeciesList() => Enumerable.Range(1, MAX_SPECIES_ID).Select(z => (DevID)z).Except(SpeciesBanlist).ToList(); | |
private static List<int> GetPermittedAbilities() | |
{ | |
List<int> Banned = | |
[ | |
059, // Forecast | |
121, // Multitype | |
161, // Zen Mode | |
176, // Stance Change | |
197, // Shields Down | |
208, // Schooling | |
209, // Disguise | |
211, // Power Construct | |
225, // RKS System | |
241, // Gulp Missile | |
248, // Ice Face | |
258, // Hunger Switch | |
278, // Zero to Hero | |
307, // Tera Shift | |
]; | |
if (BAN_ABILITY_WONDER_GUARD) | |
Banned.Add(025); | |
List<int> Abilities = Enumerable.Range(1, MAX_ABILITY_ID).Except(Banned).ToList(); | |
return Abilities; | |
} | |
private static List<ushort> GetItemList() | |
{ | |
string file = Path.Combine(PATH_MASTER, "world/data/item/itemdata/itemdata_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<ItemDataArray>(file); | |
List<FieldPocket> DisallowedPockets = [ FieldPocket.FPOCKET_WAZA, FieldPocket.FPOCKET_EVENT, FieldPocket.FPOCKET_MATERIAL ]; | |
List<int> DisallowedItems = [ 0016, 0111, 0485, 0486, 0487, 0488, 0489, 0490, 0491, 0500, 0708, 0709, 1785, 2351, 2352, 2353, 2354 ]; | |
if (!ALLOW_RANDOM_PICNIC_ITEMS) | |
DisallowedPockets.Add(FieldPocket.FPOCKET_PICNIC); | |
// we only want picks and ingredients in the pool; accessories like cups and tablecloths should be capped at 1 | |
else | |
{ | |
for (int i = 1947; i <= 1955; i++) DisallowedItems.Add(i); // unused, but have item data | |
for (int i = 2311; i <= 2400; i++) DisallowedItems.Add(i); | |
for (int i = 2417; i <= 2437; i++) DisallowedItems.Add(i); | |
DisallowedItems.Add(2551); // Blueberry Tablecloth | |
DisallowedItems.Add(2552); // Blueberry Chairs | |
} | |
List<ushort> Items = obj.Table.Where(z => !DisallowedPockets.Contains(z.FieldPocket) && !DisallowedItems.Contains(z.Id)).Select(z => (ushort)z.Id).ToList(); | |
Items.Add(2351); // Black Augurite | |
Items.Add(2352); // Peat Block | |
Items.Add(2353); // Foreign Treat | |
Items.Add(2354); // Linking Cord | |
Items = Items.Distinct().ToList(); | |
return Items; | |
} | |
private static List<int> GetTMList() | |
{ | |
string file = Path.Combine(PATH_MASTER, "world/data/item/itemdata/itemdata_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<ItemDataArray>(file); | |
List<int> TechnicalMachines = obj.Table.Where(z => z.FieldPocket is FieldPocket.FPOCKET_WAZA).Select(z => z.Id).ToList(); | |
return TechnicalMachines; | |
} | |
private static List<int> GetPermittedMoves() | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/waza_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<WazaTable>(file); | |
List<int> Moves = obj.Table.Where(z => z.CanUseMove).Select(z => (int)z.MoveID).ToList(); | |
return Moves; | |
} | |
private static DevID GetRandomSpecies(DevID dev) | |
{ | |
ushort nat = GetNationalDexID((ushort)dev); | |
DevID species; | |
if (FORCE_SWAP_SPECIES_LEGENDARY && Legendary.Contains(nat)) | |
{ | |
List<DevID> Legendaries = Legendary.Select(z => (DevID)z).Except(SpeciesBanlist).ToList(); | |
species = Legendaries[GetRandom(Legendaries.Count)]; | |
return (DevID)GetDevID((ushort)species); | |
} | |
if (FORCE_SWAP_SPECIES_MYTHICAL && Mythical.Contains(nat)) | |
{ | |
List<DevID> Mythicals = Mythical.Select(z => (DevID)z).Except(SpeciesBanlist).ToList(); | |
species = Mythicals[GetRandom(Mythicals.Count)]; | |
return (DevID)GetDevID((ushort)species); | |
} | |
if (FORCE_SWAP_SPECIES_PARADOX && Paradox.Contains(nat)) | |
{ | |
List<DevID> Paradoxes = Paradox.Select(z => (DevID)z).Except(SpeciesBanlist).ToList(); | |
species = Paradoxes[GetRandom(Paradoxes.Count)]; | |
return (DevID)GetDevID((ushort)species); | |
} | |
species = SpeciesList[GetRandom(SpeciesList.Count)]; | |
return species; | |
} | |
private static PokeDataFull ForceEvolveSpeciesOnce(PokeDataFull pk) | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
// does not evolve | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == (ushort)pk.DevId && z.Info.Form == pk.FormId); | |
if (sel.Evolutions.Count is 0) | |
return pk; | |
int evo = GetRandom(sel.Evolutions.Count); | |
pk.DevId = (DevID)sel.Evolutions[evo].SpeciesInternal; | |
pk.FormId = (short)sel.Evolutions[evo].Form; | |
return pk; | |
} | |
private static ushort ForceEvolveSpeciesFully(ushort species, byte form) | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
// does not evolve | |
var stage1 = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == species && z.Info.Form == form); | |
if (stage1.Evolutions.Count is 0) | |
return species; | |
int select1 = GetRandom(stage1.Evolutions.Count); | |
ushort evoS = stage1.Evolutions[select1].SpeciesInternal; | |
ushort evoF = stage1.Evolutions[select1].Form; | |
// two-stage family | |
var stage2 = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == evoS && z.Info.Form == evoF); | |
if (stage2.Evolutions.Count is 0) | |
return evoS; | |
// three-stage family | |
int select2 = GetRandom(stage2.Evolutions.Count); | |
return stage2.Evolutions[select2].SpeciesInternal; | |
} | |
private static byte GetRandomForm(DevID dev) | |
{ | |
ushort species = GetNationalDexID((ushort)dev); | |
byte count = GetFormCount((ushort)dev); | |
return species switch | |
{ | |
0025 or 0080 => GetValidForm(species), // Partner Pikachu, Mega Slowbro | |
0493 when !RANDOMIZE_PERSONAL_ABILITY || !ENSURE_SANE_FORM_HELD_ITEMS => 0, // Arceus | |
0658 => (byte)GetRandom(2), // Ash-Greninja | |
0670 => (byte)GetRandom(5), // Eternal Flower Floette | |
0774 => (byte)GetRandom(7), // Core Form Minior | |
0800 => (byte)GetRandom(3), // Ultra Necrozma | |
0890 or 1007 or 1008 => 0, // Eternatus, Koraidon, Miraidon | |
1017 => (byte)GetRandom(4), // Terastallized Ogerpon | |
_ when BattleOnlyForms.Contains(species) && !RANDOMIZE_PERSONAL_ABILITY => 0, | |
_ when UnavailableForms.Contains(species) => 0, | |
_ => (byte)GetRandom(count), | |
}; | |
} | |
private static SexType GetRandomGender(DevID species, short form) | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == (ushort)species && z.Info.Form == form).Gender; | |
return sel.Group switch | |
{ | |
SexGroup.MALE => SexType.MALE, | |
SexGroup.FEMALE => SexType.FEMALE, | |
SexGroup.UNKNOWN => SexType.DEFAULT, | |
_ => GetRandom(100) < sel.Ratio ? SexType.FEMALE : SexType.MALE, | |
}; | |
} | |
private static byte GetSceneGender(ushort species, short form, byte sex) | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == species && z.Info.Form == form).Gender; | |
return sel.Group switch | |
{ | |
SexGroup.MALE => 0, | |
SexGroup.FEMALE => 1, | |
_ when IsGenderVariant(species, form) => (byte)(sex - 1), | |
_ => 0, | |
}; | |
} | |
private static int GetRandomAbility() => PermittedAbilities[GetRandom(PermittedAbilities.Count)]; | |
private static ItemID GetRandomItem() => (ItemID)ItemList[GetRandom(ItemList.Count)]; | |
private static bool IsFormHeldItemDependent(DevID species) => (ushort)species is 0483 or 0484 or 0487 or 0493 or 0888 or 0889 or 1011; | |
private static ItemID GetFormSpecificItem(DevID species, short form) => (ushort)species switch | |
{ | |
0483 when form is 1 => ItemID.ITEMID_DAIKONGOUDAMA, // Adamant Crystal | |
0484 when form is 1 => ItemID.ITEMID_DAISIRATAMA, // Lustrous Globe | |
0487 when form is 1 => ItemID.ITEMID_DAIHAKKINDAMA, // Griseous Core | |
0888 when form is 1 => ItemID.ITEMID_KUTITATURUGI, // Rusted Sword | |
0889 when form is 1 => ItemID.ITEMID_KUTITATATE, // Rusted Shield | |
0493 => GetArceusPlate(form), | |
1011 => GetOgerponMask(form), | |
_ => ItemID.ITEMID_NONE, | |
}; | |
private static ItemID GetArceusPlate(short form) | |
{ | |
List<ushort> Plates = [ 0000, 0303, 0306, 0304, 0305, 0309, 0308, 0310, 0313, 0298, 0299, 0301, 0300, 0307, 0302, 0311, 0312, 0644 ]; | |
return (ItemID)Plates[form]; | |
} | |
private static ItemID GetOgerponMask(short form) | |
{ | |
List<ushort> Masks = [ 0000, 2407, 2408, 2406 ]; | |
return (ItemID)Masks[form]; | |
} | |
private static List<int> GetRandomMoveList() | |
{ | |
List<int> Banned = | |
[ | |
000, // ——— | |
165, // Struggle | |
464, // Dark Void | |
621, // Hyperspace Fury | |
783, // Aura Wheel | |
896, // Blazing Torque | |
897, // Wicked Torque | |
898, // Noxious Torque | |
899, // Combat Torque | |
900, // Magical Torque | |
]; | |
List<int> Moves = PermittedMoves.Except(Banned).ToList(); | |
pkNX.Randomization.Util.Shuffle(Moves); | |
return Moves; | |
} | |
private static byte GetValidForm(ushort species) | |
{ | |
byte count = GetFormCount(species); | |
byte banned = species is 0025 ? (byte)8 : (byte)1; | |
byte form = (byte)GetRandom(count - 1); | |
if (form == banned) | |
form++; | |
return form; | |
} | |
private static GemType GetTeraType(DevID species, byte form, GemType gem, bool raid) | |
{ | |
// Ogerpon Tera Type must match form | |
if (species is DevID.DEV_KAMENONI) | |
{ | |
return form switch | |
{ | |
1 => GemType.MIZU, // Wellspring Mask | |
2 => GemType.HONOO, // Hearthflame Mask | |
3 => GemType.IWA, // Cornerstone Mask | |
_ => GemType.KUSA, // Teal Mask | |
}; | |
} | |
// Terapagos can only have Stellar | |
if (species is DevID.DEV_KODAIGAME) | |
return GemType.NIJI; | |
// raids must be random | |
if (raid) | |
return GemType.RANDOM; | |
// abide by standard type | |
if (!ALLOW_FULLY_RANDOM_TERA_TYPES) | |
return GemType.DEFAULT; | |
// roll for a chance of Stellar, otherwise random | |
bool chance = GetRandom(100) < STELLAR_TERA_TYPE_CHANCE_PERCENT; | |
if (chance) | |
return GemType.NIJI; | |
return (GemType)GetRandom(2, 20); | |
} | |
private static byte GetFormCount(ushort species) | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
byte count = (byte)obj.Table.Where(z => z.Info.SpeciesInternal == species).ToArray().Length; | |
return count; | |
} | |
private static bool GetIsPresentInGame(ushort species, byte form) | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == species && z.Info.Form == form); | |
return sel.IsPresentInGame; | |
} | |
private static bool IsValidForm(ushort species, byte form) => GetNationalDexID(species) switch | |
{ | |
// can only be a different type if it does not have Multitype, or it is holding the correct plate | |
0493 when form is not 0 => RANDOMIZE_PERSONAL_ABILITY || ENSURE_SANE_FORM_HELD_ITEMS, | |
// handled by BibiyonFormNoSave_formNo | |
0664 or 0665 or 0666 when form is not 18 => false, // Scatterbug, Spewpa, Vivillon | |
// form change is dependent on a signature ability | |
0774 when form is >= 7 => RANDOMIZE_PERSONAL_ABILITY, // Minior | |
0778 when form is not 0 => RANDOMIZE_PERSONAL_ABILITY, // Mimikyu | |
0845 when form is not 0 => RANDOMIZE_PERSONAL_ABILITY, // Cramorant | |
0875 when form is not 0 => RANDOMIZE_PERSONAL_ABILITY, // Eiscue | |
0877 when form is not 0 => RANDOMIZE_PERSONAL_ABILITY, // Morpeko | |
0964 when form is not 0 => RANDOMIZE_PERSONAL_ABILITY, // Palafin | |
// no proper animations | |
1007 or 1008 when form is not 0 => false, // Koraidon, Miraidon | |
// safeguards for invalid Tera Types | |
1017 when form is not 0 => false, // Ogerpon | |
_ => true, | |
}; | |
private static ushort GetDevID(ushort species) | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesNational == species); | |
return sel.Info.SpeciesInternal; | |
} | |
private static ushort GetNationalDexID(ushort species) | |
{ | |
string file = Path.Combine(PATH_MASTER, "avalon/data/personal_array.bin"); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(file); | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesInternal == species); | |
return sel.Info.SpeciesNational; | |
} | |
private static byte GetMoveType(ushort move) | |
{ | |
const string file = "waza_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "avalon/data/"); | |
string source = File.Exists(Path.Combine(dest, file)) ? Path.Combine(dest, file) : Path.Combine(PATH_MASTER, "avalon/data/", file); | |
var obj = FlatBufferConverter.DeserializeFrom<WazaTable>(source); | |
var sel = obj.Table.FirstOrDefault(z => z.MoveID == move); | |
return sel.Type; | |
} | |
private static List<ushort> GetCurrentMoves(ushort species, byte form, byte level) | |
{ | |
const string file = "personal_array.bin"; | |
string dest = Path.Combine(DEST_MASTER, "avalon/data/"); | |
string source = Directory.Exists(dest) ? Path.Combine(dest, file) : Path.Combine(PATH_MASTER, "avalon/data/", file); | |
var obj = FlatBufferConverter.DeserializeFrom<PersonalTable>(source); | |
var sel = obj.Table.FirstOrDefault(z => z.Info.SpeciesNational == species && z.Info.Form == form); | |
List<ushort> SelfFaintMoves = [ 120, 153, 262, 361, 461, 515, 802 ]; | |
List<ushort> Moves = sel.Learnset.Where(z => !SelfFaintMoves.Contains(z.Move) && z.Move is not 906 && z.Level <= level).Distinct().TakeLast(4).Select(z => z.Move).ToList(); | |
// always give a list of 4 moves | |
if (Moves.Count < 4) | |
{ | |
do Moves.Add(0); | |
while (Moves.Count is not 4); | |
} | |
return Moves; | |
} | |
private static string[] GetStringsCommon(string lang, string file) | |
{ | |
bool exists = File.Exists(Path.Combine(DEST_MASTER, $"message/dat/{lang}/common/{file}.dat")); | |
string source = file.StartsWith("item") && exists ? DEST_MASTER : PATH_MASTER; | |
string path = Path.Combine(source, $"message/dat/{lang}/common/{file}.dat"); | |
byte[] data = File.ReadAllBytes(path); | |
TextConfig cfg = new(GameVersion.SV); | |
return new TextFile(data, cfg).Lines; | |
} | |
private static string[] GetStringsScript(string lang, string file) | |
{ | |
string path = Path.Combine(PATH_MASTER, $"message/dat/{lang}/script/{file}.dat"); | |
byte[] data = File.ReadAllBytes(path); | |
TextConfig cfg = new(GameVersion.SV); | |
return new TextFile(data, cfg).Lines; | |
} | |
private static string GetStringFromAHTBCommon(string file, ulong hash, string lang) | |
{ | |
string path = Path.Combine(PATH_MASTER, $"message/dat/{lang}/common/{file}.dat"); | |
string ahtb = Path.Combine(PATH_MASTER, $"message/dat/{lang}/common/{file}.tbl"); | |
byte[] data = File.ReadAllBytes(path); | |
TextConfig cfg = new(GameVersion.SV); | |
string[] text = new TextFile(data, cfg).Lines; | |
byte[] dataAHTB = File.ReadAllBytes(ahtb); | |
List<AHTBEntry> Entries = new AHTB(dataAHTB).Entries.ToList(); | |
AHTBEntry target = Entries.FirstOrDefault(z => z.Hash == hash); | |
int index = Entries.IndexOf(target); | |
if (target is null) | |
return "INVALID"; | |
return text[index]; | |
} | |
private static string GetStringFromAHTBScript(string file, ulong hash, string lang) | |
{ | |
string path = Path.Combine(PATH_MASTER, $"message/dat/{lang}/script/{file}.dat"); | |
string ahtb = Path.Combine(PATH_MASTER, $"message/dat/{lang}/script/{file}.tbl"); | |
byte[] data = File.ReadAllBytes(path); | |
TextConfig cfg = new(GameVersion.SV); | |
string[] text = new TextFile(data, cfg).Lines; | |
byte[] dataAHTB = File.ReadAllBytes(ahtb); | |
List<AHTBEntry> Entries = new AHTB(dataAHTB).Entries.ToList(); | |
AHTBEntry target = Entries.FirstOrDefault(z => z.Hash == hash); | |
int index = Entries.IndexOf(target); | |
if (target is null) | |
return "INVALID"; | |
return text[index]; | |
} | |
private static void UpdateOverworldSceneSingle(string scene, DevID species, short form, SexType sex) | |
{ | |
int index = scene.LastIndexOf("/") + 1; | |
string f = scene[index..]; | |
string d = scene[..index]; | |
string dest = Path.Combine(DEST_MASTER, d); | |
string path = Path.Combine(PATH_MASTER, d); | |
byte[] data = File.ReadAllBytes(Path.Combine(path, f)); | |
var obj = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplate>(data); | |
foreach (var o in obj.Objects) | |
{ | |
foreach (var s in o.SubObjects.Where(z => z.Type is "ti_PokemonModelComponent")) | |
{ | |
var pmc = FlatBufferConverter.DeserializeFrom<TIPokemonModelComponent>(s.Data); | |
pmc.DevId = (ushort)species; | |
pmc.FormId = (byte)form; | |
pmc.Sex = (sbyte)GetSceneGender((ushort)species, form, (byte)sex); | |
s.Data = FlatBufferConverter.SerializeFrom(pmc); | |
} | |
foreach (var s in o.SubObjects.Where(z => z.Type is "ti_FieldPokemonComponent")) | |
{ | |
var fpc = FlatBufferConverter.DeserializeFrom<TIFieldPokemonComponent>(s.Data); | |
fpc.DevId = (ushort)species; | |
fpc.FormId = (byte)form; | |
fpc.Sex = (sbyte)GetSceneGender((ushort)species, form, (byte)sex); | |
s.Data = FlatBufferConverter.SerializeFrom(fpc); | |
} | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] write = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, f), write); | |
File.WriteAllBytes(Path.Combine(dest, f.Replace("_0.", "_1.")), write); | |
} | |
private static void UpdateOverworldSceneMulti(string scene, List<DevID> OldSpecies, List<DevID> NewSpecies, List<short> Forms, List<SexType> Sex, bool IsNushiDragon) | |
{ | |
int index = scene.LastIndexOf("/") + 1; | |
string f = scene[index..]; | |
string d = scene[..index]; | |
string dest = Path.Combine(DEST_MASTER, d); | |
string path = IsNushiDragon ? dest : Path.Combine(PATH_MASTER, d); | |
byte[] data = File.ReadAllBytes(Path.Combine(path, f)); | |
var obj = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplate>(data); | |
foreach (var o in obj.Objects) | |
{ | |
foreach (var s in o.SubObjects.Where(z => z.Type is "ti_PokemonModelComponent")) | |
{ | |
var pmc = FlatBufferConverter.DeserializeFrom<TIPokemonModelComponent>(s.Data); | |
for (int i = 0; i < OldSpecies.Count; i++) | |
{ | |
if (pmc.DevId == (ushort)OldSpecies[i]) | |
{ | |
pmc.DevId = (ushort)NewSpecies[i]; | |
pmc.FormId = (byte)Forms[i]; | |
pmc.Sex = (sbyte)GetSceneGender((ushort)NewSpecies[i], Forms[i], (byte)Sex[i]); | |
} | |
} | |
s.Data = FlatBufferConverter.SerializeFrom(pmc); | |
} | |
foreach (var s in o.SubObjects.Where(z => z.Type is "ti_FieldPokemonComponent")) | |
{ | |
var fpc = FlatBufferConverter.DeserializeFrom<TIFieldPokemonComponent>(s.Data); | |
for (int i = 0; i < OldSpecies.Count; i++) | |
{ | |
if (fpc.DevId == (ushort)OldSpecies[i]) | |
{ | |
fpc.DevId = (ushort)NewSpecies[i]; | |
fpc.FormId = (byte)Forms[i]; | |
fpc.Sex = (sbyte)GetSceneGender((ushort)NewSpecies[i], Forms[i], (byte)Sex[i]); | |
} | |
} | |
s.Data = FlatBufferConverter.SerializeFrom(fpc); | |
} | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] write = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, f), write); | |
File.WriteAllBytes(Path.Combine(dest, f.Replace("_0.", "_1.")), write); | |
} | |
private static void UpdateOverworldSceneGeneric(string scene) | |
{ | |
int index = scene.LastIndexOf("/") + 1; | |
string f = scene[index..]; | |
string d = scene[..index]; | |
string dest = Path.Combine(DEST_MASTER, d); | |
string path = Path.Combine(PATH_MASTER, d); | |
byte[] data = File.ReadAllBytes(Path.Combine(path, f)); | |
var obj = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplate>(data); | |
int count = 0; | |
int ctr = 0; | |
foreach (var o in obj.Objects) | |
{ | |
foreach (var s in o.SubObjects.Where(z => z.Type is "ti_PokemonModelComponent" or "ti_FieldPokemonComponent")) | |
count++; | |
} | |
List<DevID> Species = new(); | |
List<short> Forms = new(); | |
List<SexType> Sex = new(); | |
for (int i = 0; i <= count; i++) | |
{ | |
DevID rSpecies = GetRandomSpecies(0); | |
short rForm = GetRandomForm(rSpecies); | |
SexType rGender = GetRandomGender(rSpecies, rForm); | |
Species.Add(rSpecies); | |
Forms.Add(rForm); | |
Sex.Add(rGender); | |
} | |
foreach (var o in obj.Objects) | |
{ | |
foreach (var s in o.SubObjects.Where(z => z.Type is "ti_PokemonModelComponent" or "ti_FieldPokemonComponent")) | |
{ | |
var pmc = FlatBufferConverter.DeserializeFrom<TIPokemonModelComponent>(s.Data); | |
for (int i = 0; i < Species.Count; i++) | |
{ | |
pmc.DevId = (ushort)Species[ctr]; | |
pmc.FormId = (byte)Forms[ctr]; | |
pmc.Sex = (sbyte)GetSceneGender((ushort)Species[ctr], Forms[ctr], (byte)Sex[ctr]); | |
pmc.Rare = GetRandom(100) < GENERIC_OVERWORLD_POKEMON_SHINY_CHANCE_PERCENT ? (sbyte)1 : (sbyte)0; | |
} | |
ctr++; | |
s.Data = FlatBufferConverter.SerializeFrom(pmc); | |
} | |
} | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] write = FlatBufferConverter.SerializeFrom(obj); | |
File.WriteAllBytes(Path.Combine(dest, f), write); | |
File.WriteAllBytes(Path.Combine(dest, f.Replace("_0.", "_1.")), write); | |
} | |
private static void UpdateBossText(List<DevID> Bosses) | |
{ | |
TextConfig cfg = new(GameVersion.SV); | |
foreach (string lang in Languages) | |
{ | |
string dest = Path.Combine(DEST_MASTER, $"message/dat/{lang}/script/"); | |
string[] names = GetStringsCommon(lang, "monsname"); | |
string[] lines = GetStringsScript(lang, "futatsuna"); | |
lines[08] = names[(int)Bosses[0]]; | |
lines[09] = names[(int)Bosses[1]]; | |
lines[10] = names[(int)Bosses[2]]; | |
lines[11] = names[(int)Bosses[3]]; | |
lines[12] = names[(int)Bosses[4]]; | |
lines[13] = names[(int)Bosses[5]]; | |
lines[14] = names[(int)Bosses[5]]; | |
lines[15] = names[(int)Bosses[6]]; | |
lines[16] = names[(int)Bosses[7]]; | |
if (!Directory.Exists(dest)) | |
Directory.CreateDirectory(dest); | |
byte[] write = TextFile.GetBytes(lines, cfg); | |
File.WriteAllBytes(Path.Combine(dest, "futatsuna.dat"), write); | |
} | |
} | |
private static PokeDataEventBattle ApplyParadiseSoftlockPrevention(PokeDataEventBattle t) | |
{ | |
t.GemType = GemType.NIJI; // always deals neutral damage | |
// Ogerpon can't be Stellar Tera Type, and we specifically want Cornerstone Mask to avoid additional potential softlocks | |
if (t.DevId is DevID.DEV_KAMENONI) | |
{ | |
t.FormId = 3; | |
t.GemType = GemType.IWA; | |
if (ENSURE_SANE_FORM_HELD_ITEMS) | |
t.Item = ItemID.ITEMID_ISHIDUENOMEN; | |
} | |
// Tera Starstorm for Terapagos, Tera Blast for all others | |
WazaID tera = t.DevId is DevID.DEV_KODAIGAME ? WazaID.WAZA_TERAKURASUTAA : WazaID.WAZA_TERABAASUTO; | |
List<ushort> Moves = GetCurrentMoves((ushort)t.DevId, (byte)t.FormId, (byte)t.Level); | |
t.WazaType = WazaType.MANUAL; | |
t.Waza1.WazaId = (WazaID)Moves[0]; | |
t.Waza2.WazaId = (WazaID)Moves[1]; | |
t.Waza3.WazaId = (WazaID)Moves[2]; | |
t.Waza4.WazaId = tera; // the game is scripted to eventually spam move slot 4 if the battle has not yet ended | |
// if the Pokémon knows less than 4 moves, we need to fill in the blanks with other moves | |
int numEmptyMoves = Moves.Count(z => z is 0); | |
if (numEmptyMoves is 3) | |
{ | |
t.Waza2.WazaId = WazaID.WAZA_TURUGINOMAI; // Swords Dance | |
t.Waza3.WazaId = WazaID.WAZA_WARUDAKUMI; // Nasty Plot | |
} | |
if (numEmptyMoves is 2) | |
t.Waza3.WazaId = WazaID.WAZA_TUBOWOTUKU; // Acupressure | |
return t; | |
} | |
private static PokeDataEventBattle ApplyTerapagosSoftlockPrevention(PokeDataEventBattle t) | |
{ | |
// Tera Starstorm for Terapagos, Tera Blast for all others | |
WazaID tera = t.DevId is DevID.DEV_KODAIGAME ? WazaID.WAZA_TERAKURASUTAA : WazaID.WAZA_TERABAASUTO; | |
List<ushort> Moves = GetCurrentMoves((ushort)t.DevId, (byte)t.FormId, (byte)t.Level); | |
// always put Tera Blast/Starstorm in final move slot | |
int numEmptyMoves = Moves.Count(z => z is 0); | |
if (numEmptyMoves is 3) Moves[1] = (ushort)tera; Moves[2] = Moves[3] = 0; | |
if (numEmptyMoves is 2) Moves[2] = (ushort)tera; Moves[3] = 0; | |
if (numEmptyMoves is 1) Moves[3] = (ushort)tera; | |
t.WazaType = WazaType.MANUAL; | |
t.Waza1.WazaId = (WazaID)Moves[0]; | |
t.Waza2.WazaId = (WazaID)Moves[1]; | |
t.Waza3.WazaId = (WazaID)Moves[2]; | |
t.Waza4.WazaId = (WazaID)Moves[3]; | |
return t; | |
} | |
private static EventBattlePokemonArray StandardizeStaticEncounters(EventBattlePokemonArray obj) | |
{ | |
var table = obj.Table; | |
// Scream Tail / Iron Bundle | |
table[001].PokeData.DevId = table[002].PokeData.DevId; | |
table[001].PokeData.FormId = table[002].PokeData.FormId; | |
table[001].PokeData.Sex = table[002].PokeData.Sex; | |
table[001].PokeData.Item = table[002].PokeData.Item; | |
table[001].PokeData.GemType = table[002].PokeData.GemType; | |
// Great Tusk / Iron Treads (Area Zero) | |
table[003].PokeData.DevId = table[004].PokeData.DevId; | |
table[003].PokeData.FormId = table[004].PokeData.FormId; | |
table[003].PokeData.Sex = table[004].PokeData.Sex; | |
table[003].PokeData.Item = table[004].PokeData.Item; | |
table[003].PokeData.GemType = table[004].PokeData.GemType; | |
// Great Tusk, Brute Bonnet, Flutter Mane / Iron Treads, Iron Hands, Iron Jugulis | |
table[005].PokeData.DevId = table[008].PokeData.DevId; | |
table[005].PokeData.FormId = table[008].PokeData.FormId; | |
table[005].PokeData.Sex = table[008].PokeData.Sex; | |
table[005].PokeData.Item = table[008].PokeData.Item; | |
table[005].PokeData.GemType = table[008].PokeData.GemType; | |
table[006].PokeData.DevId = table[009].PokeData.DevId; | |
table[006].PokeData.FormId = table[009].PokeData.FormId; | |
table[006].PokeData.Sex = table[009].PokeData.Sex; | |
table[006].PokeData.Item = table[009].PokeData.Item; | |
table[006].PokeData.GemType = table[009].PokeData.GemType; | |
table[007].PokeData.DevId = table[010].PokeData.DevId; | |
table[007].PokeData.FormId = table[010].PokeData.FormId; | |
table[007].PokeData.Sex = table[010].PokeData.Sex; | |
table[007].PokeData.Item = table[010].PokeData.Item; | |
table[007].PokeData.GemType = table[010].PokeData.GemType; | |
// Gimmighoul | |
for (int i = 012; i <= 023; i++) | |
{ | |
table[i].PokeData.DevId = table[011].PokeData.DevId; | |
table[i].PokeData.FormId = table[011].PokeData.FormId; | |
table[i].PokeData.Sex = table[011].PokeData.Sex; | |
table[i].PokeData.Item = table[011].PokeData.Item; | |
table[i].PokeData.GemType = table[011].PokeData.GemType; | |
} | |
// Sunflora | |
for (int i = 027; i <= 030; i++) | |
{ | |
table[i].PokeData.DevId = table[026].PokeData.DevId; | |
table[i].PokeData.FormId = table[026].PokeData.FormId; | |
table[i].PokeData.Sex = table[026].PokeData.Sex; | |
table[i].PokeData.Item = table[026].PokeData.Item; | |
table[i].PokeData.GemType = table[026].PokeData.GemType; | |
} | |
// Dondozo | |
table[033].PokeData.DevId = table[034].PokeData.DevId; | |
table[033].PokeData.FormId = table[034].PokeData.FormId; | |
table[033].PokeData.Sex = table[034].PokeData.Sex; | |
table[033].PokeData.Item = table[034].PokeData.Item; | |
table[033].PokeData.GemType = table[034].PokeData.GemType; | |
// Orthworm | |
table[035].PokeData.DevId = table[036].PokeData.DevId; | |
table[035].PokeData.FormId = table[036].PokeData.FormId; | |
table[035].PokeData.Sex = table[036].PokeData.Sex; | |
table[035].PokeData.Item = table[036].PokeData.Item; | |
table[035].PokeData.GemType = table[036].PokeData.GemType; | |
// Bombirdier | |
table[041].PokeData.DevId = table[042].PokeData.DevId; | |
table[041].PokeData.FormId = table[042].PokeData.FormId; | |
table[041].PokeData.Sex = table[042].PokeData.Sex; | |
table[041].PokeData.Item = table[042].PokeData.Item; | |
table[041].PokeData.GemType = table[042].PokeData.GemType; | |
// Klawf | |
table[043].PokeData.DevId = table[044].PokeData.DevId; | |
table[043].PokeData.FormId = table[044].PokeData.FormId; | |
table[043].PokeData.Sex = table[044].PokeData.Sex; | |
table[043].PokeData.Item = table[044].PokeData.Item; | |
table[043].PokeData.GemType = table[044].PokeData.GemType; | |
// Great Tusk / Iron Treads (Titan) | |
table[045].PokeData.DevId = table[046].PokeData.DevId = table[047].PokeData.DevId = table[048].PokeData.DevId; | |
table[045].PokeData.FormId = table[046].PokeData.FormId = table[047].PokeData.FormId = table[048].PokeData.FormId; | |
table[045].PokeData.Sex = table[046].PokeData.Sex = table[047].PokeData.Sex = table[048].PokeData.Sex; | |
table[045].PokeData.Item = table[046].PokeData.Item = table[047].PokeData.Item = table[048].PokeData.Item; | |
table[045].PokeData.GemType = table[046].PokeData.GemType = table[047].PokeData.GemType = table[048].PokeData.GemType; | |
// Munkidori | |
table[053].PokeData.DevId = table[054].PokeData.DevId = table[057].PokeData.DevId = table[072].PokeData.DevId = table[073].PokeData.DevId; | |
table[053].PokeData.FormId = table[054].PokeData.FormId = table[057].PokeData.FormId = table[072].PokeData.FormId = table[073].PokeData.FormId; | |
table[053].PokeData.Sex = table[054].PokeData.Sex = table[057].PokeData.Sex = table[072].PokeData.Sex = table[073].PokeData.Sex; | |
table[053].PokeData.Item = table[054].PokeData.Item = table[057].PokeData.Item = table[072].PokeData.Item = table[073].PokeData.Item; | |
table[053].PokeData.GemType = table[054].PokeData.GemType = table[057].PokeData.GemType = table[072].PokeData.GemType = table[073].PokeData.GemType; | |
// Okidogi | |
table[055].PokeData.DevId = table[068].PokeData.DevId = table[069].PokeData.DevId; | |
table[055].PokeData.FormId = table[068].PokeData.FormId = table[069].PokeData.FormId; | |
table[055].PokeData.Sex = table[068].PokeData.Sex = table[069].PokeData.Sex; | |
table[055].PokeData.Item = table[068].PokeData.Item = table[069].PokeData.Item; | |
table[055].PokeData.GemType = table[068].PokeData.GemType = table[069].PokeData.GemType; | |
// Fezandipiti | |
table[056].PokeData.DevId = table[070].PokeData.DevId = table[071].PokeData.DevId; | |
table[056].PokeData.FormId = table[070].PokeData.FormId = table[071].PokeData.FormId; | |
table[056].PokeData.Sex = table[070].PokeData.Sex = table[071].PokeData.Sex; | |
table[056].PokeData.Item = table[070].PokeData.Item = table[071].PokeData.Item; | |
table[056].PokeData.GemType = table[070].PokeData.GemType = table[071].PokeData.GemType; | |
// Ogerpon | |
for (int i = 059; i <= 065; i++) | |
{ | |
table[i].PokeData.DevId = table[058].PokeData.DevId; | |
table[i].PokeData.FormId = table[058].PokeData.FormId; | |
table[i].PokeData.Sex = table[058].PokeData.Sex; | |
table[i].PokeData.Item = table[058].PokeData.Item; | |
table[i].PokeData.GemType = table[058].PokeData.GemType; | |
} | |
// Milotic | |
table[066].PokeData.DevId = table[067].PokeData.DevId; | |
table[066].PokeData.FormId = table[067].PokeData.FormId; | |
table[066].PokeData.Sex = table[067].PokeData.Sex; | |
table[066].PokeData.Item = table[067].PokeData.Item; | |
table[066].PokeData.GemType = table[067].PokeData.GemType; | |
// Sandy Shocks / Iron Thorns | |
table[082].PokeData.DevId = table[083].PokeData.DevId; | |
table[082].PokeData.FormId = table[083].PokeData.FormId; | |
table[082].PokeData.Sex = table[083].PokeData.Sex; | |
table[082].PokeData.Item = table[083].PokeData.Item; | |
table[082].PokeData.GemType = table[083].PokeData.GemType; | |
// Terapagos | |
table[087].PokeData.DevId = table[088].PokeData.DevId; | |
table[087].PokeData.FormId = table[088].PokeData.FormId; | |
table[087].PokeData.Sex = table[088].PokeData.Sex; | |
table[087].PokeData.Item = table[088].PokeData.Item; | |
table[087].PokeData.GemType = table[088].PokeData.GemType; | |
if (RANDOMIZE_KORAIDON_MIRAIDON) | |
{ | |
table[031].PokeData.DevId = table[032].PokeData.DevId = table[115].PokeData.DevId = table[116].PokeData.DevId; | |
table[031].PokeData.FormId = table[032].PokeData.FormId = table[115].PokeData.FormId = table[116].PokeData.FormId; | |
table[031].PokeData.Sex = table[032].PokeData.Sex = table[115].PokeData.Sex = table[116].PokeData.Sex; | |
table[031].PokeData.Tokusei = table[032].PokeData.Tokusei = table[115].PokeData.Tokusei = table[116].PokeData.Tokusei; | |
table[031].PokeData.Item = table[032].PokeData.Item = table[115].PokeData.Item = table[116].PokeData.Item; | |
table[031].PokeData.GemType = table[032].PokeData.GemType = table[115].PokeData.GemType = table[116].PokeData.GemType; | |
table[031].PokeData.WazaType = table[032].PokeData.WazaType = table[115].PokeData.WazaType = table[116].PokeData.WazaType; | |
table[031].PokeData.Waza1 = table[032].PokeData.Waza1 = table[115].PokeData.Waza1 = table[116].PokeData.Waza1; | |
table[031].PokeData.Waza2 = table[032].PokeData.Waza2 = table[115].PokeData.Waza2 = table[116].PokeData.Waza2; | |
table[031].PokeData.Waza3 = table[032].PokeData.Waza3 = table[115].PokeData.Waza3 = table[116].PokeData.Waza3; | |
table[031].PokeData.Waza4 = table[032].PokeData.Waza4 = table[115].PokeData.Waza4 = table[116].PokeData.Waza4; | |
} | |
return obj; | |
} | |
private static EventAddPokemonArray StandardizeGiftEncounters(EventAddPokemonArray objG, EventBattlePokemonArray objS) | |
{ | |
var tableG = objG.Table; | |
var tableS = objS.Table; | |
// make your Koraidon and Miraidon match the professor's | |
tableG[005].PokeData.DevId = tableG[006].PokeData.DevId = tableS[116].PokeData.DevId; | |
tableG[005].PokeData.FormId = tableG[006].PokeData.FormId = tableS[116].PokeData.FormId; | |
tableG[005].PokeData.Sex = tableG[006].PokeData.Sex = tableS[116].PokeData.Sex; | |
tableG[005].PokeData.Tokusei = tableG[006].PokeData.Tokusei = tableS[116].PokeData.Tokusei; | |
tableG[005].PokeData.Item = tableG[006].PokeData.Item = tableS[116].PokeData.Item; | |
tableG[005].PokeData.GemType = tableG[006].PokeData.GemType = tableS[116].PokeData.GemType; | |
tableG[005].PokeData.WazaType = tableG[006].PokeData.WazaType = tableS[116].PokeData.WazaType; | |
tableG[005].PokeData.Waza1 = tableG[006].PokeData.Waza1 = tableS[116].PokeData.Waza1; | |
tableG[005].PokeData.Waza2 = tableG[006].PokeData.Waza2 = tableS[116].PokeData.Waza2; | |
tableG[005].PokeData.Waza3 = tableG[006].PokeData.Waza3 = tableS[116].PokeData.Waza3; | |
tableG[005].PokeData.Waza4 = tableG[006].PokeData.Waza4 = tableS[116].PokeData.Waza4; | |
return objG; | |
} | |
private static TrDataMainArray StandardizeTrainerData(TrDataMainArray objT, EventBattlePokemonArray objS) | |
{ | |
var tableT = objT.Table; | |
var tableS = objS.Table; | |
// make Guardian of Paradise match your legendary | |
if (RANDOMIZE_KORAIDON_MIRAIDON) | |
{ | |
var sada = tableT.FirstOrDefault(z => z.TrId is "professor_A_02").Poke1; | |
var turo = tableT.FirstOrDefault(z => z.TrId is "professor_B_02").Poke1; | |
sada.DevId = turo.DevId = tableS[116].PokeData.DevId; | |
sada.FormId = turo.FormId = tableS[116].PokeData.FormId; | |
sada.Sex = turo.Sex = tableS[116].PokeData.Sex; | |
sada.Tokusei = turo.Tokusei = tableS[116].PokeData.Tokusei; | |
sada.Item = turo.Item = tableS[116].PokeData.Item; | |
sada.GemType = turo.GemType = tableS[116].PokeData.GemType; | |
sada.WazaType = turo.WazaType = tableS[116].PokeData.WazaType; | |
sada.Waza1 = turo.Waza1 = tableS[116].PokeData.Waza1; | |
sada.Waza2 = turo.Waza2 = tableS[116].PokeData.Waza2; | |
sada.Waza3 = turo.Waza3 = tableS[116].PokeData.Waza3; | |
sada.Waza4 = turo.Waza4 = tableS[116].PokeData.Waza4; | |
sada.RareType = turo.RareType = RareType.NO_RARE; | |
sada.BallId = turo.BallId = BallType.MASUTAABOORU; | |
} | |
// make Kieran's Terapagos match the static encounter | |
var tera = tableT.FirstOrDefault(z => z.TrId is "brother_02_02").Poke1; | |
tera.DevId = tableS[088].PokeData.DevId; | |
tera.FormId = tableS[088].PokeData.FormId; | |
tera.Sex = tableS[088].PokeData.Sex; | |
tera.Tokusei = tableS[088].PokeData.Tokusei; | |
tera.Item = tableS[088].PokeData.Item; | |
tera.RareType = RareType.NO_RARE; | |
tera.BallId = BallType.MASUTAABOORU; | |
return objT; | |
} | |
private static void UpdateAllStaticScenes(EventBattlePokemonArray obj) | |
{ | |
var table = obj.Table; | |
// Area Zero Multi Battles | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/common_1055_/common_1055_main_0.trsog", table[000].PokeData.DevId, table[000].PokeData.FormId, table[000].PokeData.Sex); // Glimmora | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/common_1075_/common_1075_main_0.trsog", table[002].PokeData.DevId, table[002].PokeData.FormId, table[002].PokeData.Sex); // Scream Tail / Iron Bundle | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/common_1095_/common_1095_main_0.trsog", table[004].PokeData.DevId, table[004].PokeData.FormId, table[004].PokeData.Sex); // Great Tusk / Iron Treads | |
UpdateOverworldSceneSingle("world/obj_template/parts/coin_symbol/coin_symbol_box_/coin_symbol_box_0.trsot", table[023].PokeData.DevId, table[023].PokeData.FormId, table[023].PokeData.Sex); // Gimmighoul | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/common_0150_/common_0150_main_0.trsog", table[025].PokeData.DevId, table[025].PokeData.FormId, table[025].PokeData.Sex); // Houndoom | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/gym_kusa_poke_finding_/pokes_0.trsog", table[030].PokeData.DevId, table[030].PokeData.FormId, table[030].PokeData.Sex); // Sunflora | |
// Orthworm | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/hagane/nushi_hagane_fp_1048_010_/nushi_hagane_fp_1048_010_0.trsot", table[036].PokeData.DevId, table[036].PokeData.FormId, table[036].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/hagane/nushi_hagane_fp_1048_020_/nushi_hagane_fp_1048_020_0.trsot", table[036].PokeData.DevId, table[036].PokeData.FormId, table[036].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_hagane_010_/nushi_hagane_010_pre_start_0.trsog", table[036].PokeData.DevId, table[036].PokeData.FormId, table[036].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_hagane_020_/nushi_hagane_020_pre_start_0.trsog", table[036].PokeData.DevId, table[036].PokeData.FormId, table[036].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_039_/sub_039_pre_start_0.trsog", table[036].PokeData.DevId, table[036].PokeData.FormId, table[036].PokeData.Sex); | |
// Dondozo | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/dragon/nushi_dragon_fp_1035_010_/nushi_dragon_fp_1035_010_0.trsot", table[034].PokeData.DevId, table[034].PokeData.FormId, table[034].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/dragon/nushi_dragon_fp_1035_020_/nushi_dragon_fp_1035_020_0.trsot", table[034].PokeData.DevId, table[034].PokeData.FormId, table[034].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_dragon_010_/nushi_dragon_010_always_0.trsog", table[034].PokeData.DevId, table[034].PokeData.FormId, table[034].PokeData.Sex); | |
// Tatsugiri | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/dragon/nushi_dragon_fp_1056_010_/nushi_dragon_fp_1056_010_0.trsot", table[037].PokeData.DevId, table[037].PokeData.FormId, table[037].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/dragon/nushi_dragon_fp_1056_020_/nushi_dragon_fp_1056_020_0.trsot", table[037].PokeData.DevId, table[037].PokeData.FormId, table[037].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_dragon_010_/nushi_dragon_010_pre_start_0.trsog", table[037].PokeData.DevId, table[037].PokeData.FormId, table[037].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_040_/sub_040_pre_start_0.trsog", table[037].PokeData.DevId, table[037].PokeData.FormId, table[037].PokeData.Sex); | |
// Bombirdier | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/hiko/nushi_hiko_fp_1063_010_/nushi_hiko_fp_1063_010_0.trsot", table[042].PokeData.DevId, table[042].PokeData.FormId, table[042].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/hiko/nushi_hiko_fp_1063_020_/nushi_hiko_fp_1063_020_0.trsot", table[042].PokeData.DevId, table[042].PokeData.FormId, table[042].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_hikou_010_/nushi_hikou_010_pre_start_0.trsog", table[042].PokeData.DevId, table[042].PokeData.FormId, table[042].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_hikou_020_/nushi_hikou_020_main_0.trsog", table[042].PokeData.DevId, table[042].PokeData.FormId, table[042].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_hikou_020_/nushi_hikou_020_pre_start_0.trsog", table[042].PokeData.DevId, table[042].PokeData.FormId, table[042].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_038_/sub_038_pre_start_0.trsog", table[042].PokeData.DevId, table[042].PokeData.FormId, table[042].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/field/field_contents/nushi/hiko/HikoNushi_/HikoNushi_0.trscn", table[042].PokeData.DevId, table[042].PokeData.FormId, table[042].PokeData.Sex); | |
// Klawf | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/iwa/nushi_iwa_fp_1066_/nushi_iwa_fp_1066_0.trsot", table[044].PokeData.DevId, table[044].PokeData.FormId, table[044].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/iwa/nushi_iwa_fp_1066_020_/nushi_iwa_fp_1066_020_0.trsot", table[044].PokeData.DevId, table[044].PokeData.FormId, table[044].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_iwa_010_/nushi_iwa_010_pre_start_0.trsog", table[044].PokeData.DevId, table[044].PokeData.FormId, table[044].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_iwa_020_/nushi_iwa_020_pre_start_0.trsog", table[044].PokeData.DevId, table[044].PokeData.FormId, table[044].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_037_/sub_037_pre_start_0.trsog", table[044].PokeData.DevId, table[044].PokeData.FormId, table[044].PokeData.Sex); | |
// Great Tusk / Iron Treads (Titan) | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/jimen/nushi_jimen_fp_1082_010_/nushi_jimen_fp_1082_010_0.trsot", table[048].PokeData.DevId, table[048].PokeData.FormId, table[048].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/jimen/nushi_jimen_fp_1082_020_/nushi_jimen_fp_1082_020_0.trsot", table[048].PokeData.DevId, table[048].PokeData.FormId, table[048].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/jimen/nushi_jimen_fp_1090_010_/nushi_jimen_fp_1090_010_0.trsot", table[048].PokeData.DevId, table[048].PokeData.FormId, table[048].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/obj_template/parts/nushi/jimen/nushi_jimen_fp_1090_020_/nushi_jimen_fp_1090_020_0.trsot", table[048].PokeData.DevId, table[048].PokeData.FormId, table[048].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_jimen_010_/nushi_jimen_010_pre_start_0.trsog", table[048].PokeData.DevId, table[048].PokeData.FormId, table[048].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/nushi_jimen_020_/nushi_jimen_020_pre_start_0.trsog", table[048].PokeData.DevId, table[048].PokeData.FormId, table[048].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_041_/sub_041_pre_start_0.trsog", table[048].PokeData.DevId, table[048].PokeData.FormId, table[048].PokeData.Sex); | |
// Treasures of Ruin | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_014_/sub_014_main_0.trsog", table[049].PokeData.DevId, table[049].PokeData.FormId, table[049].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_015_/sub_015_main_0.trsog", table[050].PokeData.DevId, table[050].PokeData.FormId, table[050].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_016_/sub_016_main_0.trsog", table[051].PokeData.DevId, table[051].PokeData.FormId, table[051].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/sub_017_/sub_017_main_0.trsog", table[052].PokeData.DevId, table[052].PokeData.FormId, table[052].PokeData.Sex); | |
// Kitakami | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0300_/sdc01_0300_main_0.trsog", table[067].PokeData.DevId, table[067].PokeData.FormId, table[067].PokeData.Sex); // Milotic | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s1_side02_0030_/s1_side02_0030_main_0.trsog", table[074].PokeData.DevId, table[074].PokeData.FormId, table[074].PokeData.Sex); // Ariados | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s1_side02_0050_/s1_side02_0050_main_0.trsog", table[075].PokeData.DevId, table[075].PokeData.FormId, table[075].PokeData.Sex); // Bloodmoon Ursaluna | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_side02_0010_/s2_side02_0010_pre_start_0.trsog", table[076].PokeData.DevId, table[076].PokeData.FormId, table[076].PokeData.Sex); // Gouging Fire / Iron Boulder | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_side02_0020_/s2_side02_0020_pre_start_0.trsog", table[077].PokeData.DevId, table[077].PokeData.FormId, table[077].PokeData.Sex); // Raging Bolt / Iron Crown | |
// Area Zero Underdepths Stellar Pokémon | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_sub_005_/s2_sub_005_pre_start_0.trsog", table[078].PokeData.DevId, table[078].PokeData.FormId, table[078].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc02_0262_/sdc02_0262_main_0.trsog", table[079].PokeData.DevId, table[079].PokeData.FormId, table[079].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc02_0263_/sdc02_0263_pre_start_0.trsog", table[080].PokeData.DevId, table[080].PokeData.FormId, table[080].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc02_0267_/sdc02_0267_pre_start_0.trsog", table[081].PokeData.DevId, table[081].PokeData.FormId, table[081].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc02_0265_/sdc02_0265_pre_start_0.trsog", table[082].PokeData.DevId, table[082].PokeData.FormId, table[082].PokeData.Sex); | |
// Pecharunt | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_side01_0160_/s2_side01_0160_always_0.trsog", table[086].PokeData.DevId, table[086].PokeData.FormId, table[086].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_side01_0180_/s2_side01_0180_always_0.trsog", table[086].PokeData.DevId, table[086].PokeData.FormId, table[086].PokeData.Sex); | |
// Terapagos | |
UpdateOverworldSceneSingle("world/scene/parts/demo/ev/d730_/d730_0.trscn", table[088].PokeData.DevId, table[088].PokeData.FormId, table[088].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc02_0320_/sdc02_0320_always_0.trsog", table[088].PokeData.DevId, table[088].PokeData.FormId, table[088].PokeData.Sex); | |
// Meloetta | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_sub_003_pop_/s2_sub_003_pop_0.trscn", table[114].PokeData.DevId, table[114].PokeData.FormId, table[114].PokeData.Sex); | |
// Snacksworth Legendary Pokémon | |
List<byte> Snacksworth = [ 092, 112, 102, 104, 091, 109, 100, 095, 098, 099, 096, 094, 105, 089, 110, 111, 106, 113, 097, 107, 101, 103, 090, 093, 108 ]; | |
for (int i = 13, k = 0; i <= 37; i++, k++) | |
{ | |
DevID dev = table[Snacksworth[k]].PokeData.DevId; | |
short form = table[Snacksworth[k]].PokeData.FormId; | |
SexType sex = table[Snacksworth[k]].PokeData.Sex; | |
UpdateOverworldSceneSingle($"world/scene/parts/event/event_scenario/sub_scenario/s2_sub_{i:000}_/s2_sub_{i:000}_pre_start_0.trsog", dev, form, sex); | |
} | |
// Area Zero Paradox Pokémon Gauntlet (Great Tusk/Iron Treads ×2, Brute Bonnet/Iron Hands ×5, Flutter Mane/Iron Jugulis ×1) | |
List<DevID> ParadoxGauntletOld = [ DevID.DEV_ADONFAN, DevID.DEV_AMOROBARERU, DevID.DEV_AMUUMA, DevID.DEV_AMOROBARERU, DevID.DEV_AMOROBARERU, DevID.DEV_ADONFAN, DevID.DEV_AMOROBARERU, DevID.DEV_AMOROBARERU ]; | |
List<DevID> ParadoxGauntletNew = [ table[008].PokeData.DevId, table[009].PokeData.DevId, table[010].PokeData.DevId, table[009].PokeData.DevId, table[009].PokeData.DevId, table[008].PokeData.DevId, table[009].PokeData.DevId, table[009].PokeData.DevId ]; | |
List<short> ParadoxGauntletForm = [ table[008].PokeData.FormId, table[009].PokeData.FormId, table[010].PokeData.FormId, table[009].PokeData.FormId, table[009].PokeData.FormId, table[008].PokeData.FormId, table[009].PokeData.FormId, table[009].PokeData.FormId ]; | |
List<SexType> ParadoxGauntletSex = [ table[008].PokeData.Sex, table[009].PokeData.Sex, table[010].PokeData.Sex, table[009].PokeData.Sex, table[009].PokeData.Sex, table[008].PokeData.Sex, table[009].PokeData.Sex, table[009].PokeData.Sex ]; | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/common_1170_/common_1170_always_0.trsog", ParadoxGauntletOld, ParadoxGauntletNew, ParadoxGauntletForm, ParadoxGauntletSex, false); | |
// Poco Path Catching Tutorial (Lechonk ×5, Tarountula ×4, Pawmi ×1, Fletchling ×2) | |
DevID Tarountula = GetRandomSpecies(0); | |
DevID Pawmi = GetRandomSpecies(0); | |
DevID Fletchling = GetRandomSpecies(0); | |
List<DevID> PocoPathOld = [ DevID.DEV_BUTA1, DevID.DEV_BUTA1, DevID.DEV_BUTA1, DevID.DEV_BUTA1, DevID.DEV_BUTA1, DevID.DEV_KUMO1, DevID.DEV_KUMO1, DevID.DEV_KUMO1, DevID.DEV_KUMO1, DevID.DEV_MAAMOTTO1, DevID.DEV_YAYAKOMA, DevID.DEV_YAYAKOMA ]; | |
List<DevID> PocoPathNew = [ table[024].PokeData.DevId, table[024].PokeData.DevId, table[024].PokeData.DevId, table[024].PokeData.DevId, table[024].PokeData.DevId, (DevID)Tarountula, (DevID)Tarountula, (DevID)Tarountula, (DevID)Tarountula, (DevID)Pawmi, (DevID)Fletchling, (DevID)Fletchling ]; | |
List<short> PocoPathForm = [ table[024].PokeData.FormId, table[024].PokeData.FormId, table[024].PokeData.FormId, table[024].PokeData.FormId, table[024].PokeData.FormId, 0, 0, 0, 0, 0, 0, 0 ]; | |
List<SexType> PocoPathSex = [ table[024].PokeData.Sex, table[024].PokeData.Sex, table[024].PokeData.Sex, table[024].PokeData.Sex, table[024].PokeData.Sex, 0, 0, 0, 0, 0, 0, 0 ]; | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/common_0100_/common_0100_main_0.trsog", PocoPathOld, PocoPathNew, PocoPathForm, PocoPathSex, false); | |
// Casseroya Lake | |
List<DevID> CasseroyaOld = [ DevID.DEV_OYAKATA, DevID.DEV_SUSIDORA, DevID.DEV_YOKUBARISU ]; | |
List<DevID> CasseroyaNew = [ table[034].PokeData.DevId, table[037].PokeData.DevId, DevID.DEV_YOKUBARISU ]; | |
List<short> CasseroyaForm = [ table[034].PokeData.FormId, table[037].PokeData.FormId, 0 ]; | |
List<SexType> CasseroyaSex = [ table[034].PokeData.Sex, table[037].PokeData.Sex, 0 ]; | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/nushi_dragon_020_/nushi_dragon_020_pre_start_0.trsog", CasseroyaOld, CasseroyaNew, CasseroyaForm, CasseroyaSex, false); | |
} | |
private static void UpdateAllGiftScenes(EventAddPokemonArray obj) | |
{ | |
var table = obj.Table; | |
List<DevID> StarterOld = [ DevID.DEV_NEKO1, DevID.DEV_WANI1, DevID.DEV_KAMO1 ]; | |
List<DevID> StarterNew = [ table[001].PokeData.DevId, table[000].PokeData.DevId, table[002].PokeData.DevId ]; | |
List<short> StarterForm = [ table[001].PokeData.FormId, table[000].PokeData.FormId, table[002].PokeData.FormId ]; | |
List<SexType> StarterSex = [ table[001].PokeData.Sex, table[000].PokeData.Sex, table[002].PokeData.Sex ]; | |
// Starter Pokémon | |
UpdateOverworldSceneMulti("world/scene/parts/demo/ev/d030_/d030_0.trscn", StarterOld, StarterNew, StarterForm, StarterSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/common_0060_/common_0060_always_0.trsog", StarterOld, StarterNew, StarterForm, StarterSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/common_0060_/common_0060_main_0.trsog", StarterOld, StarterNew, StarterForm, StarterSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/common_0070_/common_0070_always_0.trsog", StarterOld, StarterNew, StarterForm, StarterSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/common_0088_/common_0088_always_0.trsog", StarterOld, StarterNew, StarterForm, StarterSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/common_0090_/common_0090_main_0.trsog", StarterOld, StarterNew, StarterForm, StarterSex, false); | |
// Hisuian Growlithe | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s1_side02_0000_/s1_side02_0000_always_0.trsog", table[008].PokeData.DevId, table[008].PokeData.FormId, table[008].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s1_side02_0030_/s1_side02_0030_always_0.trsog", table[008].PokeData.DevId, table[008].PokeData.FormId, table[008].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s1_side02_0060_/s1_side02_0060_pre_start_0.trsog", table[008].PokeData.DevId, table[008].PokeData.FormId, table[008].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_side02_0000_/s2_side02_0000_pre_start_0.trsog", table[008].PokeData.DevId, table[008].PokeData.FormId, table[008].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_side02_0005_/s2_side02_0005_pre_start_0.trsog", table[008].PokeData.DevId, table[008].PokeData.FormId, table[008].PokeData.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s2_side02_0030_/s2_side02_0030_always_0.trsog", table[008].PokeData.DevId, table[008].PokeData.FormId, table[008].PokeData.Sex); | |
// crashes and softlocks | |
// world/scene/parts/event/event_scenario/sub_scenario/s1_side02_0040_/targets_fieldwork_0.trsog -- game will crash on starting the fieldwork minigame | |
} | |
private static void UpdateAllIguanaScenes(EventAddPokemonArray obj) | |
{ | |
var table = obj.Table; | |
DevID iguanaDev = table[005].PokeData.DevId; | |
short iguanaForm = table[005].PokeData.FormId; | |
SexType iguanaSex = table[005].PokeData.Sex; | |
List<string> IguanaScenes = | |
[ | |
"world/scene/parts/demo/ev/d040_/d040_0.trscn", | |
"world/scene/parts/demo/ev/d400_/d400_0.trscn", | |
"world/scene/parts/demo/ev/d410_/d410_0.trscn", | |
"world/scene/parts/demo/ev/d500_/d500_0.trscn", | |
"world/scene/parts/demo/ev/d740_/d740_0.trscn", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0135_/common_0135_post_end_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0140_legend_action_02_/common_0140_legend_action_02_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0170_/common_0170_always_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0170_/common_0170_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0185_/common_0185_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0270_/common_0270_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0310_/common_0310_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_1050_/common_1050_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_1150_/common_1150_always_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_1190_/common_1190_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_1190_/common_1190_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_1290_/common_1290_always_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/legend_0020_/legend_0020_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc01_side01_010_/sdc01_side01_010_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_0250_/sdc02_0250_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_b_02_/sdc02_4kings_b_02_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/team_0050_/team_0050_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/team_0070_/team_0070_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/team_0090_/team_0090_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/s2_sub_039_/s2_sub_039_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/sub_018_/sub_018_pre_start_0.trsog", | |
]; | |
foreach (string t in IguanaScenes) | |
UpdateOverworldSceneSingle(t, iguanaDev, iguanaForm, iguanaSex); | |
List<DevID> InletGrottoOld = [ DevID.DEV_AIGUANA, DevID.DEV_DERUBIRU, DevID.DEV_DERUBIRU, DevID.DEV_DERUBIRU, DevID.DEV_DERUBIRU, DevID.DEV_DERUBIRU, DevID.DEV_DERUBIRU, DevID.DEV_DERUBIRU, DevID.DEV_DERUBIRU ]; | |
List<DevID> InletGrottoNew = [ iguanaDev ]; | |
List<short> InletGrottoForm = [ iguanaForm ]; | |
List<SexType> InletGrottoSex = [ iguanaSex ]; | |
for (int i = 0; i < 8; i++) | |
{ | |
if (RANDOMIZE_GENERIC_OVERWORLD_POKEMON) | |
{ | |
DevID species = GetRandomSpecies(0); | |
short form = GetRandomForm(species); | |
SexType sex = GetRandomGender(species, form); | |
InletGrottoNew.Add(species); | |
InletGrottoForm.Add(form); | |
InletGrottoSex.Add(sex); | |
} | |
else | |
{ | |
InletGrottoNew.Add(DevID.DEV_DERUBIRU); | |
InletGrottoForm.Add(0); | |
InletGrottoSex.Add(0); | |
} | |
} | |
UpdateOverworldSceneMulti("world/scene/parts/demo/ev/d050_/d050_0.trscn", InletGrottoOld, InletGrottoNew, InletGrottoForm, InletGrottoSex, false); | |
DevID MabosstiffSpecies = RANDOMIZE_GENERIC_OVERWORLD_POKEMON ? GetRandomSpecies(0) : DevID.DEV_MASUTHIHU2; | |
short MabosstiffForm = RANDOMIZE_GENERIC_OVERWORLD_POKEMON ? GetRandomForm(MabosstiffSpecies) : (short)0; | |
SexType MabosstiffSex = RANDOMIZE_GENERIC_OVERWORLD_POKEMON ? GetRandomGender(MabosstiffSpecies, MabosstiffForm) : 0; | |
List<DevID> PathOfLegendsOld = [ DevID.DEV_AIGUANA, DevID.DEV_MASUTHIHU2 ]; | |
List<DevID> PathOfLegendsNew = [ iguanaDev, MabosstiffSpecies ]; | |
List<short> PathOfLegendsForm = [ iguanaForm, MabosstiffForm ]; | |
List<SexType> PathOfLegendsSex = [ iguanaSex, MabosstiffSex ]; | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/legend_0030_/legend_0030_main_0.trsog", PathOfLegendsOld, PathOfLegendsNew, PathOfLegendsForm, PathOfLegendsSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/legend_0040_/legend_0040_main_0.trsog", PathOfLegendsOld, PathOfLegendsNew, PathOfLegendsForm, PathOfLegendsSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/legend_0050_/legend_0050_main_0.trsog", PathOfLegendsOld, PathOfLegendsNew, PathOfLegendsForm, PathOfLegendsSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/legend_0060_/legend_0060_always_0.trsog", PathOfLegendsOld, PathOfLegendsNew, PathOfLegendsForm, PathOfLegendsSex, false); | |
List<DevID> CrystalPoolOld = [ DevID.DEV_AIGUANA, DevID.DEV_KODAIGAME ]; | |
List<DevID> CrystalPoolNew = [ iguanaDev, DevID.DEV_KODAIGAME ]; | |
List<short> CrystalPoolForm = [ iguanaForm, 0 ]; | |
List<SexType> CrystalPoolSex = [ iguanaSex, 0 ]; | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/sub_scenario/s2_sub_042_/s2_sub_042_always_0.trsog", CrystalPoolOld, CrystalPoolNew, CrystalPoolForm, CrystalPoolSex, false); | |
// crashes and softlocks | |
// world/scene/parts/event/event_scenario/main_scenario/common_0150_/common_0150_pre_start_0.trsog -- randomized Pokémon does not walk inside Inlet Grotto, softlocking the game | |
} | |
private static void UpdateAllKitakamiScenes(EventBattlePokemonArray obj) | |
{ | |
var table = obj.Table; | |
DevID OkidogiDev = table[069].PokeData.DevId; | |
short OkidogiForm = table[069].PokeData.FormId; | |
SexType OkidogiSex = table[069].PokeData.Sex; | |
DevID MunkidoriDev = table[073].PokeData.DevId; | |
short MunkidoriForm = table[073].PokeData.FormId; | |
SexType MunkidoriSex = table[073].PokeData.Sex; | |
DevID FezandipitiDev = table[071].PokeData.DevId; | |
short FezandipitiForm = table[071].PokeData.FormId; | |
SexType FezandipitiSex = table[071].PokeData.Sex; | |
DevID OgerponDev = table[065].PokeData.DevId; | |
short OgerponForm = table[065].PokeData.FormId; | |
SexType OgerponSex = table[065].PokeData.Sex; | |
List<DevID> KitakamiLegendsOld = [ DevID.DEV_KAMENONI, DevID.DEV_DOKUINU, DevID.DEV_DOKUZARU, DevID.DEV_DOKUKIZI ]; | |
List<DevID> KitakamiLegendsNew = [ OgerponDev, OkidogiDev, MunkidoriDev, FezandipitiDev ]; | |
List<short> KitakamiLegendsForm = [ OgerponForm, OkidogiForm, MunkidoriForm, FezandipitiForm ]; | |
List<SexType> KitakamiLegendsSex = [ OgerponSex, OkidogiSex, MunkidoriSex, FezandipitiSex ]; | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/sdc01_0330_/sdc01_0330_always_0.trsog", KitakamiLegendsOld, KitakamiLegendsNew, KitakamiLegendsForm, KitakamiLegendsSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/sdc01_0360_/sdc01_0360_pre_start_0.trsog", KitakamiLegendsOld, KitakamiLegendsNew, KitakamiLegendsForm, KitakamiLegendsSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/sdc01_0400_/sdc01_0400_main_0.trsog", KitakamiLegendsOld, KitakamiLegendsNew, KitakamiLegendsForm, KitakamiLegendsSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/sdc01_0410_/sdc01_0410_main_0.trsog", KitakamiLegendsOld, KitakamiLegendsNew, KitakamiLegendsForm, KitakamiLegendsSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/sdc01_0420_/sdc01_0420_main_0.trsog", KitakamiLegendsOld, KitakamiLegendsNew, KitakamiLegendsForm, KitakamiLegendsSex, false); | |
// Loyal Three | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_3gods_a_/sdc01_3gods_a_pre_start_0.trsog", OkidogiDev, OkidogiForm, OkidogiSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s1_sub_011_/s1_sub_011_pre_start_0.trsog", OkidogiDev, OkidogiForm, OkidogiSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_3gods_b_/sdc01_3gods_b_pre_start_0.trsog", MunkidoriDev, MunkidoriForm, MunkidoriSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s1_sub_012_/s1_sub_012_pre_start_0.trsog", MunkidoriDev, MunkidoriForm, MunkidoriSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_3gods_c_/sdc01_3gods_c_pre_start_0.trsog", FezandipitiDev, FezandipitiForm, FezandipitiSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_3gods_c_before_/sdc01_3gods_c_before_pre_start_0.trsog", FezandipitiDev, FezandipitiForm, FezandipitiSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/sub_scenario/s1_sub_016_/s1_sub_016_pre_start_0.trsog", FezandipitiDev, FezandipitiForm, FezandipitiSex); | |
// Ogerpon | |
UpdateOverworldSceneSingle("world/scene/parts/demo/ev/d610_/d610_0.trscn", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0105_/sdc01_0105_main_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0130_/sdc01_0130_always_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0160_/sdc01_0160_always_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0370_/sdc01_0370_post_end_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0390_/sdc01_0390_main_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0430_/sdc01_0430_main_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0440_/sdc01_0440_always_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0460_/sdc01_0460_main_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_0480_/sdc01_0480_main_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_3gods_a_/sdc01_3gods_a_main_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_3gods_b_/sdc01_3gods_b_main_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
UpdateOverworldSceneSingle("world/scene/parts/event/event_scenario/main_scenario/sdc01_3gods_c_/sdc01_3gods_c_main_0.trsog", OgerponDev, OgerponForm, OgerponSex); | |
} | |
private static void UpdateAllTrainerScenes(TrDataMainArray obj) | |
{ | |
var table = obj.Table; | |
PokeDataBattle Shellder = obj.Table.FirstOrDefault(z => z.TrId is "pepper_nusi_01").Poke1; | |
PokeDataBattle Toedscool = obj.Table.FirstOrDefault(z => z.TrId is "pepper_nusi_02").Poke1; | |
PokeDataBattle Nacli = obj.Table.FirstOrDefault(z => z.TrId is "pepper_nusi_03").Poke1; | |
PokeDataBattle Scovillain = obj.Table.FirstOrDefault(z => z.TrId is "pepper_nusi_04").Poke1; | |
PokeDataBattle Greedent = obj.Table.FirstOrDefault(z => z.TrId is "pepper_nusi_05").Poke1; | |
List<DevID> TitanRockOld = [ 0, 0, DevID.DEV_SHERUDAA ]; | |
List<DevID> TitanRockNew = [ 0, 0, Shellder.DevId ]; | |
List<short> TitanRockForm = [ 0, 0, Shellder.FormId ]; | |
List<SexType> TitanRockSex = [ 0, 0, Shellder.Sex ]; | |
List<DevID> TitanDragonOld = [ 0, 0, DevID.DEV_YOKUBARISU ]; | |
List<DevID> TitanDragonNew = [ 0, 0, Greedent.DevId ]; | |
List<short> TitanDragonForm = [ 0, 0, Greedent.FormId ]; | |
List<SexType> TitanDragonSex = [ 0, 0, Greedent.Sex ]; | |
UpdateOverworldSceneSingle("world/scene/parts/field/field_contents/nushi/hagane/HaganeRockClashEvent_/HaganeRockClashEvent_0.trscn", Toedscool.DevId, Toedscool.FormId, Toedscool.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/field/field_contents/nushi/hiko/HikoRockClashEvent_/HikoRockClashEvent_0.trscn", Nacli.DevId, Nacli.FormId, Nacli.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/field/field_contents/nushi/jimen/JimenRockClashEvent_/JimenRockClashEvent_0.trscn", Scovillain.DevId, Scovillain.FormId, Scovillain.Sex); | |
UpdateOverworldSceneSingle("world/scene/parts/field/field_contents/nushi/dragon/DragonRockClashEvent_/DragonRockClashEvent_0.trscn", Greedent.DevId, Greedent.FormId, Greedent.Sex); | |
UpdateOverworldSceneMulti("world/scene/parts/field/field_contents/nushi/common/RockClashEvent_/RockClashEvent_0.trscn", TitanRockOld, TitanRockNew, TitanRockForm, TitanRockSex, false); | |
UpdateOverworldSceneMulti("world/scene/parts/event/event_scenario/main_scenario/nushi_dragon_020_/nushi_dragon_020_pre_start_0.trsog", TitanDragonOld, TitanDragonNew, TitanDragonForm, TitanDragonSex, true); | |
} | |
private static void AddCustomItemData() | |
{ | |
"Preparing randomizer changes...".Dump(); | |
// only English is supported right now; translations are welcomed! | |
List<string> ItemStringFiles = [ "iteminfo", "itemname", "itemname_acc", "itemname_acc_classified", "itemname_classified", "itemname_plural", "itemname_plural_classified" ]; | |
TextConfig cfg = new(GameVersion.SV); | |
const ushort BLACK_AUGURITE_INDEX_OLD = 1691; | |
const ushort BLACK_AUGURITE_INDEX_NEW = 2351; | |
const ushort PEAT_BLOCK_INDEX_OLD = 1692; | |
const ushort PEAT_BLOCK_INDEX_NEW = 2352; | |
const ushort FOREIGN_TREAT_INDEX = 2353; | |
const ushort LINKING_CORD_INDEX_OLD = 1611; | |
const ushort LINKING_CORD_INDEX_NEW = 2354; | |
// update item strings | |
for (int i = 0; i < Languages.Count; i++) | |
{ | |
string lang = Languages[i]; | |
foreach (string t in ItemStringFiles) | |
{ | |
string destStrings = Path.Combine(DEST_MASTER, $"message/dat/{lang}/common/"); | |
string[] lines = GetStringsCommon(lang, t); | |
lines[BLACK_AUGURITE_INDEX_NEW] = lines[BLACK_AUGURITE_INDEX_OLD]; | |
lines[PEAT_BLOCK_INDEX_NEW] = lines[PEAT_BLOCK_INDEX_OLD]; | |
lines[LINKING_CORD_INDEX_NEW] = lines[LINKING_CORD_INDEX_OLD]; | |
if (t is "iteminfo") | |
lines[FOREIGN_TREAT_INDEX] = ForeignTreatDescriptions[i]; | |
else if (t.Contains("plural")) | |
lines[FOREIGN_TREAT_INDEX] = ForeignTreatPlurals[i]; | |
else | |
lines[FOREIGN_TREAT_INDEX] = ForeignTreatSingles[i]; | |
if (!Directory.Exists(destStrings)) | |
Directory.CreateDirectory(destStrings); | |
byte[] write = TextFile.GetBytes(lines, cfg); | |
string destName = t + ".dat"; | |
File.WriteAllBytes(Path.Combine(destStrings, destName), write); | |
} | |
} | |
// update item icons | |
for (int i = 2351; i <= 2354; i++) | |
{ | |
string destIcon = Path.Combine(DEST_MASTER, $"appli/tex/icon_item/item_{i}/"); | |
int original = i is 2353 or 2354 ? 2522 : i - 660; | |
if (!Directory.Exists(destIcon)) | |
Directory.CreateDirectory(destIcon); | |
File.Copy(Path.Combine(PATH_MASTER, $"appli/tex/icon_item/item_{original}/item_{original}.bntx"), Path.Combine(destIcon, $"item_{i}.bntx"), true); | |
} | |
} | |
private static void GenerateRandomizerLogFile() | |
{ | |
"Generating randomizer log file...".Dump(); | |
DateTime dt = DateTime.Now; | |
string date = dt.ToString("dddd, MMMM d, yyyy"); | |
string time = dt.ToString("HH:mm:ss"); | |
RandomizerLog.Insert(0, "POKÉMON SCARLET / POKÉMON VIOLET - RANDOMIZER"); | |
RandomizerLog.Insert(1, $"Randomized on: {date}"); | |
RandomizerLog.Insert(2, $"Randomized at: {time}"); | |
RandomizerLog.Insert(3, "The randomizer can be found at: https://gist.github.com/sora10pls/07d69600241f3a47959b802560ab8900"); | |
RandomizerLog.Insert(4, "If you experience any issues or have any questions or suggestions, please reach out with a comment at the above URL."); | |
RandomizerLog.Insert(5, ""); | |
File.WriteAllLines(Path.Combine(DEST_LOG, "Randomizer Log.txt"), RandomizerLog); | |
} | |
private static void FinalizeRandomizerData() | |
{ | |
"Compressing randomizer data...".Dump(); | |
if (File.Exists(DEST_ZIP_ARCHIVE)) | |
File.Delete(DEST_ZIP_ARCHIVE); | |
ZipFile.CreateFromDirectory(DEST_MASTER, DEST_ZIP_ARCHIVE); | |
Process.Start("explorer.exe", DEST_LOG.Replace("/", "\\")); | |
"".Dump(); | |
"Randomization is now complete!".Dump(); | |
"In order to play your randomizer, run TrinityModLoader and apply a mod using the generated .zip file.".Dump(); | |
} | |
private static List<ushort> Legendary = | |
[ | |
0144, 0145, 0146, 0150, | |
0243, 0244, 0245, 0249, 0250, | |
0377, 0378, 0379, 0380, 0381, 0382, 0383, 0384, | |
0480, 0481, 0482, 0483, 0484, 0485, 0486, 0487, 0488, | |
0638, 0639, 0640, 0641, 0642, 0643, 0644, 0645, 0646, | |
0716, 0717, 0718, | |
0772, 0773, 0785, 0786, 0787, 0788, 0789, 0790, 0791, 0792, 0800, | |
0888, 0889, 0890, 0891, 0892, 0894, 0895, 0896, 0897, 0898, 0905, | |
1001, 1002, 1003, 1004, 1007, 1008, 1014, 1015, 1016, 1017, 1024, | |
]; | |
private static List<ushort> Mythical = | |
[ | |
0151, | |
0251, | |
0385, 0386, | |
0489, 0490, 0491, 0492, 0493, | |
0494, 0647, 0648, 0649, | |
0719, 0720, 0721, | |
0801, 0802, 0807, 0808, 0809, | |
0893, | |
1025, | |
]; | |
private static List<int> UltraBeast = Enumerable.Range(0793, 7).Concat(Enumerable.Range(0803, 4)).ToList(); | |
private static List<int> Paradox = Enumerable.Range(0984, 12).Concat(Enumerable.Range(1005, 2)).Concat(Enumerable.Range(1009, 2)).Concat(Enumerable.Range(1020, 4)).ToList(); | |
private static List<ushort> BattleOnlyForms = | |
[ | |
0778, // Mimikyu | |
0845, // Cramorant | |
0875, // Eiscue | |
0877, // Morpeko | |
0964, // Palafin | |
1024, // Terapagos | |
]; | |
private static List<ushort> UnavailableForms = | |
[ | |
// Mega Evolutions | |
0003, 0006, 0009, 0015, 0018, 0065, 0080, 0094, 0115, 0127, 0130, 0142, 0150, | |
0181, 0208, 0212, 0214, 0229, 0248, | |
0254, 0257, 0260, 0282, 0302, 0303, 0306, 0308, 0310, 0319, 0323, 0334, 0354, 0359, 0362, 0373, 0376, 0380, 0381, 0384, | |
0428, 0445, 0448, 0460, 0475, | |
0531, | |
0719, | |
0133, // Partner Eevee | |
0382, // Primal Kyogre | |
0383, // Primal Groudon | |
]; | |
private static bool IsGenderVariant(ushort species, short form) => species switch | |
{ | |
0003 or 0012 or 0041 or 0042 or 0044 or 0045 or 0064 or 0065 or 0084 or 0085 or 0097 or 0111 or 0112 or 0118 or 0119 or 0123 or 0129 or 0130 or 0133 => true, | |
0154 or 0165 or 0166 or 0178 or 0185 or 0186 or 0190 or 0195 or 0198 or 0202 or 0203 or 0207 or 0208 or 0212 or 0214 or 0215 or 0217 or 0221 or 0224 or 0229 or 0232 => true, | |
0255 or 0256 or 0257 or 0269 or 0272 or 0274 or 0275 or 0307 or 0308 or 0315 or 0316 or 0317 or 0322 or 0323 or 0332 or 0350 or 0369 => true, | |
0396 or 0397 or 0398 or 0399 or 0400 or 0401 or 0402 or 0403 or 0404 or 0405 or 0407 or 0415 or 0417 or 0418 or 0419 or 0424 or 0443 or 0444 or 0445 or 0449 or 0450 or 0453 or 0454 or 0456 or 0457 or 0459 or 0460 or 0461 or 0464 or 0465 or 0473 => true, | |
0521 or 0592 or 0593 => true, | |
0668 => true, | |
0019 or 0020 when form is 0 => true, // Rattata, Raticate | |
0025 or 0026 when form is 0 => true, // Pikachu, Raichu | |
0194 when form is 0 => true, // Wooper | |
_ => false, | |
}; | |
private static bool GetCanFlyOrLevitate(ushort species, short form) => species switch | |
{ | |
0006 or 0039 or 0040 or 0049 or 0070 or 0071 or 0074 or 0081 or 0082 or 0092 or 0093 or 0094 or 0109 or 0110 or 0123 or 0137 or 0144 or 0146 or 0149 or 0150 or 0151 => true, | |
0163 or 0164 or 0174 or 0182 or 0187 or 0188 or 0189 or 0193 or 0198 or 0200 or 0205 or 0207 or 0214 or 0225 or 0227 or 0233 or 0250 => true, | |
0278 or 0279 or 0284 or 0313 or 0314 or 0329 or 0330 or 0333 or 0334 or 0353 or 0355 or 0357 or 0358 or 0362 or 0373 or 0374 or 0375 or 0376 or 0380 or 0381 or 0384 or 0385 or 0386 => true, | |
0396 or 0397 or 0398 or 0402 or 0415 or 0416 or 0425 or 0426 or 0429 or 0430 or 0433 or 0436 or 0437 or 0462 or 0469 or 0472 or 0474 or 0476 or 0477 or 0478 or 0479 or 0480 or 0481 or 0482 or 0483 or 0484 or 0487 or 0488 or 0491 or 0493 => true, | |
0546 or 0547 or 0577 or 0578 or 0579 or 0608 or 0609 or 0615 or 0627 or 0628 or 0630 or 0635 or 0637 or 0641 or 0642 or 0643 or 0644 or 0645 => true, | |
0661 or 0662 or 0663 or 0666 or 0669 or 0670 or 0671 or 0686 or 0687 or 0701 or 0703 or 0707 or 0708 or 0714 or 0715 or 0719 or 0720 => true, | |
0722 or 0723 or 0724 or 0731 or 0732 or 0733 or 0738 or 0741 or 0742 or 0743 or 0764 or 0774 or 0789 or 0790 or 0791 or 0792 or 0800 => true, | |
0821 or 0822 or 0823 or 0841 or 0854 or 0855 or 0868 or 0873 or 0885 or 0886 or 0887 or 0890 or 0895 or 0905 => true, | |
0931 or 0938 or 0940 or 0941 or 0954 or 0955 or 0962 or 0965 or 0966 or 0969 or 0970 or 0973 or 0985 or 0987 or 0993 or 0994 or 1004 or 1005 or 1008 or 1012 or 1013 or 1016 or 1025 => true, | |
0026 when form is 1 => true, // Alolan Raichu | |
0145 when form is 0 => true, // Zapdos | |
0492 when form is 1 => true, // Sky Forme Shaymin | |
0646 when form is not 0 => true, // White Kyurem, Black Kyurem | |
0648 when form is 0 => true, // Aria Forme Meloetta | |
0898 when form is not 0 => true, // Ice Rider Calyrex, Shadow Rider Calyrex | |
_ => false, | |
}; | |
private static bool GetCanSwimOrBeAboveWater(ushort species, short form) => species switch | |
{ | |
0001 or 0002 or 0003 or 0004 or 0005 or 0006 or 0023 or 0024 or 0027 or 0028 or 0035 or 0036 or 0037 or 0038 or 0043 or 0044 or 0045 or 0048 or 0050 or 0051 or 0052 or 0053 or 0056 or 0057 or 0058 or 0059 or 0069 or 0075 or 0076 or 0084 or 0085 or 0088 or 0089 or 0096 or 0097 or 0100 or 0101 or 0102 or 0103 or 0106 or 0107 or 0111 or 0112 or 0113 or 0125 or 0126 or 0132 or 0143 => false, | |
0152 or 0153 or 0154 or 0155 or 0156 or 0157 or 0161 or 0162 or 0167 or 0168 or 0173 or 0179 or 0180 or 0181 or 0185 or 0190 or 0191 or 0192 or 0203 or 0204 or 0206 or 0209 or 0210 or 0212 or 0215 or 0216 or 0217 or 0218 or 0219 or 0220 or 0221 or 0228 or 0229 or 0231 or 0232 or 0234 or 0235 or 0236 or 0237 or 0239 or 0240 or 0242 or 0243 or 0244 or 0246 or 0247 or 0248 => false, | |
0252 or 0253 or 0254 or 0255 or 0256 or 0257 or 0261 or 0262 or 0273 or 0274 or 0275 or 0280 or 0281 or 0282 or 0285 or 0286 or 0287 or 0288 or 0289 or 0296 or 0297 or 0299 or 0302 or 0307 or 0308 or 0311 or 0312 or 0316 or 0317 or 0322 or 0323 or 0324 or 0325 or 0326 or 0328 or 0331 or 0332 or 0335 or 0336 or 0354 or 0356 or 0361 or 0371 or 0372 or 0377 or 0378 or 0379 or 0383 => false, | |
0387 or 0388 or 0389 or 0390 or 0391 or 0392 or 0401 or 0403 or 0404 or 0405 or 0408 or 0409 or 0410 or 0411 or 0417 or 0422 or 0423 or 0424 or 0434 or 0435 or 0438 or 0440 or 0442 or 0443 or 0444 or 0445 or 0446 or 0447 or 0448 or 0449 or 0450 or 0453 or 0454 or 0459 or 0460 or 0461 or 0464 or 0466 or 0467 or 0473 or 0475 or 0485 or 0486 => false, | |
0495 or 0496 or 0497 or 0498 or 0499 or 0500 or 0522 or 0523 or 0529 or 0530 or 0532 or 0533 or 0534 or 0540 or 0541 or 0542 or 0548 or 0549 or 0551 or 0552 or 0553 or 0559 or 0560 or 0570 or 0571 or 0572 or 0573 or 0574 or 0575 or 0576 or 0585 or 0586 or 0590 or 0591 or 0595 or 0596 or 0607 or 0610 or 0611 or 0612 or 0613 or 0614 or 0619 or 0620 or 0622 or 0623 or 0624 or 0625 or 0629 or 0633 or 0634 or 0636 or 0638 or 0639 or 0640 => false, | |
0650 or 0651 or 0652 or 0653 or 0654 or 0655 or 0664 or 0665 or 0667 or 0668 or 0672 or 0673 or 0677 or 0678 or 0702 or 0704 or 0705 or 0706 or 0709 or 0721 => false, | |
0725 or 0726 or 0727 or 0734 or 0735 or 0736 or 0737 or 0739 or 0740 or 0744 or 0745 or 0749 or 0750 or 0753 or 0754 or 0757 or 0758 or 0761 or 0762 or 0763 or 0765 or 0766 or 0769 or 0770 or 0775 or 0778 or 0782 or 0783 or 0784 or 0801 => false, | |
0810 or 0811 or 0812 or 0813 or 0814 or 0815 or 0819 or 0820 or 0837 or 0838 or 0839 or 0840 or 0842 or 0843 or 0844 or 0848 or 0849 or 0856 or 0857 or 0858 or 0859 or 0860 or 0861 or 0863 or 0869 or 0870 or 0871 or 0872 or 0874 or 0876 or 0877 or 0878 or 0879 or 0884 or 0888 or 0889 or 0891 or 0892 or 0893 or 0894 or 0896 or 0897 or 0899 or 0900 or 0901 or 0903 => false, | |
0906 or 0907 or 0908 or 0909 or 0910 or 0911 or 0915 or 0916 or 0917 or 0918 or 0919 or 0920 or 0924 or 0925 or 0926 or 0927 or 0928 or 0929 or 0930 or 0932 or 0933 or 0934 or 0935 or 0936 or 0937 or 0939 or 0942 or 0943 or 0944 or 0945 or 0946 or 0947 or 0948 or 0949 or 0950 or 0951 or 0952 or 0953 or 0956 or 0957 or 0958 or 0959 or 0960 or 0961 or 0967 or 0968 or 0971 or 0972 or 0974 or 0975 or 0979 or 0981 or 0982 or 0983 or 0984 or 0986 or 0988 or 0989 or 0990 or 0991 or 0992 or 0995 or 0996 or 0997 or 0998 or 0999 or 1001 or 1002 or 1003 or 1006 or 1010 or 1011 or 1014 or 1015 or 1017 or 1018 or 1019 or 1020 or 1021 or 1022 or 1023 or 1024 => false, | |
0128 when form is not 3 => false, // Aqua Breed Tauros | |
0145 when form is 1 => false, // Galarian Zapdos | |
0492 when form is 0 => false, // Land Forme Shaymin | |
0646 when form is 0 => false, // Kyurem | |
0713 when form is 0 => false, // Avalugg | |
0898 when form is 0 => false, // Calyrex | |
_ => true, | |
}; | |
private static bool GetIsThreeStageFamily(ushort species) => species switch | |
{ | |
0001 or 0004 or 0007 or 0043 or 0056 or 0060 or 0069 or 0074 or 0074 or 0081 or 0092 or 0111 or 0116 or 0137 or 0147 => true, | |
0152 or 0155 or 0158 or 0172 or 0173 or 0174 or 0179 or 0187 or 0216 or 0220 or 0239 or 0240 or 0246 => true, | |
0252 or 0255 or 0258 or 0270 or 0273 or 0280 or 0287 or 0298 or 0328 or 0355 or 0371 or 0374 => true, | |
0387 or 0390 or 0393 or 0396 or 0403 or 0440 or 0443 => true, | |
0495 or 0498 or 0501 or 0532 or 0540 or 0551 or 0574 or 0577 or 0602 or 0607 or 0610 or 0624 or 0633 => true, | |
0650 or 0653 or 0656 or 0661 or 0664 or 0669 or 0704 => true, | |
0722 or 0725 or 0728 or 0731 or 0736 or 0761 or 0782 or 0789 => true, | |
0810 or 0813 or 0816 or 0821 or 0837 or 0840 or 0856 or 0859 or 0885 => true, | |
0906 or 0909 or 0912 or 0921 or 0928 or 0932 or 0957 or 0996 => true, | |
_ => false, | |
}; | |
private static List<ushort> ValuableItems = | |
[ | |
0001, // Master Ball | |
0050, // Rare Candy | |
0051, // PP Up | |
0053, // PP Max | |
0089, // Big Pearl | |
0092, // Nugget | |
0231, // Lucky Egg | |
0492, // Fast Ball | |
0493, // Level Ball | |
0494, // Lure Ball | |
0495, // Heavy Ball | |
0496, // Love Ball | |
0497, // Friend Ball | |
0498, // Moon Ball | |
0499, // Sport Ball | |
0576, // Dream Ball | |
0580, // Balm Mushroom | |
0581, // Big Nugget | |
0582, // Pearl String | |
0583, // Comet Shard | |
0645, // Ability Capsule | |
0795, // Bottle Cap | |
0796, // Gold Bottle Cap | |
0851, // Beast Ball | |
1127, // Exp. Candy L | |
1128, // Exp. Candy XL | |
1606, // Ability Patch | |
1904, // Sweet Herba Mystica | |
1905, // Salty Herba Mystica | |
1906, // Sour Herba Mystica | |
1907, // Bitter Herba Mystica | |
1908, // Spicy Herba Mystica | |
2137, // Gimmighoul Coin | |
]; | |
private static int GetRandom(int max) => pkNX.Randomization.Util.Random.Next(max); | |
private static int GetRandom(int min, int max) => pkNX.Randomization.Util.Random.Next(min, max); | |
private static List<string> Languages = [ "English", "French", "German", "Italian", "JPN", "JPN_KANJI", "Korean", "Simp_Chinese", "Spanish", "Trad_Chinese" ]; | |
private static List<string> ForeignTreatDescriptions = | |
[ | |
/* ENG */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* FRE */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* GER */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* ITA */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* JPN */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* JPN_KANJI */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* KOR */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* CHS */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* SP-EU */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
/* CHT */ "A snack made from ingredients favored by\nPokémon who evolve differently in other regions.", | |
]; | |
private static List<string> ForeignTreatPlurals = | |
[ | |
/* ENG */ "Foreign Treats", | |
/* FRE */ "Foreign Treats", | |
/* GER */ "Foreign Treats", | |
/* ITA */ "Foreign Treats", | |
/* JPN */ "Foreign Treats", | |
/* JPN_KANJI */ "Foreign Treats", | |
/* KOR */ "Foreign Treats", | |
/* CHS */ "Foreign Treats", | |
/* SP-EU */ "Foreign Treats", | |
/* CHT */ "Foreign Treats", | |
]; | |
private static List<string> ForeignTreatSingles = | |
[ | |
/* ENG */ "Foreign Treat", | |
/* FRE */ "Foreign Treat", | |
/* GER */ "Foreign Treat", | |
/* ITA */ "Foreign Treat", | |
/* JPN */ "Foreign Treat", | |
/* JPN_KANJI */ "Foreign Treat", | |
/* KOR */ "Foreign Treat", | |
/* CHS */ "Foreign Treat", | |
/* SP-EU */ "Foreign Treat", | |
/* CHT */ "Foreign Treat", | |
]; | |
private static List<string> PlaceholderPokedexEntries = | |
[ | |
/* ENG */ "Ecology under research.", | |
/* FRE */ "Recherches sur cette espèce en cours.", | |
/* GER */ "Lebensweise wird untersucht.", | |
/* ITA */ "Le ricerche su questa specie non\nsono ancora terminate.", | |
/* JPN */ "せいたい ちょうさちゅう です。", | |
/* JPN_KANJI */ "生態調査中です。", | |
/* KOR */ "현재 생태 조사 중입니다.", | |
/* CHS */ "生态调查中。", | |
/* SP-EU */ "En proceso de investigación.", | |
/* CHT */ "生態調查中。", | |
]; | |
private static List<string> ImportantTrainers = | |
[ | |
"botan_01", | |
"botan_02", | |
"botan_02_01", | |
"botan_multi", | |
"botan_schoolwars", | |
"brother_01_01", | |
"brother_01_01_strong", | |
"brother_01_02", | |
"brother_01_02_strong", | |
"brother_01_03", | |
"brother_01_03_strong", | |
"brother_01_04", | |
"brother_01_04_strong", | |
"brother_01_05", | |
"brother_01_05_strong", | |
"brother_02_01", | |
"brother_kodaigame", | |
"camera_01_01", | |
"chairperson_01", | |
"chairperson_02", | |
"chairperson_03", | |
"clavel_01", | |
"clavel_01_hono", | |
"clavel_01_kusa", | |
"clavel_01_mizu", | |
"clavel_02_hono", | |
"clavel_02_kusa", | |
"clavel_02_mizu", | |
"dan_aku_boss_01", | |
"dan_aku_boss_02", | |
"dan_doku_boss_01", | |
"dan_doku_boss_02", | |
"dan_fairy_boss_01", | |
"dan_fairy_boss_02", | |
"dan_hono_boss_01", | |
"dan_hono_boss_02", | |
"dan_kakutou_boss_01", | |
"dan_kakutou_boss_02", | |
"dragon4_02_01", | |
"e4_dragon_01", | |
"e4_dragon_02", | |
"e4_hagane_01", | |
"e4_hikou_01", | |
"e4_jimen_01", | |
"fairy4_02_01", | |
"fairy4_02_02", | |
"gym_denki_leader_01", | |
"gym_denki_leader_02", | |
"gym_esper_leader_01", | |
"gym_esper_leader_02", | |
"gym_ghost_leader_01", | |
"gym_ghost_leader_02", | |
"gym_koori_leader_01", | |
"gym_koori_leader_02", | |
"gym_kusa_leader_01", | |
"gym_kusa_leader_02", | |
"gym_mizu_leader_01", | |
"gym_mizu_leader_02", | |
"gym_mushi_leader_01", | |
"gym_mushi_leader_02", | |
"gym_normal_leader_01", | |
"gym_normal_leader_02", | |
"hagane4_02_01", | |
"hono4_02_01", | |
"kihada_01", | |
"kihada_02", | |
"mimoza_01", | |
"pepper_01", | |
"pepper_02", | |
"pepper_02_01", | |
"pepper_03", | |
"pepper_multi", | |
"pepper_schoolwars", | |
"professor_A_01", | |
"professor_B_01", | |
"rehoru_01", | |
"rival_02", | |
"rival_02_01hono", | |
"rival_02_01kusa", | |
"rival_02_01mizu", | |
"rival_02_hono", | |
"rival_02_kusa", | |
"rival_02_mizu", | |
"rival_03", | |
"rival_03_hono", | |
"rival_03_kusa", | |
"rival_03_mizu", | |
"rival_04_hono", | |
"rival_04_kusa", | |
"rival_04_mizu", | |
"rival_05", | |
"rival_05_hono", | |
"rival_05_kusa", | |
"rival_05_mizu", | |
"rival_06", | |
"rival_06_hono", | |
"rival_06_kusa", | |
"rival_06_mizu", | |
"rival_07_hono", | |
"rival_07_kusa", | |
"rival_07_mizu", | |
"rival_08_hono", | |
"rival_08_kusa", | |
"rival_08_mizu", | |
"rival_multi_hono", | |
"rival_multi_kusa", | |
"rival_multi_mizu", | |
"rival_schoolwars_hono", | |
"rival_schoolwars_kusa", | |
"rival_schoolwars_mizu", | |
"s2_side_brother", | |
"sawaro_01", | |
"seizi_01", | |
"shiano", | |
"sister_01_01", | |
"sister_01_01_strong", | |
"sister_01_02", | |
"sister_01_02_strong", | |
"sister_01_03", | |
"sister_01_03_strong", | |
"sister_02_01", | |
"sister_02_02", | |
"sister_muruchi_01", | |
"sister_muruchi_01_strong", | |
"sister_onitaizi", | |
"sister_onitaizi_strong", | |
"su2_bukatu_Koori", | |
"su2_bukatu_akamatu", | |
"su2_bukatu_botan", | |
"su2_bukatu_claver_honoo", | |
"su2_bukatu_claver_kusa", | |
"su2_bukatu_claver_mizu", | |
"su2_bukatu_denki", | |
"su2_bukatu_doragon", | |
"su2_bukatu_esper", | |
"su2_bukatu_ghost", | |
"su2_bukatu_hagane", | |
"su2_bukatu_hikou", | |
"su2_bukatu_kakitubata", | |
"su2_bukatu_kihada", | |
"su2_bukatu_kusa", | |
"su2_bukatu_mimoza", | |
"su2_bukatu_mizu", | |
"su2_bukatu_mushi", | |
"su2_bukatu_nemo_honoo", | |
"su2_bukatu_nemo_kusa", | |
"su2_bukatu_nemo_mizu", | |
"su2_bukatu_nerine", | |
"su2_bukatu_omodaka", | |
"su2_bukatu_pepa", | |
"su2_bukatu_rehool", | |
"su2_bukatu_sawaro", | |
"su2_bukatu_seizi", | |
"su2_bukatu_suguri", | |
"su2_bukatu_taro", | |
"su2_bukatu_time", | |
"su2_bukatu_zeiyu", | |
"su2_bukatu_zimen", | |
"su2_bukatu_zinia", | |
"taimu_01", | |
"zinia_01", | |
"zinia_02", | |
]; | |
private static List<string> GenericOverworldPokemonScenes = | |
[ | |
"world/obj_template/parts/field_pokemon/fp_025_/fp_025_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_055_/fp_055_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_082_/fp_082_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_094_/fp_094_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_129_/fp_129_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_129_group_/fp_129_group_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_130_/fp_130_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_156_/fp_156_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_198_/fp_198_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_340_/fp_340_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_396_/fp_396_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_396_group_/fp_396_group_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_397_/fp_397_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_398_/fp_398_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_403_/fp_403_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_403_group_/fp_403_group_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_405_/fp_405_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_415_/fp_415_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_415_group_/fp_415_group_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_417_/fp_417_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_418_/fp_418_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_418_group_/fp_418_group_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_425_/fp_425_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_425_group_/fp_425_group_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_426_/fp_426_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_430_/fp_430_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_445_/fp_445_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_448_/fp_448_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_448_jw_/fp_448_jw_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_459_/fp_459_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_470_/fp_470_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_501_/fp_501_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_548_/fp_548_0.trsot", | |
"world/obj_template/parts/field_pokemon/fp_571_/fp_571_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_1/fp_396_group1_/fp_396_group1_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_1/fp_398_group1_/fp_398_group1_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_2/fp_396_group2_/fp_396_group2_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_2/fp_398_group2_/fp_398_group2_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_3/fp_396_group3_/fp_396_group3_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_3/fp_398_group3_/fp_398_group3_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_4/fp_396_group4_/fp_396_group4_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_4/fp_398_group4_/fp_398_group4_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_5/fp_396_group5_/fp_396_group5_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_5/fp_398_group5_/fp_398_group5_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_6/fp_396_group6_/fp_396_group6_0.trsot", | |
"world/obj_template/parts/field_pokemon/group_6/fp_398_group6_/fp_398_group6_0.trsot", | |
"world/obj_template/parts/gym/koori/gym_koori_course_pokemon_/gym_koori_course_pokemon_0.trsot", | |
"world/obj_template/parts/gym/mushi/event_npc_gym_mushi_040_pm708_/event_npc_gym_mushi_040_pm708_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_gym_denki_poke_/npc_gym_denki_poke_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_gym_ghost_poke_/npc_gym_ghost_poke_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_gym_kusa_poke_/npc_gym_kusa_poke_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_gym_mushi_poke_olive_/npc_gym_mushi_poke_olive_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_gym_mushi_poke_spider_/npc_gym_mushi_poke_spider_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_kuma_fieldwork_poke_/npc_kuma_fieldwork_poke_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_oyatsu_legend_poke_base_/npc_oyatsu_legend_poke_base_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_poke_base_/npc_poke_base_0.trsot", | |
"world/obj_template/parts/npc_pokemon/npc_poke_base_dynamic_exclusion_/npc_poke_base_dynamic_exclusion_0.trsot", | |
"world/obj_template/parts/nushi/dragon/nushi_dragon_fp_1056_015_/nushi_dragon_fp_1056_015_0.trsot", | |
"world/obj_template/parts/nushi/hiko/HikoNushiActor_/HikoNushiActor_0.trsot", | |
"world/obj_template/parts/nushi/jimen/JimenNushiActor_/JimenNushiActor_0.trsot", | |
"world/scene/parts/demo/ev/d010/d010_a_sch_office01_/d010_a_sch_office01_0.trscn", | |
"world/scene/parts/demo/ev/d010/d010_chara_c06_/d010_chara_c06_0.trscn", | |
"world/scene/parts/demo/ev/d010/d010_chara_c18_/d010_chara_c18_0.trscn", | |
"world/scene/parts/demo/ev/d010/d010_chara_c20_/d010_chara_c20_0.trscn", | |
"world/scene/parts/demo/ev/d060_/d060_0.trscn", | |
"world/scene/parts/demo/ev/d200_/d200_0.trscn", | |
"world/scene/parts/demo/ev/d210_/d210_0.trscn", | |
"world/scene/parts/demo/ev/d710_/d710_0.trscn", | |
"world/scene/parts/demo/ev/d720_/d720_0.trscn", | |
"world/scene/parts/demo/sd/sd8015_eat_bbfire_/sd8015_eat_bbfire_0.trscn", | |
"world/scene/parts/demo/sd/sd8022_clear_2_/sd8022_clear_2_0.trscn", | |
"world/scene/parts/event/event_scenario/main_scenario/ajito_honoo_010_/ajito_honoo_010_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/ajito_honoo_040_/ajito_honoo_040_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0025_/common_0025_always_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0110_/common_0110_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0110_/common_0110_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0135_/common_0135_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0140_legend_action_02_/common_0140_legend_action_02_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_0300_/common_0300_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_1180_/common_1180_always_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/common_1330_/common_1330_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_denki_020_/first_training_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_denki_020_/second_training_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_denki_020_/third_training_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_esper_040_/gym_esper_040_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_ghost_020_/npc_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_ghost_020_/step1_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_ghost_020_/step2_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_ghost_020_/step3_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_ghost_020_/step4_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_ghost_040_/gym_ghost_040_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_koori_040_/gym_koori_040_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_mushi_020_/gym_mushi_020_npc_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_mushi_040_/gym_mushi_040_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/gym_normal_020_/gym_normal_020_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/legend_0120_/legend_0120_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/nushi_dragon_015_/nushi_dragon_015_always_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc01_0040_/sdc01_0040_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_a_01_/sdc02_4kings_a_01_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_a_02_/sdc02_4kings_a_02_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_a_04_/sdc02_4kings_a_04_main_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_c_02_/question1_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_c_02_/question2_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_c_02_/question3_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_c_02_/question4_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_c_02_/question5_0.trsog", | |
"world/scene/parts/event/event_scenario/main_scenario/sdc02_4kings_c_04_/sdc02_4kings_c_04_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_d_05_/class_d_05_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_d_06_/class_d_06_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_d_07_/class_d_07_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_d_08_/class_d_08_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_e_01_/class_e_01_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_e_02_/class_e_02_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_e_03_/class_e_03_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_e_04_/class_e_04_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_e_05_/class_e_05_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_e_06_/class_e_06_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_e_07_/class_e_07_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_e_08_/class_e_08_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_f_02_/class_f_02_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/class_f_03_/class_f_03_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/kizuna_friend_d_/kizuna_friend_d_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/kizuna_language_b_/kizuna_language_b_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/kizuna_language_c_/kizuna_language_c_always_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/s1_side02_0030_/s1_side02_0030_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/s1_side02_0040_/targets_fieldwork_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/s1_sub_001_/s1_sub_001_main_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/sub_007_/sub_007_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/sub_008_/sub_008_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/sub_035_/sub_035_pre_start_0.trsog", | |
"world/scene/parts/event/event_scenario/sub_scenario/sub_046_/sub_046_pre_start_0.trsog", | |
"world/scene/parts/field/resident_event/resident_gym_koori_course/gym_koori_course_hard_/gym_koori_course_hard_0.trscn", | |
"world/scene/parts/field/resident_event/resident_gym_koori_course/gym_koori_course_very_hard_/gym_koori_course_very_hard_0.trscn", | |
"world/scene/parts/field/room/a_c01_hairsalon/a_c01_hairsalon_event/a_c01_hairsalon_npc_/a_c01_hairsalon_npc_0.trscn", | |
"world/scene/parts/field/room/a_c01_sandwich01/a_c01_sandwich01_event/a_c01_sandwich01_npc_/a_c01_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_c01_sandwich03/a_c01_sandwich03_event/a_c01_sandwich03_npc_/a_c01_sandwich03_npc_0.trscn", | |
"world/scene/parts/field/room/a_c01_sandwich04/a_c01_sandwich04_event/a_c01_sandwich04_npc_/a_c01_sandwich04_npc_0.trscn", | |
"world/scene/parts/field/room/a_c01_sandwich05/a_c01_sandwich05_event/a_c01_sandwich05_npc_/a_c01_sandwich05_npc_0.trscn", | |
"world/scene/parts/field/room/a_c01_sandwich06/a_c01_sandwich06_event/a_c01_sandwich06_npc_/a_c01_sandwich06_npc_0.trscn", | |
"world/scene/parts/field/room/a_c02_g01/a_c02_g01_event/a_c02_g01_npc_/a_c02_g01_npc_0.trscn", | |
"world/scene/parts/field/room/a_c02_hairsalon01/a_c02_hairsalon01_event/a_c02_hairsalon01_npc_/a_c02_hairsalon01_npc_0.trscn", | |
"world/scene/parts/field/room/a_c02_sandwich01/a_c02_sandwich01_event/a_c02_sandwich01_npc_/a_c02_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_c03_g01/a_c03_g01_event/a_c03_g01_npc_/a_c03_g01_npc_0.trscn", | |
"world/scene/parts/field/room/a_c03_sandwich01/a_c03_sandwich01_event/a_c03_sandwich01_npc_/a_c03_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_c03_sandwich02/a_c03_sandwich02_event/a_c03_sandwich02_npc_/a_c03_sandwich02_npc_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_cafe01/a_sch_2_cafe01_npc_daytime_/a_sch_2_cafe01_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_cafe01/a_sch_2_cafe01_npc_nighttime_/a_sch_2_cafe01_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_class1/a_sch_2_class1_npc_daytime_/a_sch_2_class1_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_class1/a_sch_2_class1_npc_nighttime_/a_sch_2_class1_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_class2/a_sch_2_class2_npc_daytime_/a_sch_2_class2_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_class2/a_sch_2_class2_npc_nighttime_/a_sch_2_class2_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_clubroom/a_sch_2_clubroom_mob_npc_/room01_npc_0.trsog", | |
"world/scene/parts/field/room/a_sch_2_clubroom/a_sch_2_clubroom_mob_npc_/room02_npc_0.trsog", | |
"world/scene/parts/field/room/a_sch_2_clubroom/a_sch_2_clubroom_mob_npc_/room03_npc_0.trsog", | |
"world/scene/parts/field/room/a_sch_2_clubroom/a_sch_2_clubroom_mob_npc_/room04_npc_0.trsog", | |
"world/scene/parts/field/room/a_sch_2_clubroom/a_sch_2_clubroom_mob_npc_/room05_npc_0.trsog", | |
"world/scene/parts/field/room/a_sch_2_clubroom/a_sch_2_clubroom_mob_npc_/room06_npc_0.trsog", | |
"world/scene/parts/field/room/a_sch_2_clubroom/a_sch_2_clubroom_mob_npc_/room07_npc_0.trsog", | |
"world/scene/parts/field/room/a_sch_2_clubroom/a_sch_2_clubroom_mob_npc_/room08_npc_0.trsog", | |
"world/scene/parts/field/room/a_sch_2_entrance01/a_sch_2_entrance01_npc_/a_sch_2_entrance01_npc_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_shop01/a_sch_2_shop01_npc_daytime_/a_sch_2_shop01_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_2_shop01/a_sch_2_shop01_npc_nighttime_/a_sch_2_shop01_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_cafe01/a_sch_cafe01_npc_daytime_/a_sch_cafe01_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_cafe01/a_sch_cafe01_npc_nighttime_/a_sch_cafe01_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_class1a/a_sch_class1a_npc_daytime_/a_sch_class1a_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_class1a/a_sch_class1a_npc_nighttime_/a_sch_class1a_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_class1d/a_sch_class1d_npc_daytime_/a_sch_class1d_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_class1d/a_sch_class1d_npc_nighttime_/a_sch_class1d_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_class2g/a_sch_class2g_npc_daytime_/a_sch_class2g_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_class2g/a_sch_class2g_npc_nighttime_/a_sch_class2g_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_dorm02/a_sch_dorm02_event_/a_sch_dorm02_event_0.trscn", | |
"world/scene/parts/field/room/a_sch_dorm03/a_sch_dorm03_event_/a_sch_dorm03_event_0.trscn", | |
"world/scene/parts/field/room/a_sch_dorm04/a_sch_dorm04_event_/a_sch_dorm04_event_0.trscn", | |
"world/scene/parts/field/room/a_sch_entrance01/a_sch_entrance01_npc_daytime_/a_sch_entrance01_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_entrance01/a_sch_entrance01_npc_nighttime_/a_sch_entrance01_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_ground01/a_sch_ground01_npc_daytime_/a_sch_ground01_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_ground01/a_sch_ground01_npc_nighttime_/a_sch_ground01_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_ground01/a_sch_ground01_npc_studytime_/a_sch_ground01_npc_studytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_office02/a_sch_office02_kizuna_language_teacher_/a_sch_office02_kizuna_language_teacher_0.trscn", | |
"world/scene/parts/field/room/a_sch_office02/a_sch_office02_npc_daytime_/a_sch_office02_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_office02/a_sch_office02_npc_nighttime_/a_sch_office02_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_office03/a_sch_office03_npc_daytime_/a_sch_office03_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_office03/a_sch_office03_npc_nighttime_/a_sch_office03_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_room01/a_sch_room01_npc_daytime_/a_sch_room01_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_room01/a_sch_room01_npc_nighttime_/a_sch_room01_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_room02/a_sch_room02_npc_daytime_/a_sch_room02_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_room02/a_sch_room02_npc_nighttime_/a_sch_room02_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_room03/a_sch_room03_npc_daytime_/a_sch_room03_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_room03/a_sch_room03_npc_nighttime_/a_sch_room03_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_sch_shop01/a_sch_shop01_npc_daytime_/a_sch_shop01_npc_daytime_0.trscn", | |
"world/scene/parts/field/room/a_sch_shop01/a_sch_shop01_npc_nighttime_/a_sch_shop01_npc_nighttime_0.trscn", | |
"world/scene/parts/field/room/a_t01_i01/a_t01_i01_event/a_t01_i01_npc_/a_t01_i01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t01_i02/a_t01_i02_event/a_t01_i02_npc_/a_t01_i02_npc_0.trscn", | |
"world/scene/parts/field/room/a_t03_g01/a_t03_g01_event/a_t03_g01_npc_/a_t03_g01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t03_sandwich01/a_t03_sandwich01_event/a_t03_sandwich01_npc_/a_t03_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t04_g01/a_t04_g01_event/a_t04_g01_npc_/a_t04_g01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t04_sandwich01/a_t04_sandwich01_event/a_t04_sandwich01_npc_/a_t04_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t06_g01/a_t06_g01_event/a_t06_g01_npc_/a_t06_g01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t06_g10/a_t06_g10_event_npc_floor_/a_t06_g10_event_npc_floor_0.trscn", | |
"world/scene/parts/field/room/a_t06_g10/a_t06_g10_event_npc_outside_/a_t06_g10_event_npc_outside_0.trscn", | |
"world/scene/parts/field/room/a_t06_sandwich01/a_t06_sandwich01_event/a_t06_sandwich01_npc_/a_t06_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t07_sandwich01/a_t07_sandwich01_event/a_t07_sandwich01_npc_/a_t07_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t08_g01/a_t08_g01_event/a_t08_g01_npc_/a_t08_g01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t08_sandwich01/a_t08_sandwich01_event/a_t08_sandwich01_npc_/a_t08_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t09_g01/a_t09_g01_event/a_t09_g01_npc_/a_t09_g01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t09_sandwich01/a_t09_sandwich01_event/a_t09_sandwich01_npc_/a_t09_sandwich01_npc_0.trscn", | |
"world/scene/parts/field/room/a_t10_i01/a_t10_i01_npc_00_/a_t10_i01_npc_00_0.trscn", | |
"world/scene/parts/field/room/a_w17_g01/a_w17_g01_event/a_w17_g01_npc_/a_w17_g01_npc_0.trscn", | |
"world/scene/parts/field/room/clubroom/a_sch_2_/a_sch_2_0.trscn", | |
"world/scene/parts/field/streaming_event/area01_encount_/area01_encount_0.trscn", | |
"world/scene/parts/field/streaming_event/c01_npc_/c01_npc_0.trscn", | |
"world/scene/parts/field/streaming_event/su1_world_npc_/su1_world_npc_0.trscn", | |
"world/scene/parts/field/streaming_event/su2_world_npc_/su2_world_npc_0.trscn", | |
"world/scene/parts/field/streaming_event/world_npc_/world_npc_0.trscn", | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@SHRetro Thanks, fixed.