PKHeX/PKHeX.Core/Saves/Substructures/Records.cs
Kurt 88830e0d00
Update from .NET Framework 4.6 to .NET 7 (#3729)
Updates from net46->net7, dropping support for mono in favor of using the latest runtime (along with the performance/API improvements). Releases will be posted as 64bit only for now.

Refactors a good amount of internal API methods to be more performant and more customizable for future updates & fixes.

Adds functionality for Batch Editor commands to `>`, `<` and <=/>=

TID/SID properties renamed to TID16/SID16 for clarity; other properties exposed for Gen7 / display variants.

Main window has a new layout to account for DPI scaling (8 point grid)

Fixed: Tatsugiri and Paldean Tauros now output Showdown form names as Showdown expects
Changed: Gen9 species now interact based on the confirmed National Dex IDs (closes #3724)
Fixed: Pokedex set all no longer clears species with unavailable non-base forms (closes #3720)
Changed: Hyper Training suggestions now apply for level 50 in SV. (closes #3714)
Fixed: B2/W2 hatched egg met locations exclusive to specific versions are now explicitly checked (closes #3691)
Added: Properties for ribbon/mark count (closes #3659)
Fixed: Traded SV eggs are now checked correctly (closes #3692)
2023-01-21 20:02:33 -08:00

489 lines
18 KiB
C#

using System;
using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// <see cref="SaveFile"/> lifetime stat tracking
/// </summary>
public static class Records
{
private const byte LargeRecordCount = 100; // int32
private const byte SmallRecordCount = 100; // int16
private const byte Count = LargeRecordCount + SmallRecordCount;
/// <summary>
/// Gets the maximum value for the specified record using the provided maximum list.
/// </summary>
/// <param name="recordID">Record ID to retrieve the maximum for</param>
/// <param name="maxes">Maximum enum values for each record</param>
/// <returns>Maximum the record can be</returns>
public static int GetMax(int recordID, ReadOnlySpan<byte> maxes)
{
if ((byte)recordID >= Count)
return 0;
return MaxByType[maxes[recordID]];
}
public static int GetOffset(int baseOfs, int recordID) => recordID switch
{
< LargeRecordCount => baseOfs + (recordID * sizeof(int)),
< Count => baseOfs + (LargeRecordCount * sizeof(int)) + ((recordID - LargeRecordCount) * sizeof(ushort)),
_ => -1,
};
private static readonly int[] MaxByType = {999_999_999, 9_999_999, 999_999, 99_999, 65535, 9_999, 999, 7};
public static ReadOnlySpan<byte> DailyPairs_6 => new byte[] {29, 30, 110, 111, 112, 113, 114, 115, 116, 117};
public static ReadOnlySpan<byte> DailyPairs_7 => new byte[] {22, 23, 110, 111, 112, 113, 114, 115, 116, 117};
/// <summary>
/// Festa pairs; if updating the lower index record, update the Festa Mission record if currently active?
/// </summary>
public static ReadOnlySpan<byte> FestaPairs_7 => new byte[]
{
175, 6,
176, 33,
177, 8,
179, 38,
181, 74,
182, 73,
183, 7,
184, 159,
185, 9,
};
}
public static class RecordLists
{
public static readonly Dictionary<int, string> RecordList_6 = new()
{
{000, "Steps Taken"},
{001, "Times Saved"},
{002, "Storyline Completed Time"},
{003, "Times Bicycled"},
{004, "Total Battles"},
{005, "Wild Pokémon Battles"},
{006, "Trainer Battles"},
{007, "Pokemon Caught"},
{008, "Pokemon Caught Fishing"},
{009, "Eggs Hatched"},
{010, "Pokémon Evolved"},
{011, "Pokémon Healed at Pokémon Centers"},
{012, "Link Trades"},
{013, "Link Battles"},
{014, "Link Battle Wins"},
{015, "Link Battle Losses"},
{016, "WiFi Trades"},
{017, "WiFi Battles"},
{018, "WiFi Battle Wins"},
{019, "WiFi Battle Losses"},
{020, "IR Trades"},
{021, "IR Battles"},
{022, "IR Battle Wins"},
{023, "IR Battle Losses"},
{024, "Mart Stack Purchases"},
{025, "Money Spent"},
{026, "Times watched TV"},
{027, "Pokémon deposited at Nursery"},
{028, "Pokémon Defeated"},
{029, "Exp. Points Collected (Highest)"},
{030, "Exp. Points Collected (Today)"},
{031, "Deposited in the GTS"},
{032, "Nicknames Given"},
{033, "Bonus Premier Balls Received"},
{034, "Battle Points Earned"},
{035, "Battle Points Spent"},
{037, "Tips at Restaurant: ★☆☆"},
{038, "Tips at Restaurant: ★★☆"},
{039, "Tips at Restaurant: ★★★"},
{040, "Tips at Restaurant: Sushi High Roller"},
{041, "Tips at Café 1"},
{042, "Tips at Café 2"},
{043, "Tips at Café 3"},
{044, "Tips at Cameraman"},
{045, "Tips at Drink Vendors"},
{046, "Tips at Poet"},
{047, "Tips at Furfrou Trimmer"},
{048, "Tips at Battle Maison 1"},
{049, "Tips at Battle Maison 2"},
{050, "Tips at Battle Maison 3"},
{051, "Tips at Battle Maison 4"},
{052, "Tips at Maid"},
{053, "Tips at Butler"},
{054, "Tips at Scary House"},
{055, "Tips at Traveling Minstrel"},
{056, "Tips at Special BGM 1"},
{057, "Tips at Special BGM 2"},
{058, "Tips at Frieser Furfrou"},
{059, "Nice! Received"},
{060, "Birthday Wishes"},
{061, "Total People Met Online"},
{062, "Total People Passed By"},
{063, "Current Pokemiles"},
{064, "Total Pokemiles Received"},
{065, "Total Pokemiles sent to PGL"},
{066, "Total Super Training Attempts"},
{067, "Total Super Training Cleared"},
{068, "IV Judge Evaluations"},
{069, "Trash Cans inspected"},
{070, "Inverse Battles"},
{071, "Maison Battles"},
{072, "Times changed character clothing"},
{073, "Times changed character hairstyle"},
{074, "Berries harvested"},
{075, "Berry Field mutations"},
{076, "PR Videos"},
{077, "Friend Safari Encounters"},
{078, "O-Powers Used"},
{079, "Secret Base Updates"},
{080, "Secret Base Flags Captured"},
{081, "Contests Participated Count"},
{082, "GTS Trades"},
{083, "Wonder Trades"},
{084, "Steps Sneaked"},
{085, "Multiplayer Contests"},
{086, "Pokeblocks used"},
{087, "Times AreaNav Used"},
{088, "Times DexNav Used"},
{089, "Times BuzzNav Used"},
{090, "Times PlayNav Used"},
{100, "Champion Title Defense"},
{101, "Times rested at home"},
{102, "Times Splash used"},
{103, "Times Struggle used"},
{104, "Moves used with No Effect"},
{105, "Own Fainted Pokémon"},
{106, "Times attacked ally in battle"},
{107, "Failed Run Attempts"},
{108, "Wild encounters that fled"},
{109, "Failed Fishing Attempts"},
{110, "Pokemon Defeated (Highest)"},
{111, "Pokemon Defeated (Today)"},
{112, "Pokemon Caught (Highest)"},
{113, "Pokemon Caught (Today)"},
{114, "Trainers Battled (Highest)"},
{115, "Trainers Battled (Today)"},
{116, "Pokemon Evolved (Highest)"},
{117, "Pokemon Evolved (Today)"},
{118, "Fossils Restored"},
{119, "Sweet Scent Encounters"},
{120, "Battle Institute Tests"},
{121, "Battle Institute Rank"},
{122, "Battle Institute Score"},
{123, "Last Tip at Restaurant: ★☆☆"},
{124, "Last Tip at Restaurant: ★★☆"},
{125, "Last Tip at Restaurant: ★★★"},
{126, "Last Tip at Restaurant: Sushi High Roller"},
{127, "Last Tip at Café 1"},
{128, "Last Tip at Café 2"},
{129, "Last Tip at Café 3"},
{130, "Last Tip at Cameraman"},
{131, "Last Tip at Drink Vendors"},
{132, "Last Tip at Poet"},
{133, "Last Tip at Furfrou Trimmer"},
{134, "Last Tip at Battle Maison 1"},
{135, "Last Tip at Battle Maison 2"},
{136, "Last Tip at Battle Maison 3"},
{137, "Last Tip at Battle Maison 4"},
{138, "Last Tip at Maid"},
{139, "Last Tip at Butler"},
{140, "Last Tip at Scary House"},
{141, "Last Tip at Traveling Minstrel"},
{142, "Last Tip at Special BGM 1"},
{143, "Last Tip at Special BGM 2"},
{144, "Last Tip at Frieser Furfrou"},
{145, "Photos Taken"},
{146, "Sky Wild Battles (?)"},
{147, "Battle Maison Streak: Singles"},
{148, "Battle Maison Streak: Doubles"},
{149, "Battle Maison Streak: Triples"},
{150, "Battle Maison Streak: Rotation"},
{151, "Battle Maison Streak: Multi"},
{152, "Loto-ID Wins"},
{153, "PP Ups used"},
{154, "PSS Passerby Count (Today)"},
{155, "Amie Used"},
{156, "Roller Skate Count: Spin Left"},
{157, "Roller Skate Count: Spin Right"},
{158, "Roller Skate Count: Running Start"},
{159, "Roller Skate Count: Parallel Swizzle"},
{160, "Roller Skate Count: Drift-and-dash"},
{161, "Roller Skate Count: 360 right"},
{162, "Roller Skate Count: 360 left"},
{163, "Roller Skate Count: Flips"},
{164, "Roller Skate Count: Grind"},
{165, "Roller Skate Count: Combos"},
{166, "Fishing Chains"},
{167, "Secret Base Battles in your base"},
{168, "Secret Base Battles in another base"},
{169, "Contest Spectacular Photos taken"},
{170, "Times used Fly"},
{171, "Times used Soaring in the Sky"},
{172, "Times used Dive"},
{173, "Times used Sky Holes"},
{174, "Times healed by Mom"},
{175, "Times used Escape Rope"},
{176, "Times used Dowsing Machine"},
{177, "Trainer's Eye Rematches"},
{178, "FUREAI Interest ???"}, // similar to USUM idb
{179, "Shiny Pokemon Encountered"},
{180, "Trick House Clears"},
{181, "Eon Ticket 1 (SpotPass)"},
{182, "Eon Ticket 2 (Mystery Gift)"},
};
public static readonly Dictionary<int, string> RecordList_7 = new()
{
{000, "Steps Taken"},
{001, "Times Saved"},
{002, "Storyline Completed Time"},
{003, "Total Battles"},
{004, "Wild Pokémon Battles"},
{005, "Trainer Battles"},
{006, "Pokemon Caught"},
{007, "Pokemon Caught Fishing"},
{008, "Eggs Hatched"},
{009, "Pokémon Evolved"},
{010, "Pokémon Healed at Pokémon Centers"},
{011, "Link Trades"},
{012, "Link Battles"},
{013, "Link Battle Wins"},
{014, "Link Battle Losses"},
{015, "Battle Spot Battles"},
{016, "Battle Spot Wins"},
{017, "Battle Spot Losses"},
{018, "Mart Stack Purchases"},
{019, "Money Spent"},
{020, "Pokémon deposited at Nursery"},
{021, "Pokémon Defeated"},
{022, "Exp. Points Collected (Highest)"},
{023, "Exp. Points Collected (Today)"},
{024, "Deposited in the GTS"},
{025, "Nicknames Given"},
{026, "Bonus Premier Balls Received"},
{027, "Battle Points Earned"},
{028, "Battle Points Spent"},
{029, "Super Effective Moves Used"},
{030, "Clothing Count"},
{031, "Salon Uses"},
{032, "Berry Harvests"},
{033, "Trades at the GTS"},
{034, "Wonder Trades"},
{035, "Quick Links"},
{036, "Pokemon Rides"},
{037, "Beans Given"},
{038, "Festival Coins Spent"},
{039, "Poke Beans Collected"},
{040, "Battle Tree Challenges"},
{041, "Z-Moves Used"},
{042, "Balls Used"},
{043, "Items Thieved"},
{044, "Moves Used"},
{045, "Levels Raised"},
{046, "Ran From Battles"},
{047, "Rock Smash Items"},
{048, "Medicine Used"},
{049, "Pay Day Money Received"},
{050, "Total Thumbs-Ups"},
{051, "Times Twirled (Pirouette)"},
{052, "Record Thumbs-ups"},
{053, "Pokemon Petted"},
{054, "Poké Pelago Visits"},
{055, "Poké Pelago Bean Trades"},
{056, "Poké Pelago Tapped Pokémon"},
{057, "Poké Pelago Bean Stacks put in Crate"},
{058, "Poké Pelago Levels Gained"},
{059, "Poké Pelago Friendship Increased"},
{060, "Poké Pelago Eggs Hatched"},
{061, "Poké Pelago ???"},
{062, "Battle Video QR Teams Scanned"},
{063, "Battle Videos Watched"},
{064, "Battle Videos Rebattled"},
{065, "RotomDex Interactions"},
{066, "Guests Interacted With"},
{067, "Berry Piles (not full) Collected"},
{068, "Berry Piles (full) Collected"},
{069, "Items Reeled In"},
// USUM
{070, "Roto Lotos"},
{072, "Stickers Collected"},
{073, "Mantine Surf BP Earned"},
{074, "Battle Agency Wins"},
{100, "Champion Title Defense"},
{101, "Times rested at home"},
{102, "Times Splash used"},
{103, "Times Struggle used"},
{104, "Moves used with No Effect"},
{105, "Own Fainted Pokémon"},
{106, "Times attacked ally in battle"},
{107, "Failed Run Attempts"},
{108, "Wild encounters that fled"},
{109, "Failed Fishing Attempts"},
{110, "Pokemon Defeated (Highest)"},
{111, "Pokemon Defeated (Today)"},
{112, "Pokemon Caught (Highest)"},
{113, "Pokemon Caught (Today)"},
{114, "Trainers Battled (Highest)"},
{115, "Trainers Battled (Today)"},
{116, "Pokemon Evolved (Highest)"},
{117, "Pokemon Evolved (Today)"},
{118, "Fossils Restored"},
{119, "Photos Rated"},
{120, "Best (Super) Singles Streak"},
{121, "Best (Super) Doubles Streak"},
{122, "Best (Super) Multi Streak"},
{123, "Loto-ID Wins"},
{124, "PP Raised"},
{125, "Amie Used"},
{126, "Fishing Chains"},
{127, "Shiny Pokemon Encountered"},
{128, "Missions Participated In"},
{129, "Facilities Hosted"},
{130, "QR Code Scans"},
{131, "Moves learned with TMs"},
{132, "Café Drinks Bought"},
{133, "Trainer Card Photos Taken"},
{134, "Evolutions Cancelled"},
{135, "SOS Battle Allies Called"},
{136, "Friendship Raised"},
{137, "Battle Royal Dome Battles"},
{138, "Items Picked Up after Battle"},
{139, "Ate in Malasadas Shop"},
{140, "Hyper Trainings Received"},
{141, "Dishes eaten in Battle Buffet"},
{142, "Pokémon Refresh Accessed"},
{143, "Pokémon Storage System Log-outs"},
{144, "Lomi Lomi Massages"},
{145, "Times laid down in Ilima's Bed"},
{146, "Times laid down in Guzma's Bed"},
{147, "Times laid down in Kiawe's Bed"},
{148, "Times laid down in Lana's Bed"},
{149, "Times laid down in Mallow's Bed"},
{150, "Times laid down in Olivia's Bed"},
{151, "Times laid down in Hapu's Bed"},
{152, "Times laid down in Lusamine's Bed"},
{153, "Ambush/Smash post-battle items received"},
{154, "Rustling Tree Encounters"},
{155, "Ledges Jumped Down"},
{156, "Water Splash Encounters"},
{157, "Sand Cloud Encounters"},
{158, "Outfit Changes"},
{159, "Battle Royal Dome Wins"},
{160, "Pelago Treasure Hunts"},
{161, "Pelago Training Sessions"},
{162, "Pelago Hot Spring Sessions"},
{163, "Special QR 1"},
{164, "Special QR 2"},
{165, "Special QR Code Scans"},
{166, "Island Scans"},
{167, "Rustling Bush Encounters"},
{168, "Fly Shadow Encounters"},
{169, "Rustling Grass Encounters"},
{170, "Dirt Cloud Encounters"},
{171, "Wimpod Chases"},
{172, "Berry Tree Battles won"},
{173, "Bubbling Spot Encounters/Items"},
{174, "Times laid down in Own Bed"},
// global missions
{175, "Catch a lot of Pokémon!"},
{176, "Trade Pokémon at the GTS!"},
{177, "Hatch a lot of Eggs!"},
{178, "Harvest Poké Beans!"},
{179, "Get high scores with your Poké Finder!"},
{180, "Find Pokémon using Island Scan!"},
{181, "Catch Crabrawler!"},
{182, "Defend your Champion title!"},
{183, "Fish Pokémon at rare spots!"},
{184, "Battle Royal!"},
{185, "Try your luck!"},
{186, "Get BP at the Battle Tree!"},
{187, "Catch a lot of Pokémon!"},
// USUM
{188, "Ultra Wormhole Travels"},
{189, "Mantine Surf Plays"},
{190, "Photo Club Photos saved"},
{191, "Battle Agency Battles"},
// 192-194 unknown
{195, "Photo Club Sticker usage"},
{196, "Photo Club Photo Shoots"},
{197, "Highest Wormhole Travel Distance"},
{198, "Highest Mantine Surf BP Earned"},
};
public static readonly Dictionary<int, string> RecordList_8 = new()
{
{00, "egg_hatching"},
{01, "capture_wild"},
{02, "capture_symbol"},
{03, "capture_raid"},
{04, "capture_camp"},
{05, "capture_fishing"},
{06, "total_capture"},
{07, "dress_up"},
{08, "training"},
{09, "personal_change"},
{10, "rotomu_circuit"},
{11, "npc_trade"},
{12, "pretty"},
{13, "chain_encount"},
{14, "hall_of_fame"},
{15, "fossil_restore"},
{16, "wild_pokemon_encount"},
{17, "trade"},
{18, "magical_trade"},
{19, "one_day_captured"},
{20, "one_day_evolution"},
{21, "total_walk"},
{22, "total_watt"},
{23, "total_all_battle"},
{24, "campin"},
{25, "battle_point"},
{26, "win_battle_point"},
{27, "license_trade"},
{28, "use_skill_record"},
{29, "use_exp_ball"},
{30, "use_personal_change_item"},
{31, "clothes"},
{32, "evolution"},
{33, "net_battle"},
{34, "cooking"},
{35, "poke_job_return"},
{36, "get_rare_item"},
{37, "whistle"},
{38, "bike_dash"},
{39, "tree_shake"},
{40, "tree_nut"},
{41, "battle_lose"},
{42, "recipe"},
{43, "raid_battle"},
{44, "total_money"},
{45, "create_license_card"},
{46, "change_hair"},
/* 47 */ {G8BattleTowerSingleWin, "battle_tower_single_win"},
/* 48 */ {G8BattleTowerDoubleWin, "battle_tower_double_win"},
{49, "now_money"},
// The Records Block only stores 50 entries.
// Record IDs for future expansion content is instead handled separately.
// DLC
{50, "cormorant_robo"}, // saved in B9C0ECFC
{51, "battle_rom_mark"}, // saved in BB1DE8EF
};
public const int G8BattleTowerSingleWin = 47;
public const int G8BattleTowerDoubleWin = 48;
}