Overview
PKHeX.Core supports Pokémon Showdown’s text format for importing and exporting Pokémon teams. This allows easy team sharing and integration with competitive battle simulators.
Showdown format is a human-readable text format used by Pokémon Showdown (a popular battle simulator). Here’s an example:
Garchomp @ Choice Scarf
Ability: Rough Skin
Level: 50
EVs: 252 Atk / 4 SpD / 252 Spe
Jolly Nature
- Earthquake
- Dragon Claw
- Stone Edge
- Outrage
Basic Import
Use the ShowdownSet class to parse Showdown text:
using PKHeX.Core;
string showdownText = @"
Pikachu @ Light Ball
Ability: Static
Level: 50
Shiny: Yes
EVs: 252 SpA / 4 SpD / 252 Spe
Timid Nature
IVs: 0 Atk
- Thunderbolt
- Grass Knot
- Hidden Power Ice
- Volt Switch
";
// Parse the Showdown format
var set = new ShowdownSet(showdownText);
// Check for parsing errors
if (set.InvalidLines.Count > 0)
{
foreach (var error in set.InvalidLines)
{
Console.WriteLine($"Error: {error.Type} - {error.Line}");
}
}
Converting ShowdownSet to PKM
After parsing, convert the ShowdownSet to a PKM entity:
using PKHeX.Core;
// Parse Showdown text
var set = new ShowdownSet(showdownText);
// Create a blank Pokémon for the target generation
var pk = EntityBlank.GetBlank(EntityContext.Gen9);
// Apply the ShowdownSet to the PKM
var sav = new SimpleTrainerInfo(EntityContext.Gen9)
{
OT = "Trainer",
TID16 = 12345,
SID16 = 54321,
Gender = 0,
Language = (int)LanguageID.English
};
pk.ApplySetDetails(set, sav);
pk.RefreshChecksum();
The SimpleTrainerInfo provides trainer details that will be applied to the created Pokémon.
Accessing ShowdownSet Properties
The ShowdownSet class exposes all parsed properties:
var set = new ShowdownSet(showdownText);
// Basic information
ushort species = set.Species;
string nickname = set.Nickname;
byte? gender = set.Gender; // 0=Male, 1=Female, null=Genderless
int heldItem = set.HeldItem;
// Stats
int ability = set.Ability;
byte level = set.Level;
Nature nature = set.Nature;
bool shiny = set.Shiny;
// IVs and EVs
int[] ivs = set.IVs; // [HP, ATK, DEF, SPE, SPA, SPD]
int[] evs = set.EVs; // [HP, ATK, DEF, SPE, SPA, SPD]
// Moves
ushort[] moves = set.Moves; // Up to 4 moves
// Generation-specific
MoveType teraType = set.TeraType; // Gen 9
byte dynamaxLevel = set.DynamaxLevel; // Gen 8
bool canGigantamax = set.CanGigantamax; // Gen 8
Basic Export
Convert any PKM to Showdown format:
using PKHeX.Core;
PKM pk = new PK9
{
Species = (ushort)Species.Charizard,
Ability = (int)Ability.Blaze,
Nature = Nature.Timid,
// ... other properties
};
// Get Showdown text
string showdownText = ShowdownParsing.GetShowdownText(pk);
Console.WriteLine(showdownText);
Custom Export Settings
Control what appears in the export:
using PKHeX.Core;
var settings = new BattleTemplateExportSettings
{
Moves = MoveDisplayStyle.Directional, // How moves are formatted
StatsIVs = StatDisplayConfig.Default,
StatsEVs = StatDisplayConfig.Default
};
var set = new ShowdownSet(pk);
string text = set.GetText(settings);
Advanced Showdown Features
Parsing Multiple Pokémon (Teams)
Import entire teams at once:
string teamText = @"
Pikachu @ Light Ball
Ability: Static
EVs: 252 SpA / 252 Spe / 4 HP
Timid Nature
- Thunderbolt
- Volt Switch
- Hidden Power Ice
- Grass Knot
Charizard @ Choice Specs
Ability: Solar Power
EVs: 252 SpA / 252 Spe / 4 HP
Timid Nature
- Fire Blast
- Air Slash
- Solar Beam
- Focus Blast
";
// Parse team
var team = ShowdownTeam.Parse(teamText);
foreach (var set in team)
{
Console.WriteLine($"Parsed: {set.Species}");
// Convert each set to PKM
var pk = EntityBlank.GetBlank(EntityContext.Gen9);
// Apply set details...
}
Showdown format supports alternate forms:
string alolaPersian = @"
Persian-Alola @ Life Orb
Ability: Fur Coat
EVs: 252 HP / 252 Def / 4 SpD
Bold Nature
- Foul Play
- Taunt
- Parting Shot
- Toxic
";
var set = new ShowdownSet(alolaPersian);
Console.WriteLine($"Form: {set.FormName}"); // "Alola"
Console.WriteLine($"Form Index: {set.Form}"); // 1
Tera Type Support (Gen 9)
string teraExample = @"
Dragonite @ Choice Band
Ability: Multiscale
Tera Type: Normal
EVs: 252 Atk / 4 SpD / 252 Spe
Adamant Nature
- Extreme Speed
- Outrage
- Earthquake
- Fire Punch
";
var set = new ShowdownSet(teraExample);
Console.WriteLine($"Tera Type: {set.TeraType}"); // Normal
Gigantamax Support (Gen 8)
string gmaxExample = @"
Charizard @ Life Orb
Ability: Solar Power
Gigantamax: Yes
EVs: 252 SpA / 4 SpD / 252 Spe
Timid Nature
- Flamethrower
- Air Slash
- Solar Beam
- Roost
";
var set = new ShowdownSet(gmaxExample);
Console.WriteLine($"Can Gigantamax: {set.CanGigantamax}");
Localization Support
Showdown format supports multiple languages:
using PKHeX.Core;
// Use Japanese localization
var jpStrings = GameInfo.GetStrings("ja");
var localization = new BattleTemplateLocalization(jpStrings);
string japaneseSet = @"
ピカチュウ @ でんきだま
性格: ひかえめ
特性: せいでんき
努力値: HP 4 / 特攻 252 / 素早 252
- 10まんボルト
- くさむすび
- ボルトチェンジ
- めざめるパワー氷
";
var set = new ShowdownSet(japaneseSet, localization);
Console.WriteLine($"Species: {set.Species}"); // Pikachu
Error Handling
Detecting Parse Errors
var set = new ShowdownSet(showdownText);
if (set.InvalidLines.Count > 0)
{
Console.WriteLine("Parsing errors detected:");
foreach (var error in set.InvalidLines)
{
switch (error.Type)
{
case BattleTemplateParseErrorType.InvalidSpecies:
Console.WriteLine($"Invalid species: {error.Line}");
break;
case BattleTemplateParseErrorType.InvalidMove:
Console.WriteLine($"Invalid move: {error.Line}");
break;
case BattleTemplateParseErrorType.InvalidAbility:
Console.WriteLine($"Invalid ability: {error.Line}");
break;
case BattleTemplateParseErrorType.LineLength:
Console.WriteLine($"Line too long/short: {error.Line}");
break;
default:
Console.WriteLine($"Unknown error: {error.Line}");
break;
}
}
}
Complete Import/Export Example
using PKHeX.Core;
public class ShowdownConverter
{
// Import from Showdown text
public static PKM ImportShowdown(string showdownText, EntityContext context)
{
// Parse the text
var set = new ShowdownSet(showdownText);
// Check for errors
if (set.InvalidLines.Count > 0)
{
throw new Exception($"Parse errors: {set.InvalidLines.Count}");
}
// Create blank Pokémon
var pk = EntityBlank.GetBlank(context);
// Create trainer info
var trainer = new SimpleTrainerInfo(context)
{
OT = "Trainer",
TID16 = 12345,
SID16 = 54321,
Gender = 0,
Language = (int)LanguageID.English
};
// Apply the set
pk.ApplySetDetails(set, trainer);
pk.RefreshChecksum();
return pk;
}
// Export to Showdown text
public static string ExportShowdown(PKM pk)
{
var set = new ShowdownSet(pk);
return set.Text;
}
// Import and export roundtrip
public static void TestRoundtrip(string originalText)
{
// Import
var pk = ImportShowdown(originalText, EntityContext.Gen9);
// Export
string exportedText = ExportShowdown(pk);
Console.WriteLine("Original:");
Console.WriteLine(originalText);
Console.WriteLine("\nExported:");
Console.WriteLine(exportedText);
}
}
Common Showdown Patterns
Standard Competitive Set
Landorus-Therian @ Assault Vest
Ability: Intimidate
Level: 50
EVs: 252 HP / 252 Atk / 4 SpD
Adamant Nature
- Earthquake
- Rock Slide
- U-turn
- Superpower
Special Attacker
Kyogre @ Choice Specs
Ability: Drizzle
Level: 50
Shiny: Yes
EVs: 252 HP / 252 SpA / 4 SpD
Modest Nature
IVs: 0 Atk
- Water Spout
- Origin Pulse
- Ice Beam
- Thunder
Mixed Attacker with Hidden Power
Heatran @ Leftovers
Ability: Flash Fire
EVs: 252 SpA / 4 SpD / 252 Spe
Timid Nature
IVs: 0 Atk / 30 Def
- Magma Storm
- Earth Power
- Hidden Power Ice
- Taunt
Tips and Best Practices
- Always check
InvalidLines after parsing to catch errors
- Use
RefreshChecksum() after applying a ShowdownSet
- Showdown format is case-insensitive for most fields
- The order of lines (except first line) doesn’t matter
Some Pokémon properties cannot be represented in Showdown format (ribbons, memories, etc.). These will be lost during export/import.
Next Steps